Ticket #10637: trac_10637_answer_to_kcrisman.patch

File trac_10637_answer_to_kcrisman.patch, 11.3 KB (added by pang, 8 years ago)

minor updates suggested by kcrisman

  • sagenb/misc/comments2rst.py

    # HG changeset patch
    # User Pablo Angulo Ardoy (pang) <pablo.angulo@uam.es>
    # Date 1349798120 -7200
    # Node ID ef9db668ad1d9c5a56e5f201fbcef01c4abb94bc
    # Parent  d97775b5afedd24b48054aaf8750206ea041a2bf
    [mq]: sws2rst_answer_to_kcrisman
    
    diff --git a/sagenb/misc/comments2rst.py b/sagenb/misc/comments2rst.py
    a b  
    3333in the Sage shell (sage --sh).
    3434"""
    3535
     36#negative lookbehind: http://www.regular-expressions.info/lookaround.html
     37double_dollar = re.compile(r'(?<!\\)\$\$')
    3638def preprocess_display_latex(text):
    3739    r"""replace $$some display latex$$ with <display>some display latex</display>
    3840    before the soup is built.
     
    5860    """
    5961    ls = []
    6062    start_tag = True
    61     partes = text.split('$$')
    62     for c in partes[:-1]:
     63    parts = double_dollar.split(text)
     64    for c in parts[:-1]:
    6365        if start_tag:
    6466            ls.append(c)
    6567            ls.append('<display>')
     
    7476            elif abs(count)>1:
    7577                raise Exception, 'display latex was messed up with html code'
    7678        start_tag = not start_tag
    77     ls.append(partes[-1])
     79    ls.append(parts[-1])
    7880    return ''.join(ls)
    7981
    8082def prune_tags(text):
     
    9092        text = text.replace(c,r)
    9193    return text
    9294
     95#This is supposed to be handled by BeautifulSoup, but doesn't work
     96xml_entities = {'&lt;':'<',
     97            '&gt;':'>',
     98            '&amp;':'&',
     99            '&quot;':'"',
     100            '&apos;':"'",
     101}
     102def replace_xml_entities(text):
     103    for c,r in xml_entities.iteritems():
     104        text = text.replace(c,r)
     105    return text
     106 
     107
    93108def replace_courier(soup):
    94109    """Lacking a better option, I use courier font to mark <code>
    95110    within tinyMCE. And I want to turn that into real code tags.
    96111
    97     Most users won't be needing this(?)
     112    Most users won't be needing this(?), so this code is not called anywhere
     113    but kept for reference
    98114    """
    99115    for t in soup.findAll(lambda s:s.has_key('style') and 'courier' in s['style']):
    100116        tag = Tag(soup, 'code')
     
    102118            tag.append(t.contents[0])
    103119        t.replaceWith(tag)
    104120
    105 #inline_latex is careful not to confuse escaped dollars
    106 inline_latex = re.compile(r'([^\\])\$(.*?)([^\\])\$')
    107 latex_beginning = re.compile(r'\$(.*?)([^\\])\$')
     121#negative lookbehind: http://www.regular-expressions.info/lookaround.html
     122single_dollar = re.compile(r'(?<!\\)\$')
    108123def replace_latex(soup):
    109124    r"""Replaces inline latex by :math:`code` and escapes
    110125    some rst special chars like +, -, * and | outside of inline latex
     
    125140        <p><strong>2\+2 \| 1\+3</strong></p>
    126141    """
    127142    for t in soup.findAll(text=re.compile('.+')):
    128         if latex_beginning.match(t):
    129             t.replaceWith(inline_latex.sub('\\1:math:`\\2\\3`',
    130                                            latex_beginning.sub(':math:`\\1\\2`',
    131                                                                unicode(t),
    132                                                                1)))       
    133         elif inline_latex.search(t):
    134             t.replaceWith(inline_latex.sub('\\1:math:`\\2\\3`',
    135                                            unicode(t)))
    136         elif not (t.fetchParents(name = 'display')
    137                   or t.fetchParents(name = 'pre')):
    138             t.replaceWith(escape_chars(t))
     143        if (t.fetchParents(name = 'display') or
     144            t.fetchParents(name = 'pre')        ):
     145            continue
     146        parts = single_dollar.split(unicode(t))
     147        even  = [escape_chars(parts[i]) for i in range(0,len(parts),2)]
     148        odd   = [' :math:`%s`'%parts[i] for i in range(1,len(parts),2)]
     149        odd.append('')
     150        t.replaceWith(''.join(''.join(p) for p in zip(even,odd) ))
    139151
    140152class Soup2Rst(object):
    141153    """builds the rst text from the Soup Tree
     
    144156            'h2':'header',
    145157            'h3':'header',
    146158            'h4':'header',
    147             'p': 'inline_no_tag',
     159            'p': 'p',
    148160            '[document]': 'document',
    149161            'br': 'br',
    150162            'b':'strong',
     
    170182
    171183    headers = {'h1':u'=',
    172184               'h2':u'-',
    173                'h3':u'~',
     185               'h3':u'^',
    174186               'h4':u'"',
     187               'h5':u'~',
    175188               }
    176189   
    177190    def __init__(self, images_dir):
    178191        self.images_dir = images_dir
    179         self._nested_list = 0
    180         self._inside_ol   = False
     192        self._nested_list = -1
     193        self._inside_ol_or_ul = []
    181194        self._inside_code_tag = False
    182195
    183196    def visit(self, node):
     
    206219        return t.replace('\n','')
    207220       
    208221    def visit_header(self, node):
    209         s = ' '.join(self.visit(tag) for tag in node.contents)
     222        s = ''.join(self.visit(tag) for tag in node.contents)
    210223        spacer = self.headers[node.name]*len(s)
    211224        return s.replace( '\n', '') +  '\n' + spacer
    212225
     
    215228
    216229    def visit_ul(self, node):
    217230        self._nested_list += 1
    218         result = '\n'.join(self.visit(tag) for tag in node.contents)
     231        self._inside_ol_or_ul.append(False)
     232        result = '\n\n'+''.join(self.visit(tag) for tag in node.contents)+'\n'
     233        self._inside_ol_or_ul.pop()
    219234        self._nested_list -= 1
    220235        return result
    221236
    222237    def visit_ol(self, node):
    223238        self._nested_list += 1
    224         self._inside_ol = True
    225         result = '\n'.join(self.visit(tag) for tag in node.contents)
     239        self._inside_ol_or_ul.append(True)
     240        result = '\n\n'+''.join(self.visit(tag) for tag in node.contents)+'\n'
     241        self._inside_ol_or_ul.pop()
    226242        self._nested_list -= 1
    227         self._inside_ol = False
    228243        return result
    229244
    230245    def visit_li(self, node):
    231246        return (' '*self._nested_list
    232                 + ('#. ' if self._inside_ol else '- ')
    233                 +' '.join(self.visit(tag) for tag in node.contents))
     247                + ('#. ' if self._inside_ol_or_ul[-1] else '- ')
     248                +' '.join(self.visit(tag) for tag in node.contents)
     249                + '\n')
    234250
    235251    def visit_display(self, node):
    236         return ('\n.. MATH::\n\n    ' +
     252        return ('\n\n.. MATH::\n\n    ' +
    237253                unicode(node)[9:-10].replace('<br></br>','\n').replace('\n','\n    ') +
    238                 '\n\n')
     254                '\n\n.. end of math\n\n')
    239255
    240256    def visit_img(self, node):
    241257        return '.. image:: ' + os.path.join(self.images_dir, node['src'].replace(' ','_')) + '\n    :align: center\n'
     
    251267                            if hasattr(row,'name') and
    252268                            row.name=='tr')
    253269                rows.append([]) #this row represents a separator
    254             elif elt.name == 'tbody':
     270            elif (elt.name == 'tbody') or (elt.name == 'tfoot'):
    255271                rows.extend(self.prepare_tr(row)
    256272                            for row in elt
    257273                            if hasattr(row,'name') and
     
    285301    def visit_strong(self, node):
    286302        if node.contents:
    287303            content = ' '.join(self.visit(tag) for tag in node.contents).strip()
    288             if '``' in content or self._inside_code_tag:
     304            if '``' in content:
    289305                return content
    290306            else:
    291307                return '**' + content + '**'
     
    294310
    295311    def visit_em(self,node):
    296312        if node.contents:
    297             return '*' + ' '.join(self.visit(tag) for tag in node.contents).strip() + '*'
     313            return ' *' + ' '.join(self.visit(tag) for tag in node.contents).strip() + '* '
    298314        else:
    299315            return ''
    300316
    301317    def visit_code(self, node):
    302318        if node.contents:
    303             self._inside_code_tag = True
    304319            content = self.get_plain_text(node).strip()
    305             self._inside_code_tag = False
    306320            return '``' + content + '``'
    307321        else:
    308322            return ''
    309323
    310324    def visit_inline_no_tag(self, node):
    311325        return (' '.join(self.visit(tag)
    312                          for tag in node.contents)).strip() + '\n'
     326                         for tag in node.contents)).strip()
    313327
    314328    def visit_block_no_tag(self, node):
    315         return '\n'.join(self.visit(tag) for tag in node.contents)
     329        return '\n'.join(self.visit(tag) for tag in node.contents) + '\n'
     330
     331    def visit_p(self, node):
     332        return ''.join(self.visit(tag) for tag in node.contents) + '\n\n'
    316333
    317334    def visit_a(self, node):
    318         return ('`' + ' '.join(self.visit(tag) for tag in node.contents) +
    319                 ' <' + node['href'] + '>`_'
    320                 )
     335        c = ' '.join(self.visit(tag) for tag in node.contents)
     336        try:
     337            link = node['href']
     338            if link[0]=='#':
     339                return ':ref:`%s <%s>`'%(c, link[1:])
     340            else:                   
     341                return '`%s <%s>`_'%(c, link)
     342        except KeyError:
     343            return '.. _%s:\n\n'%node['name']
     344
    321345
    322346def html2rst(text, images_dir):
    323347    """Converts html, tipically generated by tinyMCE, into rst
     
    364388    #ICantBelieveItsBeautifulSoup is better than BeautifulSoup
    365389    #for html that wasn't generated by humans (like tinyMCE)
    366390    soup = ICantBelieveItsBeautifulSoup(text,
    367                        convertEntities=ICantBelieveItsBeautifulSoup.HTML_ENTITIES)   
     391                       convertEntities=ICantBelieveItsBeautifulSoup.ALL_ENTITIES)       
    368392
    369393    #remove all comments
    370394    comments = soup.findAll(text=lambda text:isinstance(text, Comment))
    371395    for comment in comments:
    372396        comment.extract()
    373397
    374     replace_courier(soup)
     398#    replace_courier(soup)
    375399    replace_latex(soup)
    376400    v = Soup2Rst(images_dir)
    377     return v.visit(soup)
     401
     402#    return v.visit(soup)
     403    text = v.visit(soup)
     404    more_than_2_blank_lines = re.compile(r'\n\n+', re.MULTILINE)
     405    text = more_than_2_blank_lines.sub('\n\n', text)
     406    text = replace_xml_entities(text)
     407    return text
     408   
  • sagenb/misc/worksheet2rst.py

    diff --git a/sagenb/misc/worksheet2rst.py b/sagenb/misc/worksheet2rst.py
    a b  
    116116        lines.append(prefix + l)
    117117    return '\n'.join(lines)
    118118
     119HEADER_RE = re.compile(r'<h\d>')
     120def add_title_if_there_is_none(text):
     121    if not HEADER_RE.search(text):
     122        return '<h1>Please write a title for this worksheet!</h1>\n' + text
     123    else:
     124        return text
     125
    119126def worksheet2rst(s, images_dir=''):
    120127    """Parses a string, tipically the content of the file
    121128    worksheet.html inside a sws file, and converts it into
     
    142149    : worksheet2rst(s)
    143150    u'.. -*- coding: utf-8 -*-\n\n\n::\n\n    sage: show(f)\n\n.. MATH::\n\n    \\sqrt{x}\n\n.. end of output\n'       
    144151    """
    145     result_parser = results2rst
     152    s = add_title_if_there_is_none(s)
    146153    state = States.COMMENT
    147154    result = ['.. -*- coding: utf-8 -*-\n']
    148155    ls = []
     
    157164                result.append(html2rst(u'\n'.join(ls), img_path))
    158165            elif state == States.RESULT:
    159166                img_path = os.path.join(images_dir, 'cell_%s_'%last_cell_id)
    160                 result.append(result_parser(u'\n'.join(ls),
     167                result.append(results2rst(u'\n'.join(ls),
    161168                                             img_path))
    162169                result.append('')
    163170                result.append('.. end of output')
     
    191198        fichero.close()
    192199    else:
    193200        text = sys.stdin.read()
     201    images_dir = sys.argv[2] if len(sys.argv)>2 else ''
    194202
    195     print worksheet2rst(text).encode('utf-8')
     203    print worksheet2rst(text, images_dir).encode('utf-8')
    196204