leoRst.py :  » Development » Leo » Leo-4.7.1-final » leo » core » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Development » Leo 
Leo » Leo 4.7.1 final » leo » core » leoRst.py
#@+leo-ver=4-thin
#@+node:ekr.20090502071837.3:@thin leoRst.py
#@<< docstring >>
#@+node:ekr.20090502071837.4:<< docstring >>
'''Support for restructured text (rST), adapted from rst3plugin. import 

For full documentation, see:
http://webpages.charter.net/edreamleo/rstplugin3.html

To generate documents from rSTfilesPythonsdocutils_modulemustbe import 
installed. The code will use the SilverCity_ syntax coloring package if is is
available.'''
#@nonl
#@-node:ekr.20090502071837.4:<< docstring >>
#@nl

if 0:
    bwm_file = open("bwm_file", "w")

#@<< imports >>
#@+node:ekr.20090502071837.5:<< imports >>
import leo.core.leoGlobals as g
import leo.core.leoTest as leoTest
import leo.core.leoCommands as commands

import os
import pprint

if g.isPython3:
    import io
    StringIO = io.StringIO
else:
    import StringIO
    StringIO = StringIO.StringIO

import sys

try:
    import leo.plugins.mod_http as mod_http
except ImportError:
    mod_http = None
except Exception:
    # Don't let a problem with a plugin crash Leo's core!
    g.es_print('leoRst: can not import leo.plugins.mod_http')
    g.es_exception()
    mod_http = None

try:
    import docutils
    import docutils.parsers.rst
    import docutils.core
    import docutils.io
except ImportError:
    docutils = None

try:
    import SilverCity
except ImportError:
    SilverCity = None
#@-node:ekr.20090502071837.5:<< imports >>
#@nl

#@+others
#@+node:ekr.20090502071837.12:code_block
def code_block (name,arguments,options,
    content,lineno,content_offset,block_text,state,state_machine):

    '''Implement the code-block directive for docutils.'''

    try:
        language = arguments [0]
        # See http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/252170
        module = SilverCity and getattr(SilverCity,language)
        generator = module and getattr(module,language+"HTMLGenerator")
        if generator:
            io = StringIO()
            generator().generate_html(io,'\n'.join(content))
            html = '<div class="code-block">\n%s\n</div>\n' % io.getvalue()
        else:
            html = '<div class="code-block">\n%s\n</div>\n' % '<br>\n'.join(content)
        raw = docutils.nodes.raw('',html,format='html')
        return [raw]
    except Exception: # Return html as shown.  Lines are separated by <br> elements.
        g.es_trace('exception in rst3:code_block()')
        g.es_exception()
        return [None]

# See http://docutils.sourceforge.net/spec/howto/rst-directives.html
code_block.arguments = (
    1, # Number of required arguments.
    0, # Number of optional arguments.
    0) # True if final argument may contain whitespace.

# A mapping from option name to conversion function.
if docutils:
    code_block.options = {
        'language':
        docutils.parsers.rst.directives.unchanged # Return the text argument, unchanged.
    }
    code_block.content = 1 # True if content is allowed.

    # Register the directive with docutils.
    docutils.parsers.rst.directives.register_directive('code-block',code_block)
else:
    code_block.options = {}
#@nonl
#@-node:ekr.20090502071837.12:code_block
#@+node:ekr.20090502071837.33:class rstCommands
#@+at
# This plugin optionally stores information for the http plugin. 
# Each node can
# have one additional attribute, with the name 
# rst_http_attributename, which is a
# list. The first three elements are stack of tags, the rest is html 
# code::
# 
#     [<tag n start>, <tag n end>, <other stack elements>, <html 
# line 1>, <html line 2>, ...]
# 
# <other stack elements has the same structure::
# 
#     [<tag n-1 start>, <tag n-1 end>, <other stack elements>]
#@-at
#@@c

class rstCommands:

    '''A class to write rst markup in Leo outlines.'''

    #@    @+others
    #@+node:ekr.20090502071837.34: Birth & init
    #@+node:ekr.20090502071837.35: ctor (rstClass)
    def __init__ (self,c):

        global SilverCity

        self.c = c
        #@    << init ivars >>
        #@+node:ekr.20090502071837.36:<< init ivars >> (leoRst)
        self.silverCityWarningGiven = False

        # The options dictionary.
        self.optionsDict = {}
        self.option_prefix = '@rst-option'

        # Formatting...
        self.code_block_string = ''
        self.node_counter = 0
        self.topLevel = 0
        self.topNode = None
        self.use_alternate_code_block = SilverCity is None

        # Http support...
        self.nodeNumber = 0
        # All nodes are numbered so that unique anchors can be generated.

        self.http_map = {} 
        # Keys are named hyperlink targets.  Value are positions.
        # The targets mark the beginning of the html code specific
        # for this position.

        self.anchor_map = {}
        # Maps anchors (generated by this module) to positions

        self.rst3_all = False
        # Set to True by the button which processes all @rst trees.

        # For writing.
        self.atAutoWrite = False # True, special cases for writeAtAutoFile.
        self.atAutoWriteUnderlines = '' # Forced underlines for writeAtAutoFile.
        self.leoDirectivesList = g.globalDirectiveList
        self.encoding = 'utf-8'
        self.ext = None # The file extension.
        self.outputFileName = None # The name of the file being written.
        self.outputFile = None # The open file being written.
        self.path = '' # The path from any @path directive.
        self.source = None # The written source as a string.
        self.trialWrite = False # True if doing a trialWrite.
        #@nonl
        #@-node:ekr.20090502071837.36:<< init ivars >> (leoRst)
        #@nl
        self.createDefaultOptionsDict()
        self.initOptionsFromSettings() # Still needed.
        self.initHeadlineCommands() # Only needs to be done once.
        self.initSingleNodeOptions()
    #@-node:ekr.20090502071837.35: ctor (rstClass)
    #@+node:ekr.20090502071837.102: getPublicCommands
    def getPublicCommands (self):        

        c = self.c

        return {
            'rst3': self.rst3, # Formerly write-restructured-text.
        }
    #@-node:ekr.20090502071837.102: getPublicCommands
    #@+node:ekr.20090511055302.5792:finishCreate
    def finishCreate(self):

        c = self.c
        d = self.getPublicCommands()
        c.commandsDict.update(d)
    #@-node:ekr.20090511055302.5792:finishCreate
    #@+node:ekr.20090502071837.38:initHeadlineCommands
    def initHeadlineCommands (self):

        '''Init the list of headline commands used by writeHeadline.'''

        self.headlineCommands = [
            self.getOption('code_prefix'),
            self.getOption('doc_only_prefix'),
            self.getOption('default_path_prefix'),
            self.getOption('rst_prefix'),
            self.getOption('ignore_headline_prefix'),
            self.getOption('ignore_headlines_prefix'),
            self.getOption('ignore_node_prefix'),
            self.getOption('ignore_tree_prefix'),
            self.getOption('option_prefix'),
            self.getOption('options_prefix'),
            self.getOption('show_headline_prefix'),
            # # Suggested by Hemanth P.S.: prevent @file nodes from creating headings.
            # self.getOption('keep_at_file_prefix'),
            # self.getOption('strip_at_file_prefix'),
        ]
    #@nonl
    #@-node:ekr.20090502071837.38:initHeadlineCommands
    #@+node:ekr.20090502071837.39:initSingleNodeOptions
    def initSingleNodeOptions (self):

        self.singleNodeOptions = [
            'ignore_this_headline',
            'ignore_this_node',
            'ignore_this_tree',
            'preformat_this_node',
            'show_this_headline',
        ]
    #@nonl
    #@-node:ekr.20090502071837.39:initSingleNodeOptions
    #@+node:ekr.20090502071837.40:munge
    def munge (self,name):

        '''Convert an option name to the equivalent ivar name.'''

        i = g.choose(name.startswith('rst'),3,0)

        while i < len(name) and name[i].isdigit():
            i += 1

        if i < len(name) and name[i] == '_':
            i += 1

        s = name[i:].lower()
        s = s.replace('-','_')

        return s
    #@nonl
    #@-node:ekr.20090502071837.40:munge
    #@-node:ekr.20090502071837.34: Birth & init
    #@+node:ekr.20090511055302.5793:rst3 command
    def rst3 (self,event=None):

        '''Write all @rst nodes.'''

        # This used to be the called the write-restructured-text command.

        self.processTopTree(self.c.p)
    #@-node:ekr.20090511055302.5793:rst3 command
    #@+node:ekr.20090502071837.41:options...
    #@+node:ekr.20090502071837.42:createDefaultOptionsDict
    def createDefaultOptionsDict(self):

        # Warning: changing the names of options changes the names of the corresponding ivars.

        self.defaultOptionsDict = {
            # Http options...
            'rst3_clear_http_attributes':   False,
            'rst3_http_server_support':     False,
            'rst3_http_attributename':      'rst_http_attribute',
            'rst3_node_begin_marker':       'http-node-marker-',
            # Path options...
            'rst3_default_path': None, # New in Leo 4.4a4 # Bug fix: must be None, not ''.
            'rst3_stylesheet_name': 'default.css',
            'rst3_stylesheet_path': None, # Bug fix: must be None, not ''.
          'rst3_stylesheet_embed': True,
            'rst3_publish_argv_for_missing_stylesheets': None,
            # Global options...
            'rst3_code_block_string': '',
            'rst3_number_code_lines': True,
            'rst3_underline_characters': '''#=+*^~"'`-:><_''',
            'rst3_verbose':True,
            'rst3_write_intermediate_file': False, # Used only if generate_rst is True.
            # Mode options...
            'rst3_code_mode': False, # True: generate rst markup from @code and @doc parts.
            'rst3_doc_only_mode': False, # True: generate only from @doc parts.
            'rst3_generate_rst': True, # True: generate rst markup.  False: generate plain text.
            'rst3_generate_rst_header_comment': True,
                # True generate header comment (requires generate_rst option)
            # Formatting options that apply to both code and rst modes....
            'rst3_show_headlines': True,  # Can be set by @rst-no-head headlines.
            'rst3_show_organizer_nodes': True,
            'rst3_show_options_nodes': False,
            'rst3_show_sections': True,
            'rst3_strip_at_file_prefixes': True,
            'rst3_show_doc_parts_in_rst_mode': True,
            # Formatting options that apply only to code mode.
            'rst3_show_doc_parts_as_paragraphs': False,
            'rst3_show_leo_directives': True,
            'rst3_show_markup_doc_parts': False,
            'rst3_show_options_doc_parts': False,
            # *Names* of headline commands...
            'rst3_code_prefix':             '@rst-code',     # Enter code mode.
            'rst3_doc_only_prefix':         '@rst-doc-only', # Enter doc-only mode.
            'rst3_rst_prefix':              '@rst',          # Enter rst mode.
            'rst3_ignore_headline_prefix':  '@rst-no-head',
            'rst3_ignore_headlines_prefix': '@rst-no-headlines',
            'rst3_ignore_node_prefix':      '@rst-ignore-node',
            'rst3_ignore_prefix':           '@rst-ignore',
            'rst3_ignore_tree_prefix':      '@rst-ignore-tree',
            'rst3_option_prefix':           '@rst-option',
            'rst3_options_prefix':          '@rst-options',
            'rst3_preformat_prefix':        '@rst-preformat',
            'rst3_show_headline_prefix':    '@rst-head',
        }
    #@nonl
    #@-node:ekr.20090502071837.42:createDefaultOptionsDict
    #@+node:ekr.20090502071837.43:dumpSettings (debugging)
    def dumpSettings (self):

        d = self.optionsDict
        keys = sorted(d)

        g.pr('present settings...')
        for key in keys:
            g.pr('%20s %s' % (key,d.get(key)))
    #@nonl
    #@-node:ekr.20090502071837.43:dumpSettings (debugging)
    #@+node:ekr.20090502071837.44:getOption
    def getOption (self,name):

        bwm = False
        if bwm:
            g.trace("bwm: getOption self:%s, name:%s, value:%s" % (
                self, name, self.optionsDict.get(name)))

        return self.optionsDict.get(name)
    #@nonl
    #@-node:ekr.20090502071837.44:getOption
    #@+node:ekr.20090502071837.45:initCodeBlockString
    def initCodeBlockString(self,p):

        # New in Leo 4.4.4: do this here, not in initWrite.
        trace = False and not g.unitTesting
        c = self.c
        # if trace: os.system('cls')
        d = c.scanAllDirectives(p)
        language = d.get('language')
        if language is None: language = 'python'
        else: language = language.lower()
        syntax = SilverCity is not None

        if trace: g.trace('language',language,'language.title()',language.title(),p.h)

        # Note: lines that end with '\n\n' are a signal to handleCodeMode.
        s = self.getOption('code_block_string')
        if s:
            self.code_block_string = s.replace('\\n','\n')
        elif syntax and language in ('python','ruby','perl','c'):
            self.code_block_string = '**code**:\n\n.. code-block:: %s\n\n' % (
                language.title())
        else:
            self.code_block_string = '**code**:\n\n.. class:: code\n..\n\n::\n\n'
    #@nonl
    #@-node:ekr.20090502071837.45:initCodeBlockString
    #@+node:ekr.20090502071837.46:preprocessTree & helpers
    def preprocessTree (self,root):

        self.tnodeOptionDict = {}

        # Bug fix 12/4/05: must preprocess parents too.
        for p in root.parents():
            self.preprocessNode(p)

        for p in root.self_and_subtree():
            self.preprocessNode(p)

        if 0:
            g.trace(root.h)
            for key in self.tnodeOptionDict.keys():
                g.trace(key)
                g.printDict(self.tnodeOptionDict.get(key))
    #@nonl
    #@+node:ekr.20090502071837.47:preprocessNode
    def preprocessNode (self,p):

        d = self.tnodeOptionDict.get(p.v)
        if d is None:
            d = self.scanNodeForOptions(p)
            self.tnodeOptionDict [p.v] = d
    #@nonl
    #@-node:ekr.20090502071837.47:preprocessNode
    #@+node:ekr.20090502071837.48:parseOptionLine
    def parseOptionLine (self,s):

        '''Parse a line containing name=val and return (name,value) or None.

        If no value is found, default to True.'''

        s = s.strip()
        if s.endswith(','): s = s[:-1]
        # Get name.  Names may contain '-' and '_'.
        i = g.skip_id(s,0,chars='-_')
        name = s [:i]
        if not name: return None
        j = g.skip_ws(s,i)
        if g.match(s,j,'='):
            val = s [j+1:].strip()
            # g.trace(val)
            return name,val
        else:
            # g.trace('*True')
            return name,'True'
    #@nonl
    #@-node:ekr.20090502071837.48:parseOptionLine
    #@+node:ekr.20090502071837.49:scanForOptionDocParts
    def scanForOptionDocParts (self,p,s):

        '''Return a dictionary containing all options from rstoptionsdocpartsp. import 
        Multiple @rst-options doc parts are allowed: this code aggregates all options.
        '''

        d = {} ; n = 0 ; lines = g.splitLines(s)
        while n < len(lines):
            line = lines[n] ; n += 1
            if line.startswith('@'):
                i = g.skip_ws(line,1)
                for kind in ('@rst-options','@rst-option'):
                    if g.match_word(line,i,kind):
                        # Allow options on the same line.
                        line = line[i+len(kind):]
                        d.update(self.scanOption(p,line))
                        # Add options until the end of the doc part.
                        while n < len(lines):
                            line = lines[n] ; n += 1 ; found = False
                            for stop in ('@c','@code', '@'):
                                if g.match_word(line,0,stop):
                                    found = True ; break
                            if found:
                                break
                            else:
                                d.update(self.scanOption(p,line))
                        break
        return d
    #@nonl
    #@-node:ekr.20090502071837.49:scanForOptionDocParts
    #@+node:ekr.20090502071837.50:scanHeadlineForOptions
    def scanHeadlineForOptions (self,p):

        '''Return a dictionary containing the options implied by p's headline.'''

        h = p.h.strip()

        if p == self.topNode:
            return {} # Don't mess with the root node.
        elif g.match_word(h,0,self.getOption('option_prefix')): # '@rst-option'
            s = h [len(self.option_prefix):]
            return self.scanOption(p,s)
        elif g.match_word(h,0,self.getOption('options_prefix')): # '@rst-options'
            return self.scanOptions(p,p.b)
        else:
            # Careful: can't use g.match_word because options may have '-' chars.
            i = g.skip_id(h,0,chars='@-')
            word = h[0:i]

            for prefix,ivar,val in (
                ('code_prefix','code_mode',True), # '@rst-code'
                ('doc_mode_prefix','doc_only_mode',True), # @rst-doc-only.
                ('default_path_prefix','default_prefix',''), # '@rst-default-path'
                ('rst_prefix','code_mode',False), # '@rst'
                ('ignore_headline_prefix','ignore_this_headline',True), # '@rst-no-head'
                ('show_headline_prefix','show_this_headline',True), # '@rst-head'  
                ('ignore_headlines_prefix','show_headlines',False), # '@rst-no-headlines'
                ('ignore_prefix','ignore_this_tree',True),      # '@rst-ignore'
                ('ignore_node_prefix','ignore_this_node',True), # '@rst-ignore-node'
                ('ignore_tree_prefix','ignore_this_tree',True), # '@rst-ignore-tree'
                ('preformat_prefix','preformat_this_node',True), # '@rst-preformat
            ):
                prefix = self.getOption(prefix)
                if prefix and word == prefix: # Do _not_ munge this prefix!
                    d = { ivar: val }
                    if ivar != 'code_mode':
                        d ['code_mode'] = False # Enter rst mode.
                        d ['doc_only_mode'] = False
                    # Special case: Treat a bare @rst like @rst-no-head
                    if h == self.getOption('rst_prefix'):
                        d ['ignore_this_headline'] = True
                    # g.trace(repr(h),repr(prefix),ivar,d)
                    return d

            if h.startswith('@rst'):
                g.trace('word',word,'rst_prefix',self.getOption('rst_prefix'))
                g.trace('unknown kind of @rst headline',p.h)

            return {}
    #@-node:ekr.20090502071837.50:scanHeadlineForOptions
    #@+node:ekr.20090502071837.51:scanNodeForOptions
    def scanNodeForOptions (self,p):

        '''Return a dictionary containing all the option-name:value entries in p.

        Such entries may arise from rstoptionrstoptionstheheadline import 
        or from rstoptionsdocparts. import 

        h = p.h

        d = self.scanHeadlineForOptions(p)

        d2 = self.scanForOptionDocParts(p,p.b)

        # A fine point: body options over-ride headline options.
        d.update(d2)

        return d
    #@nonl
    #@-node:ekr.20090502071837.51:scanNodeForOptions
    #@+node:ekr.20090502071837.52:scanOption
    def scanOption (self,p,s):

        '''Return { name:val } if s is a line of the form name=val.
        Otherwise return {}'''

        if not s.strip() or s.strip().startswith('..'): return {}

        data = self.parseOptionLine(s)

        if data:
            name,val = data
            fullName = 'rst3_' + self.munge(name)
            if fullName in list(self.defaultOptionsDict.keys()):
                if   val.lower() == 'true': val = True
                elif val.lower() == 'false': val = False
                # g.trace('%24s %8s %s' % (self.munge(name),val,p.h))
                return { self.munge(name): val }
            else:
                g.es_print('ignoring unknown option: %s' % (name),color='red')
                return {}
        else:
            g.trace(repr(s))
            s2 = 'bad rst3 option in %s: %s' % (p.h,s)
            g.es_print(s2,color='red')
            return {}
    #@-node:ekr.20090502071837.52:scanOption
    #@+node:ekr.20090502071837.53:scanOptions
    def scanOptions (self,p,s):

        '''Return a dictionary containing all the options in s.'''

        d = {}

        for line in g.splitLines(s):
            d2 = self.scanOption(p,line)
            if d2: d.update(d2)

        return d
    #@nonl
    #@-node:ekr.20090502071837.53:scanOptions
    #@-node:ekr.20090502071837.46:preprocessTree & helpers
    #@+node:ekr.20090502071837.54:scanAllOptions & helpers
    # Once an option is seen, no other related options in ancestor nodes have any effect.

    def scanAllOptions(self,p):

        '''Scan position p and p's ancestors looking for options,
        setting corresponding ivars.
        '''

        self.initOptionsFromSettings() # Must be done on every node.
        self.handleSingleNodeOptions(p)
        seen = self.singleNodeOptions[:] # Suppress inheritance of single-node options.

        # g.trace('-'*20)
        for p in p.self_and_parents():
            d = self.tnodeOptionDict.get(p.v,{})
            # g.trace(p.h,d)
            for key in d.keys():
                ivar = self.munge(key)
                if not ivar in seen:
                    seen.append(ivar)
                    val = d.get(key)
                    self.setOption(key,val,p.h)

        # self.dumpSettings()
        if self.rst3_all:
            self.setOption("generate_rst", True, "rst3_all")
            self.setOption("generate_rst_header_comment",True, "rst3_all")
            self.setOption("http_server_support", True, "rst3_all")
            self.setOption("write_intermediate_file", True, "rst3_all")
    #@+node:ekr.20090502071837.55:initOptionsFromSettings
    def initOptionsFromSettings (self):

        c = self.c ; d = self.defaultOptionsDict
        keys = sorted(d)

        for key in keys:
            for getter,kind in (
                (c.config.getBool,'@bool'),
                (c.config.getString,'@string'),
                (d.get,'default'),
            ):
                val = getter(key)
                if kind == 'default' or val is not None:
                    self.setOption(key,val,'initOptionsFromSettings')
                    break
        # Special case.
        if self.getOption('http_server_support') and not mod_http:
            g.es('No http_server_support: can not import mod_http plugin',color='red')
            self.setOption('http_server_support',False)
    #@-node:ekr.20090502071837.55:initOptionsFromSettings
    #@+node:ekr.20090502071837.56:handleSingleNodeOptions
    def handleSingleNodeOptions (self,p):

        '''Init the settings of single-node options from thetnodeOptionsDict. import 

        All such options default to False.'''

        d = self.tnodeOptionDict.get(p.v, {} )

        for ivar in self.singleNodeOptions:
            val = d.get(ivar,False)
            #g.trace('%24s %8s %s' % (ivar,val,p.h))
            self.setOption(ivar,val,p.h)

    #@-node:ekr.20090502071837.56:handleSingleNodeOptions
    #@-node:ekr.20090502071837.54:scanAllOptions & helpers
    #@+node:ekr.20090502071837.57:setOption
    def setOption (self,name,val,tag):

        # if name == 'rst3_underline_characters':
            # g.trace(name,val,g.callers(4))

        ivar = self.munge(name)

        # bwm = False
        # if bwm:
            # if ivar not in self.optionsDict:
                # g.trace('init %24s %20s %s %s' % (ivar, val, tag, self))
            # elif self.optionsDict.get(ivar) != val:
                # g.trace('set  %24s %20s %s %s' % (ivar, val, tag, self))

        self.optionsDict [ivar] = val
    #@-node:ekr.20090502071837.57:setOption
    #@-node:ekr.20090502071837.41:options...
    #@+node:ekr.20090502071837.59:Top-level write code
    #@+node:ekr.20090502071837.60:initWrite (rstCommands)
    def initWrite (self,p):

        self.initOptionsFromSettings() # Still needed.

        # Set the encoding from any parent @encoding directive.
        # This can be overridden by @rst-option encoding=whatever.
        c = self.c
        d = c.scanAllDirectives(p)
        self.encoding = d.get('encoding') or 'utf-8'
        self.path = d.get('path') or ''

        # g.trace('path:',self.path)
    #@-node:ekr.20090502071837.60:initWrite (rstCommands)
    #@+node:ekr.20090512153903.5803:writeAtAutoFile (rstCommands)
    def writeAtAutoFile (self,p,fileName,outputFile,trialWrite=False):

        '''Write an @auto tree containing imported rST code.
        The caller will close the output file.'''

        try:
            self.trialWrite = trialWrite
            self.atAutoWrite = True
            self.initAtAutoWrite(p,fileName,outputFile)
            self.topNode = p.copy() # Indicate the top of this tree.
            self.topLevel = p.level()
            after = p.nodeAfterTree()
            ok = self.isSafeWrite(p)
            if ok:
                p = p.firstChild() # A hack: ignore the root node.
                while p and p != after:
                    self.writeNode(p) # side effect: advances p
        finally:
            self.atAutoWrite = False
        return ok
    #@nonl
    #@+node:ekr.20090513073632.5733:initAtAutoWrite (rstCommands)
    def initAtAutoWrite(self,p,fileName,outputFile):

        # Set up for a standard write.
        self.createDefaultOptionsDict()
        self.tnodeOptionDict = {}
        self.scanAllOptions(p)
        self.initWrite(p)
        self.preprocessTree(p) # Allow @ @rst-options, for example.
        # Do the overrides.
        self.outputFile = outputFile
        self.outputFileName = fileName
        # Set underlining characters.
        # It makes no sense to use user-defined
        # underlining characters in @auto-rst.
        d = p.v.u.get('rst-import',{})
        underlines2 = d.get('underlines2','')
            # Do *not* set a default for overlining characters.
        if len(underlines2) > 1:
            underlines2 = underlines2[0]
            g.trace('too many top-level underlines, using %s' % (
                underlines2),color='blue')
        underlines1 = d.get('underlines1','')
        if not underlines1: underlines1 = '=+*^~"\'`-:><_'
            # The standard defaults.
        self.atAutoWriteUnderlines = underlines2 + underlines1
        self.underlines1 = underlines1
        self.underlines2 = underlines2
    #@-node:ekr.20090513073632.5733:initAtAutoWrite (rstCommands)
    #@+node:ekr.20091228080620.6499:isSafeWrite
    def isSafeWrite (self,p):

        '''Return True if node p contributes nothing but
        rst-options to the write.'''

        if self.trialWrite or not p.isAtAutoRstNode():
            return True # Trial writes are always safe.

        lines = g.splitLines(p.b)
        for z in lines:
            if z.strip() and not z.startswith('@') and not z.startswith('.. '):
                # A real line that will not be written.
                g.es('unsafe @auto-rst',color='red')
                g.es('body text will be ignored in\n',p.h)
                return False
        else:
            return True
    #@-node:ekr.20091228080620.6499:isSafeWrite
    #@-node:ekr.20090512153903.5803:writeAtAutoFile (rstCommands)
    #@+node:ekr.20090502071837.61:writeNormalTree
    def writeNormalTree (self,p,toString=False):

        self.initWrite(p)

        # Always write to a string first.
        self.outputFile = StringIO()
        self.writeTree(p)
        self.source = self.stringOutput = self.outputFile.getvalue()

        # Copy to a file if requested.
        if not toString:
            # Comput the output file name *after* calling writeTree.
            self.outputFileName = self.computeOutputFileName(self.outputFileName)
            self.outputFile = open(self.outputFileName,'w')
            self.outputFile.write(self.stringOutput)
            self.outputFile.close()

        return True
    #@-node:ekr.20090502071837.61:writeNormalTree
    #@+node:ekr.20090502071837.62:processTopTree
    def processTopTree (self,p,justOneFile=False):

        c = self.c ; current = p.copy()

        for p in current.self_and_parents():
            h = p.h
            if h.startswith('@rst') and not h.startswith('@rst-'):
                self.processTree(p,ext=None,toString=False,justOneFile=justOneFile)
                break
        else:
            self.processTree(current,ext=None,toString=False,justOneFile=justOneFile)

        g.es_print('done',color='blue')
    #@nonl
    #@-node:ekr.20090502071837.62:processTopTree
    #@+node:ekr.20090502071837.63:processTree
    def processTree(self,p,ext,toString,justOneFile):

        '''Process all @rst nodes in a tree.'''

        self.preprocessTree(p)
        found = False ; self.stringOutput = ''
        p = p.copy() ; after= p.nodeAfterTree()
        while p and p != after:
            h = p.h.strip()
            if g.match_word(h,0,"@rst"):
                self.outputFileName = h[4:].strip()
                if (
                    (self.outputFileName and self.outputFileName[0] != '-') or
                    (toString and not self.outputFileName)
                ):
                    found = True
                    self.topLevel = p.level() # Define toplevel separately for each rst file.
                    if toString:
                        self.ext = ext
                        if not self.ext.startswith('.'):
                            self.ext = '.'+self.ext
                    else:
                        self.ext = g.os_path_splitext(self.outputFileName)[1].lower()
                    # g.trace('ext',self.ext,self.outputFileName)
                    if self.ext in ('.htm','.html','.tex','.pdf'):
                        ok = self.writeSpecialTree(p,toString=toString,justOneFile=justOneFile)
                    else:
                        ok = self.writeNormalTree(p,toString=toString)
                    self.scanAllOptions(p) # Restore the top-level verbose setting.
                    if toString:
                        return p.copy(),self.stringOutput
                    else:
                        if ok: self.report(self.outputFileName)
                    p.moveToNodeAfterTree()
                else:
                    p.moveToThreadNext()
            else: p.moveToThreadNext()
        if not found:
            g.es('No @rst nodes in selected tree',color='blue')
        return None,None
    #@-node:ekr.20090502071837.63:processTree
    #@+node:ekr.20090502071837.64:writeSpecialTree
    def writeSpecialTree (self,p,toString,justOneFile):

        c = self.c
        isHtml = self.ext in ('.html','.htm')
        if isHtml and not SilverCity:
            if not self.silverCityWarningGiven:
                self.silverCityWarningGiven = True
                g.es('SilverCity not present so no syntax highlighting')

        self.initWrite(p)
            # was encoding=g.choose(isHtml,'utf-8','iso-8859-1'))
        self.outputFile = StringIO()
        self.writeTree(p)
        self.source = self.outputFile.getvalue()
        self.outputFile = None

        if not toString:
            # Compute this here for use by intermediate file.
            self.outputFileName = self.computeOutputFileName(self.outputFileName)

            # Create the directory if it doesn't exist.
            theDir, junk = g.os_path_split(self.outputFileName)
            theDir = c.os_path_finalize(theDir)
            if not g.os_path_exists(theDir):
                ok = g.makeAllNonExistentDirectories(theDir,c=c,force=False)
                if not ok:
                    g.es_print('did not create:',theDir,color='red')
                    return False

            # if not os.access(theDir,os.F_OK):
                # os.mkdir(theDir)

            if self.getOption('write_intermediate_file'):
                name = self.outputFileName + '.txt'
                f = open(name,'w')
                f.write(self.source)
                f.close()
                self.report(name)

        try:
            output = self.writeToDocutils(self.source)
            ok = output is not None
        except Exception:
            g.pr('Exception in docutils')
            g.es_exception()
            ok = False

        if ok:
            if isHtml:
                import re
                idxTitle = output.find('<title></title>')
                if idxTitle > -1:
                    m = re.search('<h1>([^<]*)</h1>', output)
                    if not m:
                        m = re.search('<h1><[^>]+>([^<]*)</a></h1>', output)
                    if m:
                        output = output.replace(
                            '<title></title>',
                            '<title>%s</title>' % m.group(1)
                        )


            if toString:
                self.stringOutput = output
            else:
                # Write the file to the directory containing the .leo file.
                f = open(self.outputFileName,'w')
                f.write(output)
                f.close()
                self.http_endTree(self.outputFileName, p, justOneFile=justOneFile)

        return ok
    #@-node:ekr.20090502071837.64:writeSpecialTree
    #@+node:ekr.20090502071837.65:writeToDocutils (sets argv) & helper
    def writeToDocutils (self,s):

        '''Send s to docutils using the writer implied by self.ext and return the result.'''

        trace = False and not g.unitTesting

        if not docutils:
            g.es('docutils not present',color='red')
            return

        openDirectory = self.c.frame.openDirectory
        overrides = {'output_encoding': self.encoding }

        # Compute the args list if the stylesheet path does not exist.
        styleSheetArgsDict = self.handleMissingStyleSheetArgs()

        for ext,writer in (
            ('.html','html'),
            ('.htm','html'),
            ('.tex','latex'),
            ('.pdf','leo_pdf'),
        ):
            if self.ext == ext:
                break
        else:
            g.es_print('unknown docutils extension: %s' % (self.ext),color='red')
            return ''

        # Make the stylesheet path relative to the directory containing the output file.
        rel_stylesheet_path = self.getOption('stylesheet_path') or ''

        # New in Leo 4.5: The rel_stylesheet_path is relative to the open directory.
        stylesheet_path = g.os_path_finalize_join(
            self.c.frame.openDirectory,rel_stylesheet_path)

        path = g.os_path_finalize_join(
            stylesheet_path,self.getOption('stylesheet_name'))

        res = ""
        if self.getOption('stylesheet_embed') == False:
            rel_path = g.os_path_join(
                rel_stylesheet_path,self.getOption('stylesheet_name'))
            rel_path = rel_path.replace('\\','/') # 2010/01/28
            overrides['stylesheet'] = rel_path
            overrides['stylesheet_path'] = None
            overrides['embed_stylesheet'] = None
        elif g.os_path_exists(path):
            if self.ext != '.pdf':
                overrides['stylesheet'] = path
                overrides['stylesheet_path'] = None
        elif styleSheetArgsDict:
            g.es_print('using publish_argv_for_missing_stylesheets',
                styleSheetArgsDict)
            overrides.update(styleSheetArgsDict)
                # MWC add args to settings
        elif rel_stylesheet_path == stylesheet_path:
            g.es_print('stylesheet not found: %s' % (path),color='red')
        else:
            g.es_print('stylesheet not found\n',path,color='red')
            if self.path:g.es_print('@path:', self.path)
            g.es_print('open path:',self.c.frame.openDirectory)
            if rel_stylesheet_path:
                g.es_print('relative path:', rel_stylesheet_path)
        try:
            # All paths now come through here.
            if trace: g.trace('overrides',overrides)
            result = None # Ensure that result is defined.
            result = docutils.core.publish_string(source=s,
                    reader_name='standalone',
                    parser_name='restructuredtext',
                    writer_name=writer,
                    settings_overrides=overrides)
        except docutils.ApplicationError as error:
            # g.es_print('Docutils error (%s):' % (
                # error.__class__.__name__),color='red')
            g.es_print('Docutils error:',color='red')
            g.es_print(error,color='blue')
        except Exception:
            g.es_print('Unexpected docutils exception')
            g.es_exception()
        return result
    #@+node:ekr.20090502071837.66:handleMissingStyleSheetArgs
    def handleMissingStyleSheetArgs (self,s=None):

        '''Parse the publish_argv_for_missing_stylesheets option,
        returning a dict containing the parsed args.'''

        force = False
        if force:
            # See http://docutils.sourceforge.net/docs/user/config.html#documentclass
            return {'documentclass':'report', 'documentoptions':'english,12pt,lettersize'}

        if not s:
            s = self.getOption('publish_argv_for_missing_stylesheets')
        if not s: return {}

        # Handle argument lists such as this:
        # --language=en,--documentclass=report,--documentoptions=[english,12pt,lettersize]
        d = {}
        while s:
            s = s.strip()
            if not s.startswith('--'): break
            s = s[2:].strip()
            eq = s.find('=')
            cm = s.find(',')
            if eq == -1 or (-1 < cm < eq): # key[nl] or key,
                val = ''
                cm = s.find(',')
                if cm == -1:
                    key = s.strip()
                    s = ''
                else:
                    key = s[:cm].strip()
                    s = s[cm+1:].strip()
            else: # key = val
                key = s[:eq].strip()
                s = s[eq+1:].strip()
                if s.startswith('['): # [...]
                    rb = s.find(']')
                    if rb == -1: break # Bad argument.
                    val = s[:rb+1]
                    s = s[rb+1:].strip()
                    if s.startswith(','):
                        s = s[1:].strip()
                else: # val[nl] or val,
                    cm = s.find(',')
                    if cm == -1:
                        val = s
                        s = ''
                    else:
                        val = s[:cm].strip()
                        s = s[cm+1:].strip()

            # g.trace('key',repr(key),'val',repr(val),'s',repr(s))
            if not key: break
            if not val.strip(): val = '1'
            d[str(key)] = str(val)

        return d
    #@nonl
    #@-node:ekr.20090502071837.66:handleMissingStyleSheetArgs
    #@-node:ekr.20090502071837.65:writeToDocutils (sets argv) & helper
    #@+node:ekr.20090502071837.67:writeNodeToString (New in 4.4.1)
    def writeNodeToString (self,p=None,ext=None):

        '''Scan p's tree (defaults to presently selected tree) looking for @rst nodes.
        Convert the first node found to an ouput of the type specified by ext.

        The @rst may or may not be followed by a filename; the filename is *ignored*,
        and its type does not affect ext or the output generated in any way.

        ext should start with a period: .html, .tex or None (specifies rst output).

        Returns p, s, where p is the position of the @rst node and s is the converted text.'''

        c = self.c ; current = p or c.p

        for p in current.self_and_parents():
            if p.h.startswith('@rst'):
                return self.processTree(p,ext=ext,toString=True,justOneFile=True)
        else:
            return self.processTree(current,ext=ext,toString=True,justOneFile=True)
    #@nonl
    #@-node:ekr.20090502071837.67:writeNodeToString (New in 4.4.1)
    #@-node:ekr.20090502071837.59:Top-level write code
    #@+node:ekr.20090502071837.58:write methods (leoRst)
    #@+node:ekr.20090502071837.68:getDocPart
    def getDocPart (self,lines,n):

        # g.trace('n',n,repr(''.join(lines)))

        result = []
        #@    << Append whatever follows @doc or @space to result >>
        #@+node:ekr.20090502071837.69:<< Append whatever follows @doc or @space to result >>
        if n > 0:
            line = lines[n-1]
            if line.startswith('@doc'):
                s = line[4:].lstrip()
            elif line.startswith('@'):
                s = line[1:].lstrip()
            else:
                s = ''

            # New in Leo 4.4.4: remove these special tags.
            for tag in ('@rst-options','@rst-option','@rst-markup'):
                if g.match_word(s,0,tag):
                    s = s[len(tag):].strip()

            if s.strip():
                result.append(s)
        #@-node:ekr.20090502071837.69:<< Append whatever follows @doc or @space to result >>
        #@nl
        while n < len(lines):
            s = lines [n] ; n += 1
            if g.match_word(s,0,'@code') or g.match_word(s,0,'@c'):
                break
            result.append(s)
        return n, result
    #@nonl
    #@-node:ekr.20090502071837.68:getDocPart
    #@+node:ekr.20090502071837.72:handleCodeMode & helper
    def handleCodeMode (self,lines):

        '''Handle the preprocessed body text in code mode as follows:

        - Blank lines are copied after being cleaned.
        - @ @rst-markup lines get copied as is.
        - Everything else gets put into a code-block directive.'''

        result = [] ; n = 0 ; code = []
        while n < len(lines):
            s = lines [n] ; n += 1
            if (
                self.isSpecialDocPart(s,'@rst-markup') or (
                    self.getOption('show_doc_parts_as_paragraphs') and
                    self.isSpecialDocPart(s,None)
                )
            ):
                if code:
                    self.finishCodePart(result,code)
                    code = []
                result.append('')
                n, lines2 = self.getDocPart(lines,n)
                # A fix, perhaps dubious, to a bug discussed at
                # http://groups.google.com/group/leo-editor/browse_thread/thread/c212814815c92aac
                # lines2 = [z.lstrip() for z in lines2]
                # g.trace('lines2',lines2)
                result.extend(lines2)
            elif not s.strip() and not code:
                pass # Ignore blank lines before the first code block.
            elif not code: # Start the code block.
                result.append('')
                result.append(self.code_block_string)
                code.append(s)
            else: # Continue the code block.
                code.append(s)

        if code:
            self.finishCodePart(result,code)
            code = []

        # Munge the result so as to keep docutils happy.
        # Don't use self.rstripList: it's not the same.
        # g.trace(result)
        result2 = []
        for z in result:
            if z == '': result2.append('\n\n')
            elif not z.rstrip(): pass
            elif z.endswith('\n\n'): result2.append(z) # Leave alone.
            else: result2.append('%s\n' % z.rstrip())

        return result2
    #@+node:ekr.20090502071837.73:formatCodeModeLine
    def formatCodeModeLine (self,s,n,numberOption):

        if not s.strip(): s = ''

        if numberOption:
            return '\t%d: %s' % (n,s)
        else:
            return '\t%s' % s
    #@nonl
    #@-node:ekr.20090502071837.73:formatCodeModeLine
    #@+node:ekr.20090502071837.74:rstripList
    def rstripList (self,theList):

        '''Removed trailing blank lines from theList.'''

        s = '\n'.join(theList).rstrip()
        return s.split('\n')
    #@-node:ekr.20090502071837.74:rstripList
    #@+node:ekr.20090502071837.75:finishCodePart
    def finishCodePart (self,result,code):

        numberOption = self.getOption('number_code_lines')
        code = self.rstripList(code)
        i = 0
        for line in code:
            i += 1
            result.append(self.formatCodeModeLine(line,i,numberOption))
    #@nonl
    #@-node:ekr.20090502071837.75:finishCodePart
    #@-node:ekr.20090502071837.72:handleCodeMode & helper
    #@+node:ekr.20090502071837.76:handleDocOnlyMode
    def handleDocOnlyMode (self,p,lines):

        '''Handle the preprocessed body text in doc_only mode as follows:

        - Blank lines are copied after being cleaned.
        - @ @rst-markup lines get copied as is.
        - All doc parts get copied.
        - All code parts are ignored.'''

        ignore              = self.getOption('ignore_this_headline')
        showHeadlines       = self.getOption('show_headlines')
        showThisHeadline    = self.getOption('show_this_headline')
        showOrganizers      = self.getOption('show_organizer_nodes')

        result = [] ; n = 0
        while n < len(lines):
            s = lines [n] ; n += 1
            if self.isSpecialDocPart(s,'@rst-options'):
                n, lines2 = self.getDocPart(lines,n) # ignore.
            elif self.isAnyDocPart(s):
                # Handle any other doc part, including @rst-markup.
                n, lines2 = self.getDocPart(lines,n)
                if lines2: result.extend(lines2)
        if not result: result = []
        if showHeadlines:
            if result or showThisHeadline or showOrganizers or p == self.topNode:
                # g.trace(len(result),p.h)
                self.writeHeadlineHelper(p)
        return result
    #@nonl
    #@-node:ekr.20090502071837.76:handleDocOnlyMode
    #@+node:ekr.20090502071837.81:handleSpecialDocParts
    def handleSpecialDocParts (self,lines,kind,retainContents,asClass=None):

        # g.trace(kind,g.listToString(lines))

        result = [] ; n = 0
        while n < len(lines):
            s = lines [n] ; n += 1
            if s.strip().endswith('::'):
                n, lit = self.skip_literal_block(lines,n-1)
                result.extend(lit)
            elif self.isSpecialDocPart(s,kind):
                n, lines2 = self.getDocPart(lines,n)
                if retainContents:
                    result.extend([''])
                    if asClass:
                        result.extend(['.. container:: '+asClass, ''])
                        if 'literal' in asClass.split():
                            result.extend(['  ::', ''])
                        for l2 in lines2: result.append('    '+l2)
                    else:
                        result.extend(lines2)
                    result.extend([''])
            else:
                result.append(s)

        return result
    #@-node:ekr.20090502071837.81:handleSpecialDocParts
    #@+node:ekr.20090502071837.77:isAnyDocPart
    def isAnyDocPart (self,s):

        if s.startswith('@doc'):
            return True
        elif not s.startswith('@'):
            return False
        else:
            return len(s) == 1 or s[1].isspace()
    #@nonl
    #@-node:ekr.20090502071837.77:isAnyDocPart
    #@+node:ekr.20090502071837.79:isAnySpecialDocPart
    def isAnySpecialDocPart (self,s):

        for kind in (
            '@rst-markup',
            '@rst-option',
            '@rst-options',
        ):
            if self.isSpecialDocPart(s,kind):
                return True

        return False
    #@-node:ekr.20090502071837.79:isAnySpecialDocPart
    #@+node:ekr.20090502071837.78:isSpecialDocPart
    def isSpecialDocPart (self,s,kind):

        '''Return True if s is a special doc part of the indicated kind.

        If kind is None, return True if s is any doc part.'''

        if s.startswith('@') and len(s) > 1 and s[1].isspace():
            if kind:
                i = g.skip_ws(s,1)
                result = g.match_word(s,i,kind)
            else:
                result = True
        elif not kind:
            result = g.match_word(s,0,'@doc') or g.match_word(s,0,'@')
        else:
            result = False

        # g.trace('kind %s, result %s, s %s' % (
            # repr(kind),result,repr(s)))

        return result
    #@nonl
    #@-node:ekr.20090502071837.78:isSpecialDocPart
    #@+node:ekr.20090502071837.80:removeLeoDirectives
    def removeLeoDirectives (self,lines):

        '''Remove all Leo directives, except within literal blocks.'''

        n = 0 ; result = []
        while n < len(lines):
            s = lines [n] ; n += 1
            if s.strip().endswith('::'):
                n, lit = self.skip_literal_block(lines,n-1)
                result.extend(lit)
            elif s.startswith('@') and not self.isAnySpecialDocPart(s):
                for key in self.leoDirectivesList:
                    if g.match_word(s,0,key):
                        # g.trace('removing %s' % s)
                        break
                else:
                    result.append(s)
            else:
                result.append(s)

        return result
    #@nonl
    #@-node:ekr.20090502071837.80:removeLeoDirectives
    #@+node:ekr.20090502071837.82:replaceCodeBlockDirectives
    def replaceCodeBlockDirectives (self,lines):

        '''Replace code-block directive, but not in literal blocks.'''

        n = 0 ; result = []
        while n < len(lines):
            s = lines [n] ; n += 1
            if s.strip().endswith('::'):
                n, lit = self.skip_literal_block(lines,n-1)
                result.extend(lit)
            else:
                i = g.skip_ws(s,0)
                if g.match(s,i,'..'):
                    i = g.skip_ws(s,i+2)
                    if g.match_word(s,i,'code-block'):
                        if 1: # Create a literal block to hold the code.
                            result.append('::\n')
                        else: # This 'annotated' literal block is confusing.
                            result.append('%s code::\n' % s[i+len('code-block'):])
                    else:
                        result.append(s)
                else:
                    result.append(s)

        return result
    #@nonl
    #@-node:ekr.20090502071837.82:replaceCodeBlockDirectives
    #@+node:ekr.20090502071837.70:skip_literal_block
    def skip_literal_block (self,lines,n):

        s = lines[n] ; result = [s] ; n += 1
        indent = g.skip_ws(s,0)

        # Skip lines until a non-blank line is found with same or less indent.
        while n < len(lines):
            s = lines[n]
            indent2 = g.skip_ws(s,0)
            if s and not s.isspace() and indent2 <= indent:
                break # We will rescan lines [n]
            n += 1
            result.append(s)

        # g.printList(result,tag='literal block')
        return n, result
    #@nonl
    #@-node:ekr.20090502071837.70:skip_literal_block
    #@+node:ekr.20090502071837.94:write (leoRst)
    def write (self,s):

        if self.trialWrite:
            pass
        elif g.isPython3:
            pass
        else:
            s = self.encode(s)

        # g.trace(repr(s),g.callers(2))

        self.outputFile.write(s)
    #@-node:ekr.20090502071837.94:write (leoRst)
    #@+node:ekr.20090502071837.71:writeBody
    def writeBody (self,p):

        # g.trace(p.h,p.b)

        # remove trailing cruft and split into lines.
        lines = g.splitLines(p.b)

        if self.getOption('code_mode'):
            if not self.getOption('show_options_doc_parts'):
                lines = self.handleSpecialDocParts(lines,'@rst-options',
                    retainContents=False)
            if not self.getOption('show_markup_doc_parts'):
                lines = self.handleSpecialDocParts(lines,'@rst-markup',
                    retainContents=False)
            if not self.getOption('show_leo_directives'):
                lines = self.removeLeoDirectives(lines)
            lines = self.handleCodeMode(lines)
        elif self.getOption('doc_only_mode'):
            # New in version 1.15
            lines = self.handleDocOnlyMode(p,lines)
        else:
            lines = self.handleSpecialDocParts(lines,'@rst-options',
                retainContents=False)
            lines = self.handleSpecialDocParts(lines,'@rst-markup',
                retainContents=self.getOption('generate_rst'))
            if self.getOption('show_doc_parts_in_rst_mode') is True:
                pass  # original behaviour, treat as plain text
            elif self.getOption('show_doc_parts_in_rst_mode'):
                # use value as class for content
                lines = self.handleSpecialDocParts(lines,None,
                    retainContents=True, asClass=self.getOption('show_doc_parts_in_rst_mode'))
            else:  # option evaluates to false, cut them out
                lines = self.handleSpecialDocParts(lines,None,
                    retainContents=False)
            lines = self.removeLeoDirectives(lines)
            if self.getOption('generate_rst') and self.getOption('use_alternate_code_block'):
                lines = self.replaceCodeBlockDirectives(lines)

        # Write the lines.
        s = ''.join(lines)

        # We no longer add newlines to the start of nodes because
        # we write a blank line after all sections.
        # s = g.ensureLeadingNewlines(s,1)
        s = g.ensureTrailingNewlines(s,2)
        self.write(s)
    #@-node:ekr.20090502071837.71:writeBody
    #@+node:ekr.20090502071837.83:writeHeadline & helper
    def writeHeadline (self,p):

        '''Generate an rST section if options permit it.
        Remove headline commands from theheadlinefirst import 
        and never generate an rST section for @rst-option and @rst-options.'''

        docOnly             =  self.getOption('doc_only_mode')
        ignore              = self.getOption('ignore_this_headline')
        showHeadlines       = self.getOption('show_headlines')
        showThisHeadline    = self.getOption('show_this_headline')
        showOrganizers      = self.getOption('show_organizer_nodes')

        if (
            p == self.topNode or
            ignore or
            docOnly or # handleDocOnlyMode handles this.
            not showHeadlines and not showThisHeadline or
            # docOnly and not showOrganizers and not thisHeadline or
            not p.h.strip() and not showOrganizers or
            not p.b.strip() and not showOrganizers
        ):
            return

        self.writeHeadlineHelper(p)
    #@nonl
    #@+node:ekr.20090502071837.84:writeHeadlineHelper
    def writeHeadlineHelper (self,p):

        h = p.h
        if not self.atAutoWrite:
            h = h.strip()

        # Remove any headline command before writing the headline.
        i = g.skip_ws(h,0)
        i = g.skip_id(h,0,chars='@-')
        word = h [:i].strip()
        if word:
            # Never generate a section for @rst-option or @rst-options or @rst-no-head.
            if word in (
                self.getOption('option_prefix'),
                self.getOption('options_prefix'),
                self.getOption('ignore_headline_prefix'), # Bug fix: 2009-5-13
                self.getOption('ignore_headlines_prefix'),  # Bug fix: 2009-5-13
            ):
                return
            # Remove all other headline commands from the headline.
            for prefix in self.headlineCommands:
                if word == prefix:
                    h = h [len(word):].strip()
                    break

            # New in Leo 4.4.4.
            if word.startswith('@'):
                if self.getOption('strip_at_file_prefixes'):
                    for s in ('@auto','@file','@nosent','@thin',):
                        if g.match_word(word,0,s):
                            h = h [len(s):].strip()

        if not h.strip(): return

        if self.getOption('show_sections'):
            if self.getOption('generate_rst'):
                self.write(self.underline(h,p)) # Used by @auto-rst.
            else:
                self.write('\n%s\n\n' % h)
        else:
            self.write('\n**%s**\n\n' % h.replace('*',''))
    #@-node:ekr.20090502071837.84:writeHeadlineHelper
    #@-node:ekr.20090502071837.83:writeHeadline & helper
    #@+node:ekr.20090502071837.85:writeNode (rst)
    def writeNode (self,p):

        '''Format a node according to the options presently in effect.'''

        self.initCodeBlockString(p)
        self.scanAllOptions(p)

        if 0:
            g.trace('%24s code_mode %s' % (p.h,self.getOption('code_mode')))

        h = p.h.strip()

        if self.getOption('preformat_this_node'):
            self.http_addNodeMarker(p)
            self.writePreformat(p)
            p.moveToThreadNext()
        elif self.getOption('ignore_this_tree'):
            p.moveToNodeAfterTree()
        elif self.getOption('ignore_this_node'):
            p.moveToThreadNext()
        elif g.match_word(h,0,'@rst-options') and not self.getOption('show_options_nodes'):
            p.moveToThreadNext()
        else:
            self.http_addNodeMarker(p)
            self.writeHeadline(p)
            self.writeBody(p)
            p.moveToThreadNext()
    #@-node:ekr.20090502071837.85:writeNode (rst)
    #@+node:ekr.20090502071837.86:writePreformat
    def writePreformat (self,p):

        '''Write p's body text lines as if preformatted.

         ::

            line 1
            line 2 etc.
        '''

        # g.trace(p.h,g.callers())

        lines = p.b.split('\n')
        lines = [' '*4 + z for z in lines]
        lines.insert(0,'::\n')

        s = '\n'.join(lines)
        if s.strip():
            self.write('%s\n\n' % s)
    #@-node:ekr.20090502071837.86:writePreformat
    #@+node:ekr.20090502071837.87:writeTree
    def writeTree(self,p):

        '''Write p's tree to self.outputFile.'''

        self.scanAllOptions(p)

        # g.trace(self.getOption('generate_rst_header_comment'))

        if self.getOption('generate_rst'):
            if self.getOption('generate_rst_header_comment'):
                self.write(self.rstComment(
                    'rst3: filename: %s\n\n' % self.outputFileName))

        # We can't use an iterator because we may skip parts of the tree.
        p = p.copy() # Only one copy is needed for traversal.
        self.topNode = p.copy() # Indicate the top of this tree.
        after = p.nodeAfterTree()
        while p and p != after:
            self.writeNode(p) # Side effect: advances p.
    #@-node:ekr.20090502071837.87:writeTree
    #@-node:ekr.20090502071837.58:write methods (leoRst)
    #@+node:ekr.20090502071837.88:Utils
    #@+node:ekr.20090502071837.89:computeOutputFileName
    def computeOutputFileName (self,fileName):

        openDirectory = self.c.frame.openDirectory
        default_path = self.getOption('default_path')

        if default_path:
            path = g.os_path_finalize_join(self.path,default_path,fileName)
        elif self.path:
            path = g.os_path_finalize_join(self.path,fileName)
        elif openDirectory:
            path = g.os_path_finalize_join(self.path,openDirectory,fileName)
        else:
            path = g.os_path_finalize_join(fileName)

        # g.trace('openDirectory %s\ndefault_path %s\npath %s' % (
            # repr(openDirectory),repr(default_path),repr(path)))

        return path
    #@nonl
    #@-node:ekr.20090502071837.89:computeOutputFileName
    #@+node:ekr.20090502071837.90:encode
    def encode (self,s):

        return g.toEncodedString(s,encoding=self.encoding,reportErrors=True)
    #@nonl
    #@-node:ekr.20090502071837.90:encode
    #@+node:ekr.20090502071837.91:report
    def report (self,name):

        if self.getOption('verbose'):

            name = g.os_path_finalize(name)

            g.es_print('wrote: %s' % (name),color="blue")
    #@nonl
    #@-node:ekr.20090502071837.91:report
    #@+node:ekr.20090502071837.92:rstComment
    def rstComment (self,s):

        return '.. %s' % s
    #@nonl
    #@-node:ekr.20090502071837.92:rstComment
    #@+node:ekr.20090502071837.93:underline (leoRst)
    def underline (self,s,p):

        '''Return the underlining string to be used at the given level for string s.
        This includes the headline, and possibly a leading overlining line.
        '''

        trace = False and not g.unitTesting

        if trace: g.trace(self.atAutoWrite,repr(self.underlines2),p.h)

        if self.atAutoWrite:
            # We *might* generate overlines for top-level sections.
            u = self.atAutoWriteUnderlines
            level = p.level()-self.topLevel

            # This is tricky. The index n depends on several factors.
            if self.underlines2:
                level -= 1 # There *is* a double-underlined section.
                n = level
            else:
                n = level-1
            if 0 <= n < len(u): ch = u[n]
            elif u:
                ch = u[-1]
            else:
                g.trace('can not happen: no u')
                ch = '#'

            # 2010/01/10: write longer underlines for non-ascii characters.
            # n = max(4,len(s))
            n = max(4,len(g.toEncodedString(s,encoding=self.encoding,reportErrors=False)))
                ### Was encoding = 'utf-8'
            if trace: g.trace(self.topLevel,p.level(),level,repr(ch),p.h)
            if level == 0 and self.underlines2:
                return '%s\n%s\n%s\n\n' % (ch*n,p.h,ch*n)
            else:
                return '%s\n%s\n\n' % (p.h,ch*n)
        else:
            # The user is responsible for top-level overlining.
            u = self.getOption('underline_characters') #  '''#=+*^~"'`-:><_'''
            level = max(0,p.level()-self.topLevel)
            level = min(level+1,len(u)-1) # Reserve the first character for explicit titles.
            ch = u [level]
            if trace: g.trace(self.topLevel,p.level(),level,repr(ch),p.h)
            # 2010/01/10: write longer underlines for non-ascii characters.
            # n = max(4,len(s))
            n = max(4,len(g.toEncodedString(s,encoding=self.encoding,reportErrors=False)))
                ### Was encoding = 'utf-8'
            return '%s\n%s\n\n' % (p.h.strip(),ch*n)
    #@-node:ekr.20090502071837.93:underline (leoRst)
    #@-node:ekr.20090502071837.88:Utils
    #@+node:ekr.20090502071837.95:Support for http plugin
    #@+node:ekr.20090502071837.96:http_addNodeMarker
    def http_addNodeMarker (self,p):

        if (
            self.getOption('http_server_support') and
            self.getOption('generate_rst')
        ):
            self.nodeNumber += 1
            anchorname = "%s%s" % (self.getOption('node_begin_marker'),self.nodeNumber)
            s = "\n\n.. _%s:\n\n" % anchorname
            self.write(s)
            self.http_map [anchorname] = p.copy()
            # if bwm_file: print >> bwm_file, "addNodeMarker", anchorname, p
    #@nonl
    #@-node:ekr.20090502071837.96:http_addNodeMarker
    #@+node:ekr.20090502071837.97:http_endTree & helpers
    # Was http_support_main

    def http_endTree (self,filename,p,justOneFile):

        '''Do end-of-tree processing to support the http plugin.'''

        if (
            self.getOption('http_server_support') and
            self.getOption('generate_rst')
        ):
            self.set_initial_http_attributes(filename)
            self.find_anchors(p)
            if justOneFile:
                self.relocate_references(p.self_and_subtree)

            g.es_print('html updated for http plugin',color="blue")

            if self.getOption('clear_http_attributes'):
                g.es_print("http attributes cleared")
    #@nonl
    #@+node:ekr.20090502071837.98:set_initial_http_attributes
    def set_initial_http_attributes (self,filename):

        f = open(filename)
        parser = htmlParserClass(self)

        for line in f.readlines():
            parser.feed(line)

        f.close()
    #@nonl
    #@-node:ekr.20090502071837.98:set_initial_http_attributes
    #@+node:ekr.20090502071837.100:relocate_references
    #@+at 
    #@nonl
    # Relocate references here if we are only running for one file.
    # 
    # Otherwise we must postpone the relocation until we have 
    # processed all files.
    #@-at
    #@@c

    def relocate_references (self, iterator_generator):

        for p in iterator_generator():
            attr = mod_http.get_http_attribute(p)
            if not attr:
                continue
            # g.trace('before',p.h,attr)
            # if bwm_file:
                # print >> bwm_file
                # print >> bwm_file, "relocate_references(1): Position, attr:"
                # pprint.pprint((p, attr), bwm_file)
            http_lines = attr [3:]
            parser = link_htmlparserClass(self,p)
            for line in attr [3:]:
                try:
                    parser.feed(line)
                except:
                    line = ''.join([ch for ch in line if ord(ch) <= 127])
                    parser.feed(line)
            replacements = parser.get_replacements()
            replacements.reverse()
            if not replacements:
                continue
            # if bwm_file:
                # print >> bwm_file, "relocate_references(2): Replacements:"
                # pprint.pprint(replacements, bwm_file)
            for line, column, href, href_file, http_node_ref in replacements:
                # if bwm_file: 
                    # print >> bwm_file, "relocate_references(3): line:", line,
                    # "Column:", column, "href:", href, "href_file:",
                    # href_file, "http_node_ref:", http_node_ref
                marker_parts = href.split("#")
                if len(marker_parts) == 2:
                    marker = marker_parts [1]
                    # replacement = u"%s#%s" % (http_node_ref,marker)
                    replacement = '%s#%s' % (http_node_ref,marker)
                    try:
                        # attr [line + 2] = attr [line + 2].replace(u'href="%s"' % href, u'href="%s"' % replacement)
                        attr [line + 2] = attr [line + 2].replace('href="%s"' % href, 'href="%s"' % replacement)
                    except:
                        g.es("Skipped ", attr[line + 2])
                else:
                    filename = marker_parts [0]
                    try:
                        # attr [line + 2] = attr [line + 2].replace(u'href="%s"' % href,u'href="%s"' % http_node_ref)
                        attr [line + 2] = attr [line + 2].replace('href="%s"' % href,'href="%s"' % http_node_ref)
                    except:
                        g.es("Skipped", attr[line+2])
        # g.trace('after %s\n\n\n',attr)
    #@nonl
    #@-node:ekr.20090502071837.100:relocate_references
    #@+node:ekr.20090502071837.99:find_anchors
    def find_anchors (self, p):

        '''Find the anchors in all the nodes.'''

        for p1, attrs in self.http_attribute_iter(p):
            html = mod_http.reconstruct_html_from_attrs(attrs)
            # g.trace(pprint.pprint(html))
            parser = anchor_htmlParserClass(self, p1)
            for line in html:
                try:
                    parser.feed(line)
                # bwm: changed to unicode(line)
                except:
                    line = ''.join([ch for ch in line if ord(ch) <= 127])
                    # filter out non-ascii characters.
                    # bwm: not quite sure what's going on here.
                    parser.feed(line)        
        # g.trace(g.dictToString(self.anchor_map,tag='anchor_map'))
    #@nonl
    #@-node:ekr.20090502071837.99:find_anchors
    #@+node:ekr.20090502071837.101:http_attribute_iter
    def http_attribute_iter (self, p):
        """
        Iterator for all the nodes which have html code.
        Look at the descendents of p.
        Used for relocation.
        """

        for p1 in p.self_and_subtree():
            attr = mod_http.get_http_attribute(p1)
            if attr:
                yield (p1.copy(),attr)
    #@nonl
    #@-node:ekr.20090502071837.101:http_attribute_iter
    #@-node:ekr.20090502071837.97:http_endTree & helpers
    #@-node:ekr.20090502071837.95:Support for http plugin
    #@-others
#@-node:ekr.20090502071837.33:class rstCommands
#@-others
#@-node:ekr.20090502071837.3:@thin leoRst.py
#@-leo
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.