diff --git a/README.md b/README.md index 65a39c9..891bdf8 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,7 @@ pytest -x -v pyls_spyder/tests | LSP method | Spyder extensions | |:-----------------------------:|:------------------------------------------------:| | `textDocument/documentSymbol` | Find code cells `# %%` and block comments `# --` | +| `textDocument/foldingRange` | Return code cells `# %%` as code folding regions | ## Plugin configuration options This plugin can be configured by using the key `pyls_spyder` when calling `workspace/didChangeConfiguration` on the pyls. Each configuration option is described below: diff --git a/pyls_spyder/plugin.py b/pyls_spyder/plugin.py index e01c1c9..51b4532 100644 --- a/pyls_spyder/plugin.py +++ b/pyls_spyder/plugin.py @@ -79,6 +79,13 @@ def create_symbol(name: str, document: Document, } +def create_fold_region(start_line: int, end_line: int): + return { + 'startLine': start_line, + 'endLine': end_line, + } + + @hookimpl def pyls_document_symbols(config: Config, workspace: Workspace, @@ -140,3 +147,34 @@ def pyls_document_symbols(config: Config, spyder_symbols = sorted( spyder_symbols, key=lambda x: x['location']['range']['start']['line']) return spyder_symbols + + +@hookimpl +def pyls_folding_range( + config: Config, + workspace: Workspace, + document: Document) -> List[Dict]: + lines = document.lines + cell_stack = [] + cells = [] + for line_num, line in enumerate(lines): + cell_rule, cell_match = CELL_REGEX.match(line) + if cell_match is not None: + percentages = cell_match.group(1) + current_line, current_level, _ = peek_symbol(cell_stack) + if cell_rule != CELL_PERCENTAGE: + cell_level = current_level + 1 + else: + cell_level = len(percentages) - 1 + if cell_level > current_level: + cell_stack.insert(0, (line_num, cell_level, '')) + else: + while current_level >= cell_level: + cell_stack.pop(0) + cells.append(create_fold_region(current_line, line_num)) + current_line, current_level, _ = peek_symbol(cell_stack) + cell_stack.insert(0, (line_num, cell_level, '')) + for line, _, name in cell_stack: + cells.append(create_fold_region(line, line_num + 1)) + cells = sorted(cells, key=lambda x: x['startLine']) + return cells diff --git a/pyls_spyder/tests/test_plugin.py b/pyls_spyder/tests/test_plugin.py index 8a8adc1..d8cf321 100644 --- a/pyls_spyder/tests/test_plugin.py +++ b/pyls_spyder/tests/test_plugin.py @@ -17,7 +17,7 @@ from unittest.mock import MagicMock # Local imports -from pyls_spyder.plugin import pyls_document_symbols +from pyls_spyder.plugin import pyls_document_symbols, pyls_folding_range DOC_URI = uris.from_fs_path(__file__) @@ -143,3 +143,23 @@ def test_disable_block_comments(config, workspace): kind = symbol['kind'] test_results.append((name, start, end, kind)) assert expected == test_results + + +def test_cell_folding_regions(config, workspace): + document = Document(DOC_URI, workspace, DOC) + regions = pyls_folding_range(config, workspace, document) + expected = [ + (1, 23), + (6, 10), + (10, 15), + (12, 15), + (15, 23), + (20, 23), + (23, 25) + ] + test_results = [] + for region in regions: + start = region['startLine'] + end = region['endLine'] + test_results.append((start, end)) + assert expected == test_results diff --git a/setup.py b/setup.py index 9db690e..7b4a839 100644 --- a/setup.py +++ b/setup.py @@ -38,7 +38,7 @@ def get_description(): return data -REQUIREMENTS = ['python-language-server'] +REQUIREMENTS = ['python-language-server >= 0.36.2'] setup( name='pyls-spyder',