-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Implement exercise markdown #797
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
Changes from all commits
8b5a71c
4224744
a7e2224
b3d96cd
8eedd50
0fb9b69
4a1c5c5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# Markdown | ||
|
||
The markdown exercise is a refactoring exercise. There is code that parses a | ||
given string with [Markdown | ||
syntax](https://guides.github.com/features/mastering-markdown/) and returns the | ||
associated HTML for that string. Even though this code is confusingly written | ||
and hard to follow, somehow it works and all the tests are passing! Your | ||
challenge is to re-write this code to make it easier to read and maintain | ||
while still making sure that all the tests keep passing. | ||
|
||
It would be helpful if you made notes of what you did in your refactoring in | ||
comments so reviewers can see that, but it isn't strictly necessary. The most | ||
important thing is to make the code better! | ||
|
||
### Submitting Exercises | ||
|
||
Note that, when trying to submit an exercise, make sure the solution is in the `exercism/python/<exerciseName>` directory. | ||
|
||
For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/python/bob/bob.py`. | ||
|
||
|
||
For more detailed information about running tests, code style and linting, | ||
please see the [help page](http://exercism.io/languages/python). | ||
|
||
## Source | ||
|
||
Syntax [https://guides.github.com/features/mastering-markdown/](https://guides.github.com/features/mastering-markdown/) | ||
|
||
## Submitting Incomplete Solutions | ||
It's possible to submit an incomplete solution so you can see how others have completed the exercise. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import re | ||
|
||
|
||
def parse_markdown(markdown): | ||
lines = markdown.split('\n') | ||
html = '' | ||
in_list = False | ||
for line in lines: | ||
res = parse_line(line, in_list) | ||
html += res['line'] | ||
in_list = res['in_list'] | ||
if in_list: | ||
html += '</ul>' | ||
return html | ||
|
||
|
||
def wrap(line, tag): | ||
return '<{tag}>{line}</{tag}>'.format(line=line, tag=tag) | ||
|
||
|
||
def check_headers(line): | ||
pattern = '# (.*)' | ||
for i in range(6): | ||
if re.match(pattern, line): | ||
return wrap(line[(i + 2):], 'h' + str(i + 1)) | ||
pattern = '#' + pattern | ||
return line | ||
|
||
|
||
def check_bold(line): | ||
bold_pattern = '(.*)__(.*)__(.*)' | ||
bold_match = re.match(bold_pattern, line) | ||
if bold_match: | ||
return bold_match.group(1) + wrap(bold_match.group(2), 'strong')\ | ||
+ bold_match.group(3) | ||
else: | ||
return None | ||
|
||
|
||
def check_italic(line): | ||
italic_pattern = '(.*)_(.*)_(.*)' | ||
italic_match = re.match(italic_pattern, line) | ||
if italic_match: | ||
return italic_match.group(1) + wrap(italic_match.group(2), 'em')\ | ||
+ italic_match.group(3) | ||
else: | ||
return None | ||
|
||
|
||
def parse_line(line, in_list): | ||
res = check_headers(line) | ||
|
||
list_match = re.match(r'\* (.*)', res) | ||
|
||
if (list_match): | ||
if not in_list: | ||
res = '<ul>' + wrap(list_match.group(1), 'li') | ||
in_list = True | ||
else: | ||
res = wrap(list_match.group(1), 'li') | ||
else: | ||
if in_list: | ||
res += '</ul>' | ||
in_list = False | ||
|
||
if not re.match('<h|<ul|<li', res): | ||
res = wrap(res, 'p') | ||
|
||
if not re.match('(<ul>)?<li>_(.*)', res): | ||
res = re.sub('(.*)(<li>)(.*)(</li>)(.*)', r'\1\2<p>\3</p>\4\5', res) | ||
|
||
while check_bold(res): | ||
res = check_bold(res) | ||
while check_italic(res): | ||
res = check_italic(res) | ||
|
||
return { | ||
'line': res, | ||
'in_list': in_list | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import re | ||
|
||
|
||
def parse_markdown(markdown): | ||
lines = markdown.split('\n') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you forgot to replace an actual solution with an exercise placeholder for the learner. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Learner should refactor this code as I mentioned before. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @paulglass yeah, I see it now, thanks |
||
res = '' | ||
in_list = False | ||
for i in lines: | ||
if re.match('###### (.*)', i) is not None: | ||
i = '<h6>' + i[7:] + '</h6>' | ||
elif re.match('## (.*)', i) is not None: | ||
i = '<h2>' + i[3:] + '</h2>' | ||
elif re.match('# (.*)', i) is not None: | ||
i = '<h1>' + i[2:] + '</h1>' | ||
m = re.match(r'\* (.*)', i) | ||
if m: | ||
if not in_list: | ||
in_list = True | ||
is_bold = False | ||
is_italic = False | ||
curr = m.group(1) | ||
m1 = re.match('(.*)__(.*)__(.*)', curr) | ||
if m1: | ||
curr = m1.group(1) + '<strong>' + \ | ||
m1.group(2) + '</strong>' + m1.group(3) | ||
is_bold = True | ||
m1 = re.match('(.*)_(.*)_(.*)', curr) | ||
if m1: | ||
curr = m1.group(1) + '<em>' + m1.group(2) + \ | ||
'</em>' + m1.group(3) | ||
is_italic = True | ||
if is_italic or is_bold: | ||
i = '<ul><li>' + curr + '</li>' | ||
else: | ||
i = '<ul><li><p>' + curr + '</p></li>' | ||
else: | ||
is_bold = False | ||
is_italic = False | ||
curr = m.group(1) | ||
m1 = re.match('(.*)__(.*)__(.*)', curr) | ||
if m1: | ||
curr = m1.group(1) + '<strong>' + \ | ||
m1.group(2) + '</strong>' + m1.group(3) | ||
is_bold = True | ||
m1 = re.match('(.*)_(.*)_(.*)', curr) | ||
if m1: | ||
curr = m1.group(1) + '<em>' + m1.group(2) + \ | ||
'</em>' + m1.group(3) | ||
is_italic = True | ||
if is_italic or is_bold: | ||
i = '<li>' + curr + '</li>' | ||
else: | ||
i = '<li><p>' + curr + '</p></li>' | ||
else: | ||
if in_list: | ||
i = '</ul>+i' | ||
in_list = False | ||
|
||
m = re.match('<h|<ul|<p|<li', i) | ||
if not m: | ||
i = '<p>' + i + '</p>' | ||
m = re.match('(.*)__(.*)__(.*)', i) | ||
if m: | ||
i = m.group(1) + '<strong>' + m.group(2) + '</strong>' + m.group(3) | ||
m = re.match('(.*)_(.*)_(.*)', i) | ||
if m: | ||
i = m.group(1) + '<em>' + m.group(2) + '</em>' + m.group(3) | ||
res += i | ||
if in_list: | ||
res += '</ul>' | ||
return res |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import unittest | ||
from markdown import parse_markdown | ||
|
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you please also leave a comment stating what version of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @paulglass, the new format is: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed |
||
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.0.0 | ||
|
||
|
||
class TestMarkdown(unittest.TestCase): | ||
|
||
def test_paragraph(self): | ||
self.assertEqual(parse_markdown('This will be a paragraph'), | ||
'<p>This will be a paragraph</p>') | ||
|
||
def test_italics(self): | ||
self.assertEqual(parse_markdown('_This will be italic_'), | ||
'<p><em>This will be italic</em></p>') | ||
|
||
def test_bold(self): | ||
self.assertEqual(parse_markdown('__This will be bold__'), | ||
'<p><strong>This will be bold</strong></p>') | ||
|
||
def test_mixed(self): | ||
self.assertEqual(parse_markdown('This will _be_ __mixed__'), | ||
'<p>This will <em>be</em> <strong>mixed</strong></p>') | ||
|
||
def test_h1(self): | ||
self.assertEqual(parse_markdown('# This will be an h1'), | ||
'<h1>This will be an h1</h1>') | ||
|
||
def test_h2(self): | ||
self.assertEqual(parse_markdown('## This will be an h2'), | ||
'<h2>This will be an h2</h2>') | ||
|
||
def test_h6(self): | ||
self.assertEqual(parse_markdown( | ||
'###### This will be an h6'), '<h6>This will be an h6</h6>') | ||
|
||
def test_unordered_lists(self): | ||
self.assertEqual(parse_markdown('* Item 1\n* Item 2'), | ||
'<ul><li><p>Item 1</p></li>' | ||
'<li><p>Item 2</p></li></ul>') | ||
|
||
def test_little_bit_of_everything(self): | ||
self.assertEqual(parse_markdown( | ||
'# Header!\n* __Bold Item__\n* _Italic Item_'), | ||
'<h1>Header!</h1><ul><li><strong>Bold Item</strong></li>' | ||
'<li><em>Italic Item</em></li></ul>') | ||
|
||
|
||
if __name__ == '__main__': | ||
unittest.main() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure that this topic is appropriate, could you please explain your reasoning?
You can use this list of general topics common among all tracks - https://github.com/exercism/problem-specifications/blob/master/TOPICS.txt
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
regular_expressions
,pattern_matching
are good candidates for exampleThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Look at this file:
"The markdown exercise is a refactoring exercise. There is code that parses a given string with Markdown syntax and returns the associated HTML for that string. Even though this code is confusingly written and hard to follow, somehow it works and all the tests are passing! Your challenge is to re-write this code to make it easier to read and maintain while still making sure that all the tests keep passing."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@m-a-ge, this exercise is intended to be a refactoring exercise rather than an implementation one (see the README). This also explains the presence of a working solution (does TravisCI currently test this solution? I feel like it needs to) in
markdown.py
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@paulglass sorry, I've missed it due to amount of opened PRs 😕
@N-Parsons thanks!
No changes needed then 😄