From ced2d4755626e11d575c989a12be290a8f5ac0cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Cepl?= Date: Tue, 30 Dec 2025 19:53:47 +0100 Subject: [PATCH 1/2] doc: Comprehensive rewrite documentation to cover whole plugin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add automatic documentation build and deployment to GitHub Pages - Add rename configuration guide with examples - Update README with concise documentation and links to RTD - Add docs optional dependencies to pyproject.toml - Add .readthedocs.yaml configuration file - Add comprehensive test suite demonstrating configuration patterns of the rename functionality Fixes: https://github.com/python-rope/pylsp-rope/issues/29 Signed-off-by: Matěj Cepl --- .github/workflows/python-publish.yml | 13 + .readthedocs.yaml | 20 ++ README.md | 199 +++++-------- doc/.gitignore | 1 + doc/conf.py | 89 ++++++ doc/configuration.md | 179 ++++++++++++ doc/features.md | 242 ++++++++++++++++ doc/index.md | 80 ++++++ doc/installation.md | 167 +++++++++++ doc/requirements.txt | 3 + doc/troubleshooting.md | 342 ++++++++++++++++++++++ doc/usage.md | 409 +++++++++++++++++++++++++++ setup.cfg | 8 +- test/test_rename_configuration.py | 112 ++++++++ 14 files changed, 1728 insertions(+), 136 deletions(-) create mode 100644 .readthedocs.yaml create mode 100644 doc/.gitignore create mode 100644 doc/conf.py create mode 100644 doc/configuration.md create mode 100644 doc/features.md create mode 100644 doc/index.md create mode 100644 doc/installation.md create mode 100644 doc/requirements.txt create mode 100644 doc/troubleshooting.md create mode 100644 doc/usage.md create mode 100644 test/test_rename_configuration.py diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 2b260e7..12ee86d 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -33,3 +33,16 @@ jobs: - name: Publish package if: startsWith(github.ref, 'refs/tags') uses: pypa/gh-action-pypi-publish@81e9d935c883d0b210363ab89cf05f3894778450 + - name: Build documentation + if: startsWith(github.ref, 'refs/tags') + run: | + cd doc + pip install sphinx sphinx-rtd-theme myst-parser + sphinx-build -b html . _build/html + - name: Deploy documentation + if: startsWith(github.ref, 'refs/tags') + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./doc/_build/html + destination_dir: docs diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..54ff883 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,20 @@ +# .readthedocs.yaml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +version: 2 + +# Set the OS, Python version and other tools you might need +build: + os: ubuntu-22.04 + tools: + python: "3.9" + +# Build documentation in the "doc/" directory with Sphinx +sphinx: + configuration: doc/conf.py + +# Optionally declare the Python requirements required to build your docs +python: + install: + - requirements: doc/requirements.txt \ No newline at end of file diff --git a/README.md b/README.md index 8841e10..a368a08 100644 --- a/README.md +++ b/README.md @@ -2,158 +2,94 @@ [![Tests](https://github.com/python-rope/pylsp-rope/actions/workflows/run-test.yml/badge.svg)](https://github.com/python-rope/pylsp-rope/actions/workflows/run-test.yml) [![codecov](https://codecov.io/gh/python-rope/pylsp-rope/graph/badge.svg?token=LMO7PW0AEK)](https://codecov.io/gh/python-rope/pylsp-rope) +[![Documentation](https://readthedocs.org/projects/pylsp-rope/badge/?version=latest)](https://pylsp-rope.readthedocs.io/en/latest/?badge=latest) +[![PyPI version](https://badge.fury.io/py/pylsp-rope.svg)](https://badge.fury.io/py/pylsp-rope) -Extended refactoring capabilities for Python LSP Server using -[Rope](https://github.com/python-rope/rope). +Extended refactoring capabilities for Python LSP Server using [Rope](https://github.com/python-rope/rope). -This is a plugin for [Python LSP -Server](https://github.com/python-lsp/python-lsp-server), so you also need to -have it installed. +This is a plugin for [Python LSP Server](https://github.com/python-lsp/python-lsp-server), so you also need to have it installed. -python-lsp-server already has basic built-in support for using Rope, but it's -currently limited to just renaming and completion. Installing this plugin adds -more refactoring functionality to python-lsp-server. - -## Installation - -To use this plugin, you need to install this plugin in the same virtualenv as -python-lsp-server itself. - -``` bash -pip install pylsp-rope -``` - -Then run `pylsp` as usual, the plugin will be auto-discovered by -python-lsp-server if you've installed it to the right environment. On Vim, -refer to [Rope in Vim or -Neovim](https://github.com/python-rope/rope/wiki/Rope-in-Vim-or-Neovim). For -other editors, refer to your IDE/text editor's documentation on how to setup a -language server. +python-lsp-server already has basic built-in support for using Rope, but it's currently limited to just renaming and completion. Installing this plugin adds more refactoring functionality to python-lsp-server. ## Features -This plugin adds the following features to python-lsp-server: - -Rename: - -- implemented: variables, classes, functions (disabled by default) -- coming soon: modules, packages (disabled by default) - -Code Action: - -- extract method -- extract variable -- inline method/variable/parameter -- use function -- method to method object -- convert local variable to field -- organize imports -- introduce parameter -- generate variable/function/class from undefined variable - -Refer to [Rope documentation](https://github.com/python-rope/rope/blob/master/docs/overview.rst) -for more details on how these refactoring works. - -## Usage - -### Rename - -When Rename is triggered, rename the symbol under the cursor. If the symbol -under the cursor points to a module/package, it will move that module/package -files. - -### Extract method - -Variants: - -- Extract method -- Extract global method -- Extract method including similar statements -- Extract global method including similar statements +- **Enhanced Rename**: Advanced cross-file renaming with Rope's refactoring engine +- **Code Actions**: 12+ refactoring operations including extract method, inline, use function, and more +- **Code Generation**: Create variables, functions, and classes from undefined symbols +- **Import Organization**: Automatic cleanup and sorting of imports +- **Local to Field**: Convert local variables to class attributes -When CodeAction is triggered and the cursor is on any block of code, extract -that expression into a method. Optionally, similar statements can also be -extracted. +## Quick Start -### Extract variable +### Installation -Variants: +Install pylsp-rope in the same virtualenv as python-lsp-server: -- Extract variable -- Extract global variable -- Extract variable including similar statements -- Extract global variable including similar statements - -When CodeAction is triggered and the cursor is on a expression, extract that -expression into a variable. Optionally, similar statements can also be -extracted. - -### Inline - -When CodeAction is triggered and the cursor is on a resolvable Python variable, -replace all calls to that method with the method body. - -### Use function - -When CodeAction is triggered and the cursor is on the function name of a `def` -statement, try to replace code whose AST matches the selected function with a -call to the function. - -### Method to method object - -When CodeAction is triggered and the cursor is on the function name of a `def` -statement, create a callable class to replace that method. You may want to -inline the method afterwards to remove the indirection. - -### Convert local variable to field - -When CodeAction is triggered wand the cursor is on a local variable inside a -method, convert that local variable to an attribute. - -### Organize import - -Trigger CodeAction anywhere in a Python file to organize imports. +```bash +pip install pylsp-rope +``` -### Introduce parameter +### Basic Configuration -When CodeAction is triggered and the cursor is selecting a Python variable or -attribute, make that variable/attribute a parameter. +Add to your LSP configuration: -### Generate code +```json +{ + "pylsp": { + "plugins": { + "pylsp_rope": { + "enabled": true + } + } + } +} +``` -Variants: +### Enable Rename Support + +For advanced rename functionality, enable it explicitly: + +```json +{ + "pylsp": { + "plugins": { + "pylsp_rope": { + "enabled": true, + "rename": true + } + } + } +} +``` -- [x] Generate variable -- [x] Generate function -- [x] Generate class -- [ ] Generate module -- [ ] Generate package +## Documentation -When CodeAction is triggered and the cursor is on an undefined Python -variable, generate an empty variable/function/class/module/package for that -name. +Complete documentation is available at **[pylsp-rope.readthedocs.io](https://pylsp-rope.readthedocs.io/)** -## Configuration +- **[Installation Guide](https://pylsp-rope.readthedocs.io/en/latest/installation.html)** - Setup for various editors +- **[Configuration Guide](https://pylsp-rope.readthedocs.io/en/latest/configuration.html)** - Complete configuration options +- **[Features Overview](https://pylsp-rope.readthedocs.io/en/latest/features.html)** - Detailed feature descriptions +- **[Usage Examples](https://pylsp-rope.readthedocs.io/en/latest/usage.html)** - Practical examples +- **[Troubleshooting](https://pylsp-rope.readthedocs.io/en/latest/troubleshooting.html)** - Common issues and solutions -You can enable rename support using pylsp-rope with workspace config key -`pylsp.plugins.pylsp_rope.rename`. +## Available Refactorings -Note that this differs from the config key `pylsp.plugins.rope_rename.enabled` -that is used for the rope rename implementation using the python-lsp-rope's -builtin `rope_rename` plugin. To avoid confusion, avoid enabling more than one -python-lsp-server rename plugin. In other words, you should set both -`pylsp.plugins.rope_rename.enabled = false` and `pylsp.plugins.jedi_rename.enabled = false` -when pylsp-rope rename is enabled. +| Category | Operations | +|----------|------------| +| **Extract** | Method, Variable (with similar statement support) | +| **Inline** | Method, Variable, Parameter | +| **Restructure** | Use Function, Method to Method Object, Local to Field | +| **Generate** | Variable, Function, Class | +| **Utilities** | Organize Imports, Introduce Parameter | +| **Rename** | Enhanced cross-file renaming (Rope-based) | ## Caveat -Support for working on unsaved document is currently experimental, but it should work. +Support for working on unsaved documents is currently experimental, but it should work. -This plugin is in early development, so expect some bugs. Please report in -[Github issue tracker](https://github.com/python-lsp/python-lsp-server/issues) -if you had any issues with the plugin. +This plugin is in early development, so expect some bugs. Please report in the [GitHub issue tracker](https://github.com/python-lsp/python-lsp-server/issues) if you have any issues with the plugin. -## Developing +## Contributing See [CONTRIBUTING.md](https://github.com/python-rope/pylsp-rope/blob/main/CONTRIBUTING.md). @@ -161,11 +97,6 @@ See [CONTRIBUTING.md](https://github.com/python-rope/pylsp-rope/blob/main/CONTRI [![Packaging status](https://repology.org/badge/vertical-allrepos/python:pylsp-rope.svg)](https://repology.org/project/python:pylsp-rope/versions) -[![Packaging status](https://repology.org/badge/vertical-allrepos/python:lsp-rope.svg)](https://repology.org/project/python:lsp-rope/versions) - ## Credits -This package was created with -[Cookiecutter](https://github.com/audreyr/cookiecutter) from -[python-lsp/cookiecutter-pylsp-plugin](https://github.com/python-lsp/cookiecutter-pylsp-plugin) -project template. +This package was created with [Cookiecutter](https://github.com/audreyr/cookiecutter) from [python-lsp/cookiecutter-pylsp-plugin](https://github.com/python-lsp/cookiecutter-pylsp-plugin) project template. \ No newline at end of file diff --git a/doc/.gitignore b/doc/.gitignore new file mode 100644 index 0000000..88f9974 --- /dev/null +++ b/doc/.gitignore @@ -0,0 +1 @@ +_build/* diff --git a/doc/conf.py b/doc/conf.py new file mode 100644 index 0000000..b00f53a --- /dev/null +++ b/doc/conf.py @@ -0,0 +1,89 @@ +# Sphinx configuration file + +import os +import sys + +# Add project root to Python path for autodoc +sys.path.insert(0, os.path.abspath("..")) + +# -- Project information -- +project = "pylsp-rope" +copyright = "2024, pylsp-rope contributors" +author = "pylsp-rope contributors" +release = "0.1.4" + +# -- General configuration -- +extensions = [ + "myst_parser", # Markdown support + "sphinx.ext.autodoc", + "sphinx.ext.viewcode", + "sphinx.ext.napoleon", +] + +# MyST parser configuration +myst_enable_extensions = [ + "colon_fence", + "deflist", + "html_admonition", + "linkify", + "replacements", + "smartquotes", + "substitution", + "tasklist", +] + +# -- Options for HTML output -- +html_theme = "sphinx_rtd_theme" +html_static_path = [] +html_theme_options = { + "canonical_url": "", + "analytics_id": "", + "logo_only": False, + "prev_next_buttons_location": "bottom", + "style_external_links": False, + "collapse_navigation": True, + "sticky_navigation": True, + "navigation_depth": 4, + "includehidden": True, + "titles_only": False, +} + +# -- Master document -- +master_doc = "index" + +# -- Exclude patterns -- +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] + +# -- Options for other output formats -- +htmlhelp_basename = "pylsp-ropedoc" +latex_documents = [ + ( + master_doc, + "pylsp-rope.tex", + "pylsp-rope Documentation", + "pylsp-rope contributors", + "manual", + ), +] + +man_pages = [(master_doc, "pylsp-rope", "pylsp-rope Documentation", [author], 1)] + +texinfo_documents = [ + ( + master_doc, + "pylsp-rope", + "pylsp-rope Documentation", + author, + "pylsp-rope", + "One line description of project.", + "Miscellaneous", + ), +] + +# -- Exclude patterns -- +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] + +# -- ReadTheDocs specific configuration -- +# When building on ReadTheDocs, these environment variables are available +if os.environ.get("READTHEDOCS") == "True": + html_theme = "sphinx_rtd_theme" diff --git a/doc/configuration.md b/doc/configuration.md new file mode 100644 index 0000000..8d919eb --- /dev/null +++ b/doc/configuration.md @@ -0,0 +1,179 @@ +# Configuration + +This document provides comprehensive configuration information for pylsp-rope. + +## Plugin Configuration + +pylsp-rope is configured through the standard `pylsp_settings` in your LSP client configuration. The plugin settings are located under `plugins.pylsp_rope`. + +### Available Settings + +| Setting | Type | Default | Description | +|---------|------|---------|-------------| +| `enabled` | boolean | `true` | Enable/disable the pylsp-rope plugin | +| `rename` | boolean | `false` | Enable pylsp-rope's rename functionality | + +### Basic Configuration + +To enable pylsp-rope with default settings: + +```json +{ + "pylsp": { + "plugins": { + "pylsp_rope": { + "enabled": true + } + } + } +} +``` + +## Rename Configuration + +### Enabling Rename Support + +To enable pylsp-rope's rename functionality, add the following to your LSP configuration: + +```json +{ + "pylsp": { + "plugins": { + "pylsp_rope": { + "enabled": true, + "rename": true + }, + "rope_rename": { + "enabled": false + }, + "jedi_rename": { + "enabled": false + } + } + } +} +``` + +**Important:** Only enable one rename plugin at a time to avoid conflicts. + +### Rename Plugin Comparison + +| Plugin | Identifier | Description | When to Use | +|--------|------------|-------------|-------------| +| **pylsp-rope** | `pylsp_rope` | This plugin's rename using Rope's refactoring engine | Complex cross-file refactoring, future module/package support | +| **Built-in Rope** | `rope_rename` | Built-in python-lsp-server Rope rename | Basic variable/class/function renaming | +| **Built-in Jedi** | `jedi_rename` | Built-in python-lsp-server Jedi rename | Simple renaming scenarios | + +### Verification + +To verify that pylsp-rope is handling rename requests: + +1. Check your LSP logs - pylsp-rope logs `"textDocument/rename: ..."` when handling rename requests +2. The rename behavior uses Rope's refactoring engine, which may handle complex scenarios differently than Jedi +3. Ensure other rename plugins are disabled as shown above + +### Current Limitations and Future Support + +Currently, pylsp-rope's rename supports: +- ✅ Variables, classes, and functions +- ❌ Modules and packages (planned for a future release) + +### Benefits of pylsp-rope Rename + +- More mature refactoring engine (Rope) +- Improved handling of complex cross-file refactoring scenarios +- Foundation for future module/package renaming capabilities + +If you only need basic variable, class, or function renaming, the built-in rename plugin is sufficient. Use pylsp-rope if you require Rope's advanced refactoring capabilities or plan to use future module/package support. + +## Code Actions Configuration + +All code actions provided by pylsp-rope are automatically available when the plugin is enabled with `"pylsp.plugins.pylsp_rope.enabled": true`. No additional configuration is required. + +### Available Code Actions + +- **Extract Method** - Extract code blocks into methods +- **Extract Variable** - Extract expressions into variables +- **Inline** - Inline methods/variables/parameters +- **Use Function** - Replace matching code with function calls +- **Method to Method Object** - Convert methods to callable classes +- **Convert Local Variable to Field** - Convert locals to class attributes +- **Organize Imports** - Clean up import statements +- **Introduce Parameter** - Convert variables to method parameters +- **Generate Code** - Create undefined variables/functions/classes + +## Editor-Specific Configuration + +### Vim/Neovim + +For Vim or Neovim, add the configuration to your `vimrc` or init file: + +```vim +lua << EOF +require'lspconfig'.pylsp.setup{ + settings = { + pylsp = { + plugins = { + pylsp_rope = { + enabled = true, + rename = true + }, + rope_rename = { + enabled = false + }, + jedi_rename = { + enabled = false + } + } + } + } +} +EOF +``` + +### VS Code + +For VS Code with the Python LSP plugin, add to your `settings.json`: + +```json +{ + "pylsp.configurationSources": ["pycodestyle"], + "pylsp.plugins.pylsp_rope.enabled": true, + "pylsp.plugins.pylsp_rope.rename": true, + "pylsp.plugins.rope_rename.enabled": false, + "pylsp.plugins.jedi_rename.enabled": false +} +``` + +### Emacs + +For Emacs with lsp-mode, add to your configuration: + +```elisp +(setq lsp-pylsp-plugins-pylsp-rope-enabled t + lsp-pylsp-plugins-pylsp-rope-rename t + lsp-pylsp-plugins-rope-rename-enabled nil + lsp-pylsp-plugins-jedi-rename-enabled nil) +``` + +## Troubleshooting + +### Plugin Not Loading + +1. Verify pylsp-rope is installed in the same virtualenv as python-lsp-server +2. Check LSP logs for initialization messages +3. Ensure `pylsp_rope` appears in the plugin list + +### Rename Not Working + +1. Verify `rename: true` is set in pylsp_rope configuration +2. Ensure other rename plugins are disabled +3. Check logs for `"textDocument/rename"` messages +4. Try restarting the language server + +### Code Actions Not Available + +1. Ensure the plugin is enabled: `"enabled": true` +2. Check that the cursor is in the correct position for the action +3. Verify the code structure supports the requested refactoring +4. Restart the language server if needed \ No newline at end of file diff --git a/doc/features.md b/doc/features.md new file mode 100644 index 0000000..2ed0f1d --- /dev/null +++ b/doc/features.md @@ -0,0 +1,242 @@ +# Features Overview + +This document provides detailed information about all features available in pylsp-rope. + +## Rename Features + +### Current Capabilities +- ✅ **Variables**: Rename local and global variables +- ✅ **Classes**: Rename class definitions and their usages +- ✅ **Functions**: Rename function definitions and calls +- ❌ **Modules**: Move and rename module files (planned) +- ❌ **Packages**: Move and rename package directories (planned) + +### Rename Behavior +When Rename is triggered on a symbol under the cursor: + +1. **Variables, Classes, Functions**: Renames all occurrences across the project +2. **Modules/Packages** (future): Will move files and update all import statements + +## Code Actions + +### Extract Method + +**Variants:** +- Extract method +- Extract global method +- Extract method including similar statements +- Extract global method including similar statements + +**Usage:** Trigger CodeAction on any code block to extract it into a method. Similar statements can optionally be extracted as well. + +**Example:** +```python +# Before +def process_data(data): + result = data * 2 + result = result + 10 + return result + +# After extracting "result = result + 10" +def process_data(data): + result = data * 2 + result = add_offset(result) + return result + +def add_offset(value): + return value + 10 +``` + +### Extract Variable + +**Variants:** +- Extract variable +- Extract global variable +- Extract variable including similar statements +- Extract global variable including similar statements + +**Usage:** Trigger CodeAction on an expression to extract it into a variable. Similar statements can optionally be extracted as well. + +**Example:** +```python +# Before +def calculate(a, b): + return a * 2 + b * 2 + +# After extracting "a * 2" into "doubled_a" +def calculate(a, b): + doubled_a = a * 2 + return doubled_a + b * 2 +``` + +### Inline + +**Usage:** Trigger CodeAction on a resolvable variable or method to replace all calls with the actual implementation. + +**Example:** +```python +# Before +def helper(x): + return x * 2 + +def calculate(): + result = helper(5) + return result + +# After inlining helper() +def calculate(): + result = 5 * 2 + return result +``` + +### Use Function + +**Usage:** Trigger CodeAction on a function name to replace code matching the function's body with a call to that function. + +**Example:** +```python +# Before +def calculate(): + a = 5 + b = a * 2 + return b + +def another_function(): + x = 10 + y = x * 2 + return y + +# After using calculate() for the duplicated code +def calculate(): + a = 5 + b = calculate() + return b + +def another_function(): + x = 10 + y = calculate() + return y +``` + +### Method to Method Object + +**Usage:** Trigger CodeAction on a method to create a callable class that replaces that method. This is useful for complex methods that need to be converted to objects. + +**Example:** +```python +# Before +class Calculator: + def complex_calculation(self, x, y): + # complex logic here + intermediate = x + y + result = intermediate * 2 + return result + +# After +class ComplexCalculation: + def __call__(self, x, y): + intermediate = x + y + result = intermediate * 2 + return result + +class Calculator: + def complex_calculation(self, x, y): + return ComplexCalculation()(x, y) +``` + +### Convert Local Variable to Field + +**Usage:** Trigger CodeAction on a local variable inside a method to convert it to a class attribute. + +**Example:** +```python +# Before +class Counter: + def __init__(self): + self.count = 0 + + def increment(self): + local_count = self.count + 1 + self.count = local_count + +# After +class Counter: + def __init__(self): + self.count = 0 + + def increment(self): + self.count = self.count + 1 +``` + +### Organize Imports + +**Usage:** Trigger CodeAction anywhere in a Python file to clean up import statements: +- Remove unused imports +- Sort imports alphabetically +- Group imports by type (standard library, third-party, local) + +### Introduce Parameter + +**Usage:** Trigger CodeAction while selecting a variable or attribute to make it a method parameter. + +**Example:** +```python +# Before +class Greeter: + def __init__(self): + self.name = "World" + + def greet(self): + return f"Hello, {self.name}!" + +# After introducing parameter +class Greeter: + def __init__(self): + self.name = "World" + + def greet(self, name): + return f"Hello, {name}!" +``` + +### Generate Code + +**Variants:** +- ✅ Generate variable +- ✅ Generate function +- ✅ Generate class +- ❌ Generate module (planned) +- ❌ Generate package (planned) + +**Usage:** Trigger CodeAction on an undefined variable to generate an empty definition. + +**Example:** +```python +# Before +def main(): + result = calculate_something(5, 10) + print(result) + +# After generating calculate_something +def calculate_something(a, b): + pass + +def main(): + result = calculate_something(5, 10) + print(result) +``` + +## Refactoring Safety + +All refactorings use Rope's sophisticated analysis engine to: +- Preserve code semantics +- Handle cross-file dependencies +- Maintain proper scoping +- Update import statements as needed +- Handle complex inheritance hierarchies + +## Limitations + +- Support for unsaved documents is experimental +- Module and package renaming is not yet implemented +- Some complex refactorings may require manual intervention +- Performance may vary with very large codebases diff --git a/doc/index.md b/doc/index.md new file mode 100644 index 0000000..af0edde --- /dev/null +++ b/doc/index.md @@ -0,0 +1,80 @@ +# pylsp-rope Documentation + +Welcome to the comprehensive documentation for pylsp-rope, a plugin that extends Python LSP Server with advanced refactoring capabilities using Rope. + +## Features + +This plugin adds the following features to python-lsp-server: + +### Rename Functionality +- **Implemented**: Variables, classes, functions (disabled by default, requires explicit configuration) +- **Planned**: Modules, packages (coming soon) + +### Code Actions +- Extract method +- Extract variable +- Inline method/variable/parameter +- Use function +- Method to method object +- Convert local variable to field +- Organize imports +- Introduce parameter +- Generate variable/function/class from undefined variable + +## Documentation + +```{toctree} +--- +maxdepth: 2 +caption: Contents: +--- +configuration +features +installation +usage +troubleshooting +``` + +## Quick Start + +1. Install pylsp-rope: + ```bash + pip install pylsp-rope + ``` + +2. Configure in your LSP client: + ```json + { + "pylsp": { + "plugins": { + "pylsp_rope": { + "enabled": true + } + } + } + } + ``` + +3. For rename functionality, enable it explicitly: + ```json + { + "pylsp": { + "plugins": { + "pylsp_rope": { + "enabled": true, + "rename": true + } + } + } + } + ``` + +## Contributing + +Found a bug or want to contribute? Please check the Contributing Guidelines in the main repository at `../CONTRIBUTING.md`. + +## Support + +For issues and questions: +- [GitHub Issues](https://github.com/python-rope/pylsp-rope/issues) +- [Rope Documentation](https://github.com/python-rope/rope/blob/master/docs/overview.rst) diff --git a/doc/installation.md b/doc/installation.md new file mode 100644 index 0000000..adcb90a --- /dev/null +++ b/doc/installation.md @@ -0,0 +1,167 @@ +# Installation Guide + +This guide covers how to install and set up pylsp-rope. + +## Prerequisites + +- Python 3.8 or higher +- [Python LSP Server](https://github.com/python-lsp/python-lsp-server) installed +- An LSP-capable editor/IDE + +## Installation + +### Standard Installation + +Install pylsp-rope in the same virtual environment as python-lsp-server: + +```bash +pip install pylsp-rope +``` + +### Development Installation + +For development or testing: + +```bash +git clone https://github.com/python-rope/pylsp-rope.git +cd pylsp-rope +pip install -e . +``` + +### Verification + +To verify installation: + +1. Check that pylsp can load the plugin: + ```bash + pylsp --help + ``` + +2. Start your LSP client and check logs for: + ``` + Initializing pylsp_rope + ``` + +## Editor Setup + +### Vim/Neovim + +Using [vim-lsp](https://github.com/prabirshrestha/vim-lsp) or [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig): + +```vim +" For vim-lsp +if executable('pylsp') + au User lsp_setup call lsp#register_server({ + \ 'name': 'pylsp', + \ 'cmd': {server_info->['pylsp']}, + \ 'allowlist': ['python'], + \ }) +endif + +" For nvim-lspconfig +require'lspconfig'.pylsp.setup{ + settings = { + pylsp = { + plugins = { + pylsp_rope = { + enabled = true + } + } + } + } +} +``` + +### Visual Studio Code + +Install the official Python extension and configure: + +```json +{ + "pylsp.configurationSources": ["pycodestyle"], + "pylsp.plugins.pylsp_rope.enabled": true +} +``` + +### Emacs + +Using [lsp-mode](https://github.com/emacs-lsp/lsp-mode): + +```elisp +(use-package lsp-pylsp + :config + (setq lsp-pylsp-plugins-pylsp_rope-enabled t)) +``` + +### Sublime Text + +Using [LSP-pylsp](https://packagecontrol.io/packages/LSP-pylsp): + +```json +{ + "LSP-pylsp": { + "settings": { + "pylsp": { + "plugins": { + "pylsp_rope": { + "enabled": true + } + } + } + } + } +} +``` + +### Other Editors + +Any editor that supports the Language Server Protocol can use pylsp-rope. Configure the server to use `pylsp` and ensure the plugin settings are properly configured. + +## Troubleshooting + +### Plugin Not Found + +If pylsp-rope is not recognized: + +1. **Check Installation**: Ensure it's installed in the same environment as pylsp + ```bash + pip list | grep pylsp-rope + ``` + +2. **Verify Path**: Check that your editor is using the correct Python interpreter + +3. **Restart LSP**: Restart the language server in your editor + +### Virtual Environment Issues + +If using virtual environments: + +1. Activate the environment before installing +2. Install both `python-lsp-server` and `pylsp-rope` in the same environment +3. Configure your editor to use the virtual environment's Python interpreter + +Example with pipenv: +```bash +pipenv install python-lsp-server pylsp-rope +pipenv shell +``` + +Example with poetry: +```bash +poetry add python-lsp-server pylsp-rope +poetry shell +``` + +### Performance Considerations + +For large codebases: + +1. Increase Rope's memory limits in your LSP configuration +2. Consider disabling unused features +3. Ensure sufficient disk space for Rope's project cache + +## Next Steps + +- See [Configuration Guide](configuration.md) for detailed configuration options +- Check [Features Overview](features.md) to understand available refactorings +- Refer to [Usage Examples](usage.md) for practical examples \ No newline at end of file diff --git a/doc/requirements.txt b/doc/requirements.txt new file mode 100644 index 0000000..a30eaf3 --- /dev/null +++ b/doc/requirements.txt @@ -0,0 +1,3 @@ +sphinx>=4.0.0 +sphinx-rtd-theme>=1.0.0 +myst-parser>=0.17.0 \ No newline at end of file diff --git a/doc/troubleshooting.md b/doc/troubleshooting.md new file mode 100644 index 0000000..e5074b1 --- /dev/null +++ b/doc/troubleshooting.md @@ -0,0 +1,342 @@ +# Troubleshooting + +This document provides solutions to common issues with pylsp-rope. + +## General Issues + +### Plugin Not Loading + +**Symptoms:** +- pylsp-rope features don't appear in your editor +- LSP logs don't mention pylsp-rope initialization +- Code actions for refactoring are missing + +**Solutions:** + +1. **Check Installation:** + ```bash + pip list | grep pylsp-rope + ``` + If not installed, install in the same environment as pylsp: + ```bash + pip install pylsp-rope + ``` + +2. **Verify Environment:** + Ensure pylsp-rope and python-lsp-server are in the same virtual environment: + ```bash + which pylsp + pip list | grep pylsp + ``` + +3. **Check Configuration:** + Verify the plugin is enabled in your LSP settings: + ```json + { + "pylsp": { + "plugins": { + "pylsp_rope": { + "enabled": true + } + } + } + } + ``` + +4. **Restart LSP:** + Restart the language server in your editor. + +### Performance Issues + +**Symptoms:** +- Slow response times for refactoring operations +- High memory usage +- Editor freezing during large refactorings + +**Solutions:** + +1. **Increase Memory:** + Add memory limits to your configuration if available. + +2. **Disable Unused Features:** + Only enable features you actually use: + ```json + { + "pylsp": { + "plugins": { + "pylsp_rope": { + "enabled": true, + "rename": false // Disable if not used + } + } + } + } + ``` + +3. **Clean Project Cache:** + Delete Rope's cache directory: + ```bash + rm -rf .rope_project + ``` + +## Rename-Specific Issues + +### Rename Not Working + +**Symptoms:** +- Rename operation doesn't trigger +- Old rename plugin still active +- No rename dialog appears + +**Solutions:** + +1. **Enable Rename Explicitly:** + ```json + { + "pylsp": { + "plugins": { + "pylsp_rope": { + "enabled": true, + "rename": true + }, + "rope_rename": { + "enabled": false + }, + "jedi_rename": { + "enabled": false + } + } + } + } + ``` + +2. **Check for Conflicting Plugins:** + Ensure only one rename plugin is enabled: + - `pylsp_rope` (this plugin) + - `rope_rename` (built-in rope) + - `jedi_rename` (built-in jedi) + +3. **Verify Active Plugin:** + Check LSP logs for `"textDocument/rename"` messages from pylsp-rope. + +### Rename Not Updating All Files + +**Symptoms:** +- Rename only affects current file +- Cross-file references not updated +- Import statements not modified + +**Solutions:** + +1. **Save All Files:** + Ensure all modified files are saved before renaming. + +2. **Check Project Structure:** + Verify your editor has recognized the entire project as a workspace. + +3. **Restart LSP:** + Restart the language server to refresh project context. + +## Code Action Issues + +### Code Actions Not Available + +**Symptoms:** +- Refactoring options don't appear in code action menu +- "No code actions available" message +- Some refactorings work, others don't + +**Solutions:** + +1. **Check Cursor Position:** + Ensure cursor is positioned correctly for the intended refactoring: + - For extract: on the code block/expression + - For inline: on the variable/method name + - For use function: on the function definition + +2. **Verify Code Structure:** + Some refactorings require specific code structures: + - Extract method needs valid Python statements + - Inline needs resolvable references + - Generate code needs undefined symbols + +3. **Check for Syntax Errors:** + Fix any syntax errors in the file before refactoring. + +### Refactoring Fails with Error + +**Symptoms:** +- Error dialog appears +- Refactoring operation completes incorrectly +- Files become corrupted + +**Solutions:** + +1. **Check LSP Logs:** + Look for detailed error messages in the language server logs. + +2. **Save All Files:** + Ensure all files are saved before refactoring. + +3. **Try Manual Refactoring:** + If automatic refactoring fails, consider doing it manually. + +4. **Report Issue:** + If it's a bug, file an issue with: + - The error message + - Code example that reproduces the issue + - Steps to reproduce + +## Editor-Specific Issues + +### Vim/Neovim + +#### No Code Actions + +**Solution:** +```vim +" Check if LSP is working +:lua print(vim.inspect(vim.lsp.get_active_clients())) + +" Manually trigger code actions +:lua vim.lsp.buf.code_action() +``` + +#### Conflicting Keybindings + +**Solution:** +```vim +" Map code actions to available keybinding +nnoremap lua vim.lsp.buf.code_action() +``` + +### VS Code + +#### Multiple Python Interpreters + +**Symptoms:** +- pylsp-rope installed but not recognized +- Different Python interpreters being used + +**Solution:** +1. Set the interpreter explicitly: + ```json + { + "python.defaultInterpreterPath": "/path/to/your/venv/bin/python" + } + ``` + +2. Restart VS Code and reload the workspace. + +#### Extensions Conflict + +**Solution:** +Disable conflicting Python extensions: +- Pylance (may conflict with pylsp) +- Other Python LSP clients + +### Emacs + +#### lsp-mode Issues + +**Solution:** +```elisp +;; Check LSP status +lsp-describe-session + +;; Restart LSP +lsp-restart-workspace +``` + +## Rope-Specific Issues + +### Rope Cache Corruption + +**Symptoms:** +- Inconsistent refactoring behavior +- Errors mentioning Rope cache +- Slow performance + +**Solution:** +```bash +# Remove Rope cache +rm -rf .rope_project +rm -rf ~/.rope +``` + +### Large Project Issues + +**Symptoms:** +- Very slow refactoring on large projects +- Memory errors +- Timeouts + +**Solutions:** + +1. **Exclude Directories:** + Configure pylsp to exclude large directories: + ```json + { + "pylsp": { + "configurationSources": ["pycodestyle"], + "plugins": { + "pylsp_rope": { + "enabled": true + } + } + } + } + ``` + +2. **Use Incremental Refactoring:** + Break large refactorings into smaller steps. + +3. **Consider Project Structure:** + Split very large projects into smaller subprojects. + +## Getting Help + +### Log Files + +**Vim/Neovim:** +```vim +:lua print(vim.lsp.get_log_path()) +``` + +**VS Code:** +- Help → Toggle Developer Tools → Console +- Extensions → Python → Show Log + +**Emacs:** +```elisp +*Messages* buffer +*LSP* buffer +``` + +### Reporting Issues + +When reporting issues, include: + +1. **Environment Information:** + - Python version + - pylsp and pylsp-rope versions + - Editor and version + - Operating system + +2. **Configuration:** + - Your LSP configuration + - Project structure (if relevant) + +3. **Steps to Reproduce:** + - Detailed steps + - Code example + - Expected vs actual behavior + +4. **Logs:** + - LSP logs showing the error + +### Community Support + +- [GitHub Issues](https://github.com/python-rope/pylsp-rope/issues) +- [Rope Documentation](https://github.com/python-rope/rope) +- [Python LSP Server](https://github.com/python-lsp/python-lsp-server) \ No newline at end of file diff --git a/doc/usage.md b/doc/usage.md new file mode 100644 index 0000000..d650b10 --- /dev/null +++ b/doc/usage.md @@ -0,0 +1,409 @@ +# Usage Examples + +This document provides practical examples of using pylsp-rope's refactoring features. + +## Basic Usage Patterns + +Most refactoring operations follow this pattern: +1. Position your cursor on the target code +2. Trigger Code Action in your editor (typically `Ctrl+.` or `Cmd+.`) +3. Select the desired refactoring from the menu +4. Confirm any prompts or provide additional information + +## Extract Method Examples + +### Simple Method Extraction + +**Before:** +```python +def calculate_total(items): + subtotal = 0 + for item in items: + subtotal += item.price * item.quantity + tax = subtotal * 0.08 + return subtotal + tax +``` + +After selecting the tax calculation and triggering "Extract method": + +**After:** +```python +def calculate_total(items): + subtotal = 0 + for item in items: + subtotal += item.price * item.quantity + total_with_tax = add_tax(subtotal) + return total_with_tax + +def add_tax(subtotal): + tax = subtotal * 0.08 + return subtotal + tax +``` + +### Method with Similar Statements + +When similar statements exist, choose "Extract method including similar statements": + +**Before:** +```python +def process_orders(orders): + for order in orders: + if order.status == "pending": + total = sum(item.price for item in order.items) + total = total * 1.1 # Add tax + order.total = total + +def process_invoices(invoices): + for invoice in invoices: + if invoice.status == "unpaid": + total = sum(line.amount for line in invoice.lines) + total = total * 1.1 # Add tax + invoice.total = total +``` + +**After (including similar statements):** +```python +def process_orders(orders): + for order in orders: + if order.status == "pending": + order.total = calculate_tax_total(item.price for item in order.items) + +def process_invoices(invoices): + for invoice in invoices: + if invoice.status == "unpaid": + invoice.total = calculate_tax_total(line.amount for line in invoice.lines) + +def calculate_tax_total(amounts): + total = sum(amounts) + total = total * 1.1 # Add tax + return total +``` + +## Extract Variable Examples + +### Complex Expression + +**Before:** +```python +def calculate_area(shape): + return math.sqrt((shape.x2 - shape.x1) ** 2 + (shape.y2 - shape.y1) ** 2) * 0.5 +``` + +After selecting the distance calculation: + +**After:** +```python +def calculate_area(shape): + distance = math.sqrt((shape.x2 - shape.x1) ** 2 + (shape.y2 - shape.y1) ** 2) + return distance * 0.5 +``` + +## Inline Examples + +### Inline Variable + +**Before:** +```python +def get_price(item): + discounted_price = item.price * 0.9 + return discounted_price +``` + +After triggering "Inline" on `discounted_price`: + +**After:** +```python +def get_price(item): + return item.price * 0.9 +``` + +### Inline Method + +**Before:** +```python +def validate_email(email): + return "@" in email and "." in email + +def process_user(user): + if validate_email(user.email): + send_email(user.email) +``` + +After triggering "Inline" on the `validate_email` call: + +**After:** +```python +def process_user(user): + if "@" in user.email and "." in user.email: + send_email(user.email) +``` + +## Use Function Examples + +### Duplicated Code + +**Before:** +```python +def calculate_circle_area(radius): + result = math.pi * radius ** 2 + print(f"Calculated: {result}") + return result + +def calculate_square_area(side): + result = side ** 2 + print(f"Calculated: {result}") + return result + +def calculate_triangle_area(base, height): + result = 0.5 * base * height + print(f"Calculated: {result}") + return result +``` + +After selecting one of the similar blocks and using "Use function": + +**After:** +```python +def calculate_and_log(calculation): + result = calculation + print(f"Calculated: {result}") + return result + +def calculate_circle_area(radius): + return calculate_and_log(math.pi * radius ** 2) + +def calculate_square_area(side): + return calculate_and_log(side ** 2) + +def calculate_triangle_area(base, height): + return calculate_and_log(0.5 * base * height) +``` + +## Method to Method Object Examples + +### Complex Method + +**Before:** +```python +class ReportGenerator: + def generate_complex_report(self, data): + # Multiple steps with intermediate state + filtered_data = [item for item in data if item.is_valid()] + processed_data = [] + totals = {"count": 0, "sum": 0} + + for item in filtered_data: + processed = self.process_item(item) + processed_data.append(processed) + totals["count"] += 1 + totals["sum"] += processed.value + + summary = self.create_summary(totals) + return { + "data": processed_data, + "summary": summary + } +``` + +After triggering "To method object": + +**After:** +```python +class GenerateComplexReport: + def __init__(self, data): + self.data = data + self.filtered_data = None + self.processed_data = None + self.totals = None + self.summary = None + + def __call__(self): + self.filter_data() + self.process_data() + self.calculate_totals() + self.create_summary() + return { + "data": self.processed_data, + "summary": self.summary + } + + def filter_data(self): + self.filtered_data = [item for item in self.data if item.is_valid()] + + def process_data(self): + self.processed_data = [] + for item in self.filtered_data: + processed = self.process_item(item) + self.processed_data.append(processed) + + def calculate_totals(self): + self.totals = {"count": 0, "sum": 0} + for processed in self.processed_data: + self.totals["count"] += 1 + self.totals["sum"] += processed.value + + def create_summary(self): + self.summary = self.create_summary(self.totals) + +class ReportGenerator: + def generate_complex_report(self, data): + return GenerateComplexReport(data)() +``` + +## Convert Local Variable to Field Examples + +**Before:** +```python +class OrderProcessor: + def process_single_order(self, order): + customer_discount = self.get_customer_discount(order.customer_id) + order_total = order.amount * (1 - customer_discount) + return order_total + + def process_batch(self, orders): + total = 0 + for order in orders: + customer_discount = self.get_customer_discount(order.customer_id) + total += order.amount * (1 - customer_discount) + return total +``` + +After selecting `customer_discount` in the first method and using "Convert local variable to field": + +**After:** +```python +class OrderProcessor: + def process_single_order(self, order): + self.customer_discount = self.get_customer_discount(order.customer_id) + order_total = order.amount * (1 - self.customer_discount) + return order_total + + def process_batch(self, orders): + total = 0 + for order in orders: + customer_discount = self.get_customer_discount(order.customer_id) + total += order.amount * (1 - customer_discount) + return total +``` + +## Introduce Parameter Examples + +**Before:** +```python +class Logger: + def __init__(self): + self.level = "INFO" + + def log_message(self, message): + print(f"[{self.level}] {message}") + +def main(): + logger = Logger() + logger.log_message("Starting process") + logger.log_message("Process complete") +``` + +After selecting `self.level` in the log_message method and using "Introduce parameter": + +**After:** +```python +class Logger: + def __init__(self): + self.level = "INFO" + + def log_message(self, message, level="INFO"): + print(f"[{level}] {message}") + +def main(): + logger = Logger() + logger.log_message("Starting process", logger.level) + logger.log_message("Process complete", logger.level) +``` + +## Generate Code Examples + +### Generate Variable + +**Before:** +```python +def main(): + print(user_name) +``` + +After triggering "Generate variable" on `user_name`: + +**After:** +```python +def main(): + user_name = None + print(user_name) +``` + +### Generate Function + +**Before:** +```python +def main(): + result = calculate_something(5, 10) + print(result) +``` + +After triggering "Generate function" on `calculate_something`: + +**After:** +```python +def calculate_something(a, b): + pass + +def main(): + result = calculate_something(5, 10) + print(result) +``` + +### Generate Class + +**Before:** +```python +def main(): + user = User("John", "Doe") + print(user.get_full_name()) +``` + +After triggering "Generate class" on `User`: + +**After:** +```python +class User: + def __init__(self, arg1, arg2): + pass + + def get_full_name(self): + pass + +def main(): + user = User("John", "Doe") + print(user.get_full_name()) +``` + +## Editor-Specific Shortcuts + +### Vim/Neovim +```vim +" Trigger code actions +nnoremap lua vim.lsp.buf.code_action() +" Show available code actions +nnoremap ca lua vim.lsp.buf.code_action() +``` + +### VS Code +- `Ctrl+.` (Windows/Linux) +- `Cmd+.` (macOS) +- Right-click → "Refactor" + +### Emacs +```elisp +" Trigger code actions +(define-key lsp-mode-map (kbd "C-c C-r") 'lsp-execute-code-action) +``` + +These examples should help you understand how to effectively use pylsp-rope's refactoring capabilities in your development workflow. diff --git a/setup.cfg b/setup.cfg index 902b1b8..a0b42a1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -15,7 +15,6 @@ classifiers = Intended Audience :: Developers Topic :: Text Editors :: Integrated Development Environments (IDE) Topic :: Software Development - License :: OSI Approved :: MIT License [options] @@ -44,13 +43,18 @@ dev = pytest twine - # extras for CI test runner test = flake8 pytest pytest-cov +# extras for documentation +docs = + sphinx>=4.0.0 + sphinx-rtd-theme>=1.0.0 + myst-parser>=0.17.0 + [pycodestyle] max-line-length = 88 diff --git a/test/test_rename_configuration.py b/test/test_rename_configuration.py new file mode 100644 index 0000000..3508fee --- /dev/null +++ b/test/test_rename_configuration.py @@ -0,0 +1,112 @@ +""" +Integration test examples showing how to configure pylsp-rope rename functionality. + +This file demonstrates the configuration patterns that users should follow +to enable and verify pylsp-rope's rename feature. +""" + +import pytest +from pylsp_rope import typing +from pylsp_rope.plugin import pylsp_rename, pylsp_settings +from pylsp_rope.text import Position +from test.conftest import create_document +from test.helpers import assert_text_edits + + +class TestRenameConfigurationExamples: + """Examples showing proper configuration of pylsp-rope rename.""" + + def test_default_configuration_disables_rename(self, config, workspace): + """Test that rename is disabled by default.""" + # Get default settings + settings = pylsp_settings() + assert settings["plugins"]["pylsp_rope"]["rename"] is False + + # Verify rename returns None when disabled + document = create_document(workspace, "simple_rename.py") + position = Position(0, 0) # Position on "Test1" + + response = pylsp_rename(config, workspace, document, position, "NewName") + assert response is None + + def test_enabling_rename_via_configuration(self, config, workspace): + """Example of enabling rename through configuration.""" + # Simulate user configuration + config._plugin_settings["plugins"]["pylsp_rope"] = {"rename": True} + + document = create_document(workspace, "simple_rename.py") + line = 0 + pos = document.lines[line].index("Test1") + position = Position(line, pos) + + response = pylsp_rename( + config, workspace, document, position, "ShouldBeRenamed" + ) + + # Rename should now work + assert response is not None + assert typing.is_workspace_edit_with_changes(response) + + # Verify the rename happened + changes = response["changes"] + doc_uri = typing.DocumentUri(document.uri) + assert doc_uri in changes + new_text = assert_text_edits(changes[doc_uri], target="simple_rename_result.py") + assert "class ShouldBeRenamed()" in new_text + + def test_configuration_with_other_rename_plugins_disabled(self, config): + """Example showing complete rename configuration.""" + settings = pylsp_settings() + + # This is the recommended configuration pattern: + config_dict = { + "pylsp": { + "plugins": { + "pylsp_rope": {"enabled": True, "rename": True}, + "rope_rename": {"enabled": False}, + "jedi_rename": {"enabled": False}, + } + } + } + + # Verify pylsp-rope settings match recommendation + pylsp_rope_settings = settings["plugins"]["pylsp_rope"] + assert pylsp_rope_settings["enabled"] is True + assert ( + pylsp_rope_settings["rename"] is False + ) # Default, user should set to True + + def test_disabling_rename_after_enabling(self, config, workspace): + """Test that rename can be dynamically disabled.""" + # First enable rename + config._plugin_settings["plugins"]["pylsp_rope"] = {"rename": True} + + document = create_document(workspace, "simple_rename.py") + line = 0 + pos = document.lines[line].index("Test1") + position = Position(line, pos) + + # Rename should work + response = pylsp_rename(config, workspace, document, position, "NewName") + assert response is not None + + # Now disable rename + config._plugin_settings["plugins"]["pylsp_rope"] = {"rename": False} + + # Rename should not work + response = pylsp_rename(config, workspace, document, position, "AnotherName") + assert response is None + + def test_missing_configuration_key_defaults_to_disabled(self, config, workspace): + """Test that missing configuration key defaults to disabled.""" + # Remove the rename key entirely + plugin_settings = config.plugin_settings("pylsp_rope", "test://test.py") + if "rename" in plugin_settings: + del plugin_settings["rename"] + + document = create_document(workspace, "simple_rename.py") + position = Position(0, 5) + + # Should return None when key is missing + response = pylsp_rename(config, workspace, document, position, "NewName") + assert response is None From c836c382bb5a1b6ec56e9fc4d9683e525ef00dc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Cepl?= Date: Thu, 1 Jan 2026 01:05:40 +0100 Subject: [PATCH 2/2] doc: remove mentions of `rope_rename` It has been removed from `python-lsp-server` in version 1.11.0 References: https://github.com/python-lsp/python-lsp-server/blob/develop/CHANGELOG.md?plain=1#L139 --- doc/configuration.md | 10 +--------- doc/troubleshooting.md | 6 +----- pylsp_rope/plugin.py | 1 - test/test_rename_configuration.py | 1 - 4 files changed, 2 insertions(+), 16 deletions(-) diff --git a/doc/configuration.md b/doc/configuration.md index 8d919eb..572ad45 100644 --- a/doc/configuration.md +++ b/doc/configuration.md @@ -43,9 +43,6 @@ To enable pylsp-rope's rename functionality, add the following to your LSP confi "enabled": true, "rename": true }, - "rope_rename": { - "enabled": false - }, "jedi_rename": { "enabled": false } @@ -61,7 +58,6 @@ To enable pylsp-rope's rename functionality, add the following to your LSP confi | Plugin | Identifier | Description | When to Use | |--------|------------|-------------|-------------| | **pylsp-rope** | `pylsp_rope` | This plugin's rename using Rope's refactoring engine | Complex cross-file refactoring, future module/package support | -| **Built-in Rope** | `rope_rename` | Built-in python-lsp-server Rope rename | Basic variable/class/function renaming | | **Built-in Jedi** | `jedi_rename` | Built-in python-lsp-server Jedi rename | Simple renaming scenarios | ### Verification @@ -118,9 +114,6 @@ require'lspconfig'.pylsp.setup{ enabled = true, rename = true }, - rope_rename = { - enabled = false - }, jedi_rename = { enabled = false } @@ -140,7 +133,6 @@ For VS Code with the Python LSP plugin, add to your `settings.json`: "pylsp.configurationSources": ["pycodestyle"], "pylsp.plugins.pylsp_rope.enabled": true, "pylsp.plugins.pylsp_rope.rename": true, - "pylsp.plugins.rope_rename.enabled": false, "pylsp.plugins.jedi_rename.enabled": false } ``` @@ -176,4 +168,4 @@ For Emacs with lsp-mode, add to your configuration: 1. Ensure the plugin is enabled: `"enabled": true` 2. Check that the cursor is in the correct position for the action 3. Verify the code structure supports the requested refactoring -4. Restart the language server if needed \ No newline at end of file +4. Restart the language server if needed diff --git a/doc/troubleshooting.md b/doc/troubleshooting.md index e5074b1..e11c213 100644 --- a/doc/troubleshooting.md +++ b/doc/troubleshooting.md @@ -99,9 +99,6 @@ This document provides solutions to common issues with pylsp-rope. "enabled": true, "rename": true }, - "rope_rename": { - "enabled": false - }, "jedi_rename": { "enabled": false } @@ -113,7 +110,6 @@ This document provides solutions to common issues with pylsp-rope. 2. **Check for Conflicting Plugins:** Ensure only one rename plugin is enabled: - `pylsp_rope` (this plugin) - - `rope_rename` (built-in rope) - `jedi_rename` (built-in jedi) 3. **Verify Active Plugin:** @@ -339,4 +335,4 @@ When reporting issues, include: - [GitHub Issues](https://github.com/python-rope/pylsp-rope/issues) - [Rope Documentation](https://github.com/python-rope/rope) -- [Python LSP Server](https://github.com/python-lsp/python-lsp-server) \ No newline at end of file +- [Python LSP Server](https://github.com/python-lsp/python-lsp-server) diff --git a/pylsp_rope/plugin.py b/pylsp_rope/plugin.py index 7278952..7b21168 100644 --- a/pylsp_rope/plugin.py +++ b/pylsp_rope/plugin.py @@ -41,7 +41,6 @@ def pylsp_settings(): # "pylint_lint": {"enabled": False}, # "references": {"enabled": False}, # "rope_completion": {"enabled": False}, - # "rope_rename": {"enabled": False}, "pylsp_rope": { "enabled": True, "rename": False, diff --git a/test/test_rename_configuration.py b/test/test_rename_configuration.py index 3508fee..8e8df14 100644 --- a/test/test_rename_configuration.py +++ b/test/test_rename_configuration.py @@ -63,7 +63,6 @@ def test_configuration_with_other_rename_plugins_disabled(self, config): "pylsp": { "plugins": { "pylsp_rope": {"enabled": True, "rename": True}, - "rope_rename": {"enabled": False}, "jedi_rename": {"enabled": False}, } }