Skip to content

Commit

Permalink
Merge pull request #4352 from rlaverde/markdown-support
Browse files Browse the repository at this point in the history
PR: Add Markdown syntax highlighter.
  • Loading branch information
ccordoba12 authored Apr 19, 2017
2 parents 2fcb55f + d50ee6a commit bb1f3f5
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 1 deletion.
1 change: 1 addition & 0 deletions spyder/config/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
(_("Enaml files"), ('.enaml',)),
(_("Configuration files"), ('.properties', '.session', '.ini', '.inf',
'.reg', '.cfg', '.desktop')),
(_("Markdown files"), ('.md', )),
]

# Filter for all files
Expand Down
1 change: 1 addition & 0 deletions spyder/utils/sourcecode.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
'Cpp': ('c', 'cc', 'cpp', 'cxx', 'h', 'hh', 'hpp', 'hxx'),
'OpenCL': ('cl',),
'Yaml':('yaml','yml'),
"Markdown": ('md', ),
}

PYTHON_LIKE_LANGUAGES = ('Python', 'Cython', 'Enaml')
Expand Down
124 changes: 124 additions & 0 deletions spyder/utils/syntaxhighlighters.py
Original file line number Diff line number Diff line change
Expand Up @@ -910,6 +910,130 @@ class HtmlSH(BaseWebSH):
PROG = re.compile(make_html_patterns(), re.S)


# =============================================================================
# Markdown highlighter
# =============================================================================

def make_md_patterns():
h1 = '^#[^#]+'
h2 = '^##[^#]+'
h3 = '^###[^#]+'
h4 = '^####[^#]+'
h5 = '^#####[^#]+'
h6 = '^######[^#]+'

titles = any('title', [h1, h2, h3, h4, h5, h6])

html_tags = any("builtin", [r"<", r"[\?/]?>", r"(?<=<).*?(?=[ >])"])
html_symbols = '&[^; ].+;'
html_comment = '<!--.+-->'

strikethrough = any('strikethrough', [r'(~~)(.*?)~~'])
strong = any('strong', [r'(\*\*)(.*?)\*\*'])

italic = r'(__)(.*?)__'
emphasis = r'(//)(.*?)//'
italic = any('italic', [italic, emphasis])

# links - (links) after [] or links after []:
link_html = (r'(?<=(\]\())[^\(\)]*(?=\))|'
'(<https?://[^>]+>)|'
'(<[^ >]+@[^ >]+>)')
# link/image references - [] or ![]
link = r'!?\[[^\[\]]*\]'
links = any('link', [link_html, link])

# blockquotes and lists - > or - or * or 0.
blockquotes = (r'(^>+.*)'
r'|(^(?: |\t)*[0-9]+\. )'
r'|(^(?: |\t)*- )'
r'|(^(?: |\t)*\* )')
# code
code = any('code', ['^`{3,}.*$'])
inline_code = any('inline_code', ['`[^`]*`'])

# math - $$
math = any('number', [r'^(?:\${2}).*$', html_symbols])

comment = any('comment', [blockquotes, html_comment])

return '|'.join([titles, comment, html_tags, math, links, italic, strong,
strikethrough, code, inline_code])


class MarkdownSH(BaseSH):
"""Markdown Syntax Highlighter"""
# Syntax highlighting rules:
PROG = re.compile(make_md_patterns(), re.S)
NORMAL = 0
CODE = 1

def highlightBlock(self, text):
text = to_text_string(text)
previous_state = self.previousBlockState()

if previous_state == self.CODE:
self.setFormat(0, len(text), self.formats["code"])
else:
previous_state = self.NORMAL
self.setFormat(0, len(text), self.formats["normal"])

self.setCurrentBlockState(previous_state)

match = self.PROG.search(text)
while match:
for key, value in list(match.groupdict().items()):
start, end = match.span(key)

if value:
previous_state = self.previousBlockState()

if previous_state == self.CODE:
if key == "code":
# Change to normal
self.setFormat(0, len(text),
self.formats["normal"])
self.setCurrentBlockState(self.NORMAL)
else:
continue
else:
if key == "code":
# Change to code
self.setFormat(0, len(text), self.formats["code"])
self.setCurrentBlockState(self.CODE)
continue

self.setFormat(start, end - start, self.formats[key])

match = self.PROG.search(text, match.end())

self.highlight_spaces(text)

def setup_formats(self, font=None):
super(MarkdownSH, self).setup_formats(font)

font = QTextCharFormat(self.formats['normal'])
font.setFontItalic(True)
self.formats['italic'] = font

self.formats['strong'] = self.formats['definition']

font = QTextCharFormat(self.formats['normal'])
font.setFontStrikeOut(True)
self.formats['strikethrough'] = font

font = QTextCharFormat(self.formats['string'])
font.setUnderlineStyle(True)
self.formats['link'] = font

self.formats['code'] = self.formats['string']
self.formats['inline_code'] = self.formats['string']

font = QTextCharFormat(self.formats['keyword'])
font.setFontWeight(QFont.Bold)
self.formats['title'] = font


#==============================================================================
# Pygments based omni-parser
#==============================================================================
Expand Down
26 changes: 25 additions & 1 deletion spyder/utils/tests/test_syntaxhighlighters.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from qtpy.QtWidgets import QApplication
from qtpy.QtGui import QTextDocument

from spyder.utils.syntaxhighlighters import HtmlSH, PythonSH
from spyder.utils.syntaxhighlighters import HtmlSH, PythonSH, MarkdownSH

def compare_formats(actualFormats, expectedFormats, sh):
assert len(actualFormats) == len(expectedFormats)
Expand Down Expand Up @@ -46,6 +46,30 @@ def test_HtmlSH_unclosed_commend():
res = [(0, 3, 'normal')]
compare_formats(doc.firstBlock().layout().additionalFormats(), res, sh)


def test_Markdown_basic():
txt = "Some __random__ **text** with ~~different~~ [styles](link_url)"

doc = QTextDocument(txt)
sh = MarkdownSH(doc, color_scheme='Spyder')
sh.rehighlightBlock(doc.firstBlock())

res = [(0, 5, 'normal'), # |Some|
(5, 10, 'italic'), # |__random__|
(15, 1, 'normal'), # | |
(16, 8, 'strong'), # |**text**|
(24, 6, 'normal'), # |with|
(30, 13, 'italic'), # |~~diferents~~|
(43, 1, 'normal'), # | |
(44, 8, 'string'), # |[styles]|
(52, 1, 'normal'), # |(|
(53, 8, 'string'), # |(link_url)|
(61, 1, 'normal'), # ||
]

compare_formats(doc.firstBlock().layout().additionalFormats(), res, sh)


@pytest.mark.parametrize('line', ['# --- First variant',
'#------ 2nd variant',
'### 3rd variant'])
Expand Down
1 change: 1 addition & 0 deletions spyder/widgets/sourcecode/codeeditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ class CodeEditor(TextEditBaseWidget):
'Cpp': (sh.CppSH, '//', None),
'OpenCL': (sh.OpenCLSH, '//', None),
'Enaml': (sh.EnamlSH, '#', PythonCFM),
'Markdown': (sh.MarkdownSH, '#', None),
}

TAB_ALWAYS_INDENTS = ('py', 'pyw', 'python', 'c', 'cpp', 'cl', 'h')
Expand Down

0 comments on commit bb1f3f5

Please sign in to comment.