Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR: Add additional cases for code cells and block comments #10

Merged
merged 5 commits into from
Nov 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/linux-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
- name: Checkout branch/PR
uses: actions/checkout@v1
- name: Install Conda
uses: goanpeca/setup-miniconda@v1
uses: conda-incubator/setup-miniconda@v2
with:
activate-environment: test
auto-update-conda: true
Expand Down
39 changes: 33 additions & 6 deletions pyls_spyder/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,35 @@
from pyls.config.config import Config
from pyls.workspace import Workspace, Document

CELL_REGEX = re.compile(r'^[\t ]*# (%%+)(.*)?$')
BLOCK_REGEX = re.compile(r'^[\t ]*# (--+)(.*)?$')
# Local imports
from pyls_spyder.utils import RegexEvaluator

# Code cell regular expressions
# 1. Cells declared with percentages, i.e., # %% Cell
CELL_PERCENTAGE, CELL_PERCENTAGE_REGEX = (
'CELL_PERCENTAGE', re.compile(r'^[\t ]*# ?(%%+)(.*)?$'))
# 2. Cells declared with "<codecell>", i.e., # <codecell>
CELL_CODECELL, CELL_CODECELL_REGEX = (
'CELL_CODECELL', re.compile(r'^[\t ]*# ?(<codecell>)(.*)?$'))
# 3. Cells declared with "In[.*], i.e., # In[23]"
CELL_IN, CELL_IN_REGEX = (
'CELL_IN', re.compile(r'^[\t ]*# ?(In\[)([^\]\r\n]*)?\]?$'))

CELL_REGEX = RegexEvaluator({
CELL_PERCENTAGE: CELL_PERCENTAGE_REGEX,
CELL_CODECELL: CELL_CODECELL_REGEX,
CELL_IN: CELL_IN_REGEX
})

# Block comment regular expressions
# 1. Block comments declared with 4 dashes, i.e., # ---- Block comment
BLOCK_DASH = (
'BLOCK_DASH', re.compile(r'^[\t ]*# ?-{4}([^-\n\r]?.*)?$'))
# 2. Block comments declared with 3 consecutive hashes, i.e., #### Comment
BLOCK_HASH = (
'BLOCK_HASH', re.compile(r'^[\t ]*##{3}([^\#\n\r]?.*)?$'))

BLOCK_REGEX = RegexEvaluator(dict([BLOCK_DASH, BLOCK_HASH]))


def peek_symbol(list: List) -> Tuple:
Expand Down Expand Up @@ -70,8 +97,8 @@ def pyls_document_symbols(config: Config,
unnamed_block = 1

for line_num, line in enumerate(lines):
cell_match = CELL_REGEX.match(line)
block_match = BLOCK_REGEX.match(line)
cell_rule, cell_match = CELL_REGEX.match(line)
block_rule, block_match = BLOCK_REGEX.match(line)

if cell_match is not None:
percentages = cell_match.group(1)
Expand All @@ -81,7 +108,7 @@ def pyls_document_symbols(config: Config,
cell_name = 'Unnamed cell {0}'.format(unnamed_cell)
unnamed_cell += 1

if not group_cells:
if not group_cells or cell_rule != CELL_PERCENTAGE:
andfoy marked this conversation as resolved.
Show resolved Hide resolved
cells.append(create_symbol(
cell_name, document, line_num, line_num + 1))
else:
Expand All @@ -99,7 +126,7 @@ def pyls_document_symbols(config: Config,
current_name) = peek_symbol(cell_stack)
cell_stack.insert(0, (line_num, cell_level, cell_name))
elif block_match is not None and enable_block_comments:
block_name = block_match.group(2).strip()
block_name = block_match.group(1).strip()
if block_name == '':
block_name = 'Unnamed comment {0}'.format(unnamed_block)
unnamed_block += 1
Expand Down
68 changes: 40 additions & 28 deletions pyls_spyder/tests/test_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@

"""pyls-spyder plugin tests."""

# Standard library imports
import os

# PyLS imports
from pyls import uris
from pyls.workspace import Document
Expand All @@ -26,24 +23,29 @@
DOC_URI = uris.from_fs_path(__file__)
DOC = """
# %%
# -- Imports
# ---- Imports
import os
import sys

# ------
# <codecell> Other cell
# ----
def a():
#### Block comment on a
# %%% Cell inside a
for i in range(0, 10):
# %%%% Cell
pass

# %%%
def b():
# ----- Pass inside b
#---- Pass inside b
pass

# %% Empty cell
# --
# In[25]
####

#%% Empty cell
#----
"""


Expand All @@ -61,15 +63,19 @@ def test_cell_block_symbols(config, workspace):
document = Document(DOC_URI, workspace, DOC)
symbols = pyls_document_symbols(config, workspace, document)
expected = [
('Unnamed cell 1', 1, 17, 225),
('Unnamed cell 1', 1, 22, 225),
('Imports', 2, 2, 224),
('Unnamed comment 1', 6, 6, 224),
('Cell inside a', 8, 12, 225),
('Cell', 10, 12, 225),
('Unnamed cell 2', 13, 17, 225),
('Pass inside b', 15, 15, 224),
('Empty cell', 18, 19, 225),
('Unnamed comment 2', 19, 19, 224)
('Other cell', 6, 6, 225),
('Unnamed comment 1', 7, 7, 224),
('Block comment on a', 9, 9, 224),
('Cell inside a', 10, 14, 225),
('Cell', 12, 14, 225),
('Unnamed cell 2', 15, 22, 225),
('Pass inside b', 17, 17, 224),
('25', 20, 20, 225),
('Unnamed comment 2', 21, 21, 224),
('Empty cell', 23, 24, 225),
('Unnamed comment 3', 24, 24, 224)
]
test_results = []
for symbol in symbols:
Expand All @@ -90,13 +96,17 @@ def test_ungroup_cell_symbols(config, workspace):
expected = [
('Unnamed cell 1', 1, 1, 225),
('Imports', 2, 2, 224),
('Unnamed comment 1', 6, 6, 224),
('Cell inside a', 8, 8, 225),
('Cell', 10, 10, 225),
('Unnamed cell 2', 13, 13, 225),
('Pass inside b', 15, 15, 224),
('Empty cell', 18, 18, 225),
('Unnamed comment 2', 19, 19, 224)
('Other cell', 6, 6, 225),
('Unnamed comment 1', 7, 7, 224),
('Block comment on a', 9, 9, 224),
('Cell inside a', 10, 10, 225),
('Cell', 12, 12, 225),
('Unnamed cell 2', 15, 15, 225),
('Pass inside b', 17, 17, 224),
('25', 20, 20, 225),
('Unnamed comment 2', 21, 21, 224),
('Empty cell', 23, 23, 225),
('Unnamed comment 3', 24, 24, 224)
]
test_results = []
for symbol in symbols:
Expand All @@ -115,11 +125,13 @@ def test_disable_block_comments(config, workspace):
config.plugin_settings = lambda _: {'enable_block_comments': False}
symbols = pyls_document_symbols(config, workspace, document)
expected = [
('Unnamed cell 1', 1, 17, 225),
('Cell inside a', 8, 12, 225),
('Cell', 10, 12, 225),
('Unnamed cell 2', 13, 17, 225),
('Empty cell', 18, 19, 225)
('Unnamed cell 1', 1, 22, 225),
('Other cell', 6, 6, 225),
('Cell inside a', 10, 14, 225),
('Cell', 12, 14, 225),
('Unnamed cell 2', 15, 22, 225),
('25', 20, 20, 225),
('Empty cell', 23, 24, 225)
]
test_results = []
for symbol in symbols:
Expand Down
47 changes: 47 additions & 0 deletions pyls_spyder/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# -*- coding: utf-8 -*-
# ----------------------------------------------------------------------------
# Copyright (c) Spyder Project Contributors
#
# Licensed under the terms of the MIT License
# (see LICENSE.txt for details)
# ----------------------------------------------------------------------------

"""pyls-spyder misc utillites."""

# Standard library imports
from typing import Tuple, Dict


class RegexEvaluator:
andfoy marked this conversation as resolved.
Show resolved Hide resolved
"""Wrapper class around multiple regular expressions."""

def __init__(self, regex_map: Dict):
self.regexes = regex_map

def match(self, string: str) -> Tuple:
"""
Match a string `string` against a set of regular expressions.

The regular expressions are applied in a short-circuit fashion.

Parameters
----------
string: str
Input string to match regexes against.

Returns
-------
output: Tuple[Optional[str], Optional[re.Match]]
A tuple containing the regex identifier that first matched the
input, alongside the corresponding regex match object. If no regex
did matched the input, then a tuple containing `None` is returned.
"""
re_match = None
re_rule = None
for regex_name in self.regexes:
regex = self.regexes[regex_name]
re_match = regex.match(string)
if re_match is not None:
re_rule = regex_name
break
return re_rule, re_match