Metadata-Version: 2.4
Name: import-completer
Version: 0.10
Summary: A Python language server focused on intelligent import completions
Project-URL: Homepage, https://github.com/maniek2332/py-import-completer
Project-URL: Repository, https://github.com/maniek2332/py-import-completer
Project-URL: Issues, https://github.com/maniek2332/py-import-completer/issues
License-File: LICENSE
Requires-Python: >=3.13
Requires-Dist: aiosqlite==0.21.0
Requires-Dist: click>=8.0.0
Requires-Dist: loguru==0.7.3
Requires-Dist: pygls==2.0.0
Requires-Dist: tree-sitter-python==0.23.6
Requires-Dist: tree-sitter==0.24.0
Description-Content-Type: text/markdown

# py-import-completer

> A Python language server focused on one thing: intelligent import completions

[![Python Version](https://img.shields.io/badge/python-3.13%2B-blue)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![PyPI](https://img.shields.io/badge/PyPI-import--completer-blue)](https://pypi.org/project/import-completer/)

![Image](https://github.com/user-attachments/assets/4bd3ecf6-d9d2-415e-a7d7-c883fec36bd7)

## The Problem

If you've worked with Python in editors with some popular language servers (like Jedi, or Python-LSP), you've probably noticed something frustrating: **import completions are often incomplete or missing entirely**.

You're writing code and type `AsyncItera` expecting your editor to suggest `AsyncIterator` and auto-import it from `collections.abc`. Instead... nothing. You have to manually look up where the symbol is defined, type the import statement yourself, and then use it. This breaks your flow and slows you down.

Popular Python language servers excel at many things: type checking, go-to-definition, hover documentation, but they often fall short on import completions. This is where `py-import-completer` comes in.

## The Solution

`py-import-completer` is a lightweight Python language server that does **one thing and does it well**: providing import completions. When you start typing a symbol name, it:

1. Searches all available Python modules (stdlib, site-packages, your project)
2. Finds matching symbols (functions, classes, variables)
3. Suggests them as completions
4. **Automatically inserts the import statement** at the top of your file when you accept the completion

### How It Works

**Note**: This is a work in progress and some features are not complete yet or not working with full capacity.
CHeck out the [roadmap](#roadmap) for more details.

During startup, `py-import-completer` checks out your `python`'s `sys.path` to determine all the locations
of of third-party and project modules. Next, it uses `tree-sitter` to parse all Python files in your project
and extract all symbols (functions, classes, variables, etc.). Finally, it stores the extracted symbols in a
SQLite database for fast lookups. On any workspace files changes, it re-scans the affected files and rebuilds
the database (partially).

Server provides completions over standard LSP notifications, so it should work with any LSP client. Accepting
a completion will automatically insert the import statement in the imports section of the file
(always in new line, so you can use import organizer/code formatter to fix it later).

**Note**: This server provides **only** import completions. You should use it alongside other language server(s)
to get other features like type checking, go-to-definition, hover documentation, etc.

## Installation

### Prerequisites

- Python 3.13 or higher
- An LSP client (Neovim, VS Code, Helix, Emacs, etc.)

### Install from PyPI

The easiest way to install is via pip:

```bash
pip install import-completer
```

After installation, the `py-import-completer-server` command will be available.

### Install from Source

For development or the latest changes:

```bash
git clone https://github.com/maniek2332/py-import-completer
cd py-import-completer

# Using uv (recommended)
uv sync

# Or using pip
pip install -e .
```

## Editor Setup

### Neovim

Choose the setup method based on your Neovim version:

#### Neovim 0.11+ (Recommended)

The modern approach using `vim.lsp.config()` and `vim.lsp.enable()`:

```lua
-- In your init.lua or LSP config file
vim.lsp.config('import_completer', {
  cmd = {'py-import-completer-server'},
  filetypes = {'python'},
  root_markers = {'pyproject.toml', 'setup.py', '.git'},
})

-- Enable the server (auto-activates in Python buffers)
vim.lsp.enable('import_completer')
```

This approach automatically activates the server in Python files when it detects a project root. No autocmd needed!

#### Neovim 0.8-0.10 (Legacy)

For older Neovim versions, use `vim.lsp.start()`:

```lua
-- In your LSP setup file (e.g., ~/.config/nvim/lua/lsp.lua)
vim.api.nvim_create_autocmd('FileType', {
  pattern = 'python',
  callback = function(args)
    vim.lsp.start({
      name = 'import_completer',
      cmd = {'py-import-completer-server'},
      root_dir = vim.fs.dirname(
        vim.fs.find({'setup.py', 'pyproject.toml'}, { upward = true })[1]
      ),
    })
  end,
})
```

**Note**: On Neovim 0.10+ you can simplify the `root_dir` line to
`root_dir = vim.fs.root(args.buf, {'setup.py', 'pyproject.toml'})`. The
`vim.fs.root()` helper was only introduced in 0.10, so the `vim.fs.find()`
form above is used here for compatibility back to 0.8.

### Other Editors

`py-import-completer` implements the standard Language Server Protocol, so it should work with any LSP client. Here are some examples:

#### Helix

*Not tested (AI generated)*

Add to your `languages.toml`:

```toml
[language-server.py-import-completer]
command = "py-import-completer-server"

[[language]]
name = "python"
language-servers = ["py-import-completer", "pylsp"]  # Can combine with other servers
```

#### Emacs

*Not tested (AI generated)*

**Important**: `eglot` runs only **one** language server per buffer. Adding
`py-import-completer-server` to `eglot-server-programs` would *replace* your
main Python server (pyright/pylsp), leaving you with import completions only —
the opposite of what you want. To run it *alongside* another server, use an LSP
multiplexer such as [rassumfrassum](https://github.com/joaotavora/rassumfrassum)
(`rass`, by eglot's maintainer) or [lspx](https://github.com/thefrontend/lspx),
configured to fan out to both `py-import-completer-server` and your main Python
server, and point `eglot` at the multiplexer.

If you do want to use it as the sole server in a buffer, the entry covers both
the classic and tree-sitter Python modes:

```elisp
(add-to-list 'eglot-server-programs
             '((python-mode python-ts-mode) . ("py-import-completer-server")))
```

With `lsp-mode`, register it as an **add-on** client (`:add-on? t`), which is
`lsp-mode`'s mechanism for running a supplementary server alongside your main
Python client.

#### Sublime Text

*Not tested (AI generated)*

Using the [LSP package](https://packagecontrol.io/packages/LSP), open
**Preferences → Package Settings → LSP → Server Configurations** (or run
`Preferences: LSP Server Configurations` from the command palette) and add the
server, keyed directly by its name:

```json
{
  "py-import-completer": {
    "enabled": true,
    "command": ["py-import-completer-server"],
    "selector": "source.python"
  }
}
```

#### VS Code

*Not tested (AI generated)*

VS Code needs a client extension to register a language server, but you don't
have to write one. You have two options:

1. **No coding** — install a generic LSP client extension such as
   [Generic LSP Client](https://marketplace.visualstudio.com/items?itemName=zsol.vscode-glspc)
   (`zsol.vscode-glspc`) and set `glspc.server.command` to
   `py-import-completer-server` and `glspc.server.languageId` to `python`.
2. **Distributable integration** — build a thin extension wrapper (see the
   [VS Code Language Server Extension Guide](https://code.visualstudio.com/api/language-extensions/language-server-extension-guide)).

The server command is `py-import-completer-server` and it communicates via stdio.

## Configuration

Currently, `py-import-completer` automatically discovers Python modules from:
- **Standard library**: All stdlib modules
- **Site-packages**: Installed third-party packages
- **Current project**: Modules in your workspace

`py-import-completer` doesn't have to be installed in your current virtualenv. It will automatically checkout `sys.path`
of the `python` binary you're using (the first in your `$PATH`).

### CLI options

The `py-import-completer-server` command accepts the following options (each also available as an environment
variable):

| Option | Env var | Default | Description |
| ------ | ------ | ------- | ----------- |
| `--database-path` | `PY_IMPORT_COMPLETER_DATABASE_PATH` | `.py_import_completer.db` | Path to the SQLite database used for persistent symbol caching. Use `:memory:` for an in-memory database (no persistence). |
| `--modules-scoring-mapping` | `PY_IMPORT_COMPLETER_MODULES_SCORING_MAPPING` | *(none)* | Comma-separated `module_name:score` pairs, e.g. `requests:10,numpy:5`. Scores rank modules in completion results. |
| `--preview-completion` | *n/a* | *n/a* | Preview mode: perform a single completion lookup for the given text, print results, and exit without starting the server. |
| `--version` | *n/a* | *n/a* | Print the package version and exit. |

There is no configuration *file* support yet — configuration is via CLI flags and environment variables only.

## Known Limitations

`py-import-completer` is under active development. Current limitations include:

- **Top-level symbols only**: Nested classes and functions inside other symbols are not extracted
- **No dotted completions**: Only completes bare identifiers (e.g., `AsyncIterator`), not dotted names (e.g., `os.path`)
- **Single imports**: Each completion generates one import; no automatic import grouping
- **No relative imports**: Only handles absolute imports (`from module import symbol`)
- **No configuration file**: Configuration is via CLI flags and environment variables only (see [Configuration](#configuration))

These are all on the roadmap!

## Roadmap

Planned features and improvements:

- [x] Support for external stub (`*.pyi`) modules (*`.pyi` files are currently supported if they are the part of the package*)
- [x] Use persistent SQLite database instead of in-memory, so symbols from third-party modules persist across sessions
- [x] Better inserting of import statements (~~it inserts imports on line 1~~, now, the language server tries to detect
      the imports section and inserts the import statement there, although it's always a new line)
- [x] Detection of already-imported-here symbols, so they don't show get re-imported every time
- [ ] Scanning for already used imports throughout the project, so they show up more often as suggestions
      (e.g. if you are already imported `Server` from library B elsewhere, it's probably a better suggestion than `Server` from library A)
- [x] Configurable scoring system for completions (I have to make sure that this is actually possible in the LSP spec)
- [x] Hide (or impede their score) symbols starting with underscores, or coming from modules with names starting with underscores
- [ ] Add configurable lookup paths (for some unusual scenarios and environments)
- [ ] Automated tests (partially done)
- [ ] More configurable options in general
- [ ] Relative import support for intra-package imports
- [ ] Showing better description of the symbol in the completion list (e.g. function signature)
  - [ ] Merge overloads descriptions into one

## Development

Contributions are welcome! To install a development environment refer to "Install from Source" section.
Usage of pre-commit hooks is recommended - after clone, run `pre-commit install` ([more info](https://pre-commit.com/#install)).


### Project Structure

- `src/import_completer/`: Main source code
  - `cli_entrypoint.py`: CLI entry point (`py-import-completer-server` command); parses flags into a `Config` and starts the server (or runs preview mode)
  - `language_server.py`: LSP server implementation
  - `completion_engine.py`: Core completion logic
  - `symbols_database.py`: SQLite symbol storage (async, via `aiosqlite`)
  - `code_scanner.py`: Tree-sitter-based Python parser; symbol extraction and scoring
  - `origin.py`: Module path discovery (inspects the `python` binary's `sys.path`)
  - `symbol_utils.py`: Import statement generation and symbol helpers
  - `types.py`: Shared type definitions
  - `config.py`: Configuration management (`Config`, `ModulesScoringConfig`, `get_version`)

## Contributing

Contributions are welcome! Please feel free to:

- Report bugs by opening an issue
- Suggest features or improvements
- Submit pull requests

Before submitting a PR, please ensure to follow pre-commit config (see [.pre-commit-config.yaml](.pre-commit-config.yaml)).

## License

This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.

## Acknowledgments

Built with:
- [pygls](https://github.com/openlawlibrary/pygls) - Python Language Server framework
- [tree-sitter](https://tree-sitter.github.io/) - Incremental parsing library
- [tree-sitter-python](https://github.com/tree-sitter/tree-sitter-python) - Python grammar for tree-sitter
- [click](https://click.palletsprojects.com/) - CLI framework
- [aiosqlite](https://github.com/omnilib/aiosqlite) - Async SQLite wrapper
- [loguru](https://github.com/Delgan/loguru) - Logging library
