#
# FILE            $Id: docutil.py,v 1.5 1996/09/06 19:14:24 omfadmin Exp $
#
# DESCRIPTION     Collect info about modules, and generate Manual page.
#
# AUTHOR          SEISY/LKSB Daniel Larsson
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose and without fee is hereby granted,
# provided that the above copyright notice appear in all copies and that
# both that copyright notice and this permission notice appear in
# supporting documentation, and that the name of ABB Industrial Systems
# not be used in advertising or publicity pertaining to
# distribution of the software without specific, written prior permission.
#
# ABB INDUSTRIAL SYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO
# THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
# FITNESS, IN NO EVENT SHALL ABB INDUSTRIAL SYSTEMS BE LIABLE
# FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
# 
# Copyright (C) ABB Industrial Systems AB, 1996
# Unpublished work.  All Rights Reserved.
#
# HISTORY: $Log: /Gendoc/docutil.py $
# 
# 1     98-04-01 13:15 Daniel
# Revision 1.5  1996/09/06  19:14:24  omfadmin
# Fixes by Robin Friedrich (stripped too much in stripleadingtabs)
# Various small fixes.
# NOTE: Nested lists doesn't work yet!! I know basically what the
# problem is, but I'm going to Spain now...
#
# Revision 1.4  1996/09/06  09:55:07  omfadmin
# Replaced setext markup with structured text markup.
#
# Revision 1.3  1996/09/04  14:07:40  omfadmin
# More StructuredText. Removed most of setext.
#
# Revision 1.2  1996/08/26  20:29:41  omfadmin
# Added structured text (Jim Fulton) support, at least to some
# degree.
#
# Revision 1.1  1996/07/11  16:38:17  omfadmin
# Initial revision
#

"""Utility routines for doc strings."""

__author__ = "Daniel Larsson, dlarsson@sw.seisy.abb.se"
__version__ = '$Revision: 1 $'

import string, StructuredText
import regex, regsub, docregex
from ManualPage import UN_LIST, OR_LIST, DEF_LIST

# ------  Some regular expressions used to parse the __doc__ strings  ------

# Find out how many leading tabs/spaces are used in a docstring.
_lead_tab = regex.compile('.*\n+\\( *\\)')


# The Python manuals recommend the following layout of
# docstrings:
# """One-liner describing fun/class/module
# <empty line>
# Longer description of fun/class/module"""
_oneliner 	= regex.compile('.*\(\n *\)+\n')
_oneliner_only	= regex.compile('.*')

def stripleadingtabs(s):
    """Strip leading tabs in the string ~s~.
	
    The leading tabs, such as the one on this line, will be
    stripped by this function for a more readable string"""

    s = StructuredText.untabify(s)
    if _lead_tab.match(s) != -1:
	tabs = _lead_tab.group(1)
	return string.join(string.split(s, '\n'+tabs), '\n')
    else:
	return s

def split_doc(doc):
    """Splits docstring into the oneliner part and the rest,
    assuming there is a oneliner."""

    if not doc: return '', ''

    # Evaluate doc to remove enclosing "'s
    doc = stripleadingtabs(doc)
    count = _oneliner.match(doc)
    if count != -1:
	# Strip away trailing two newlines
	oneliner = _oneliner.group(0)[:-2]

	doc = doc[count:]

    else:
	count = _oneliner_only.match(doc)

	if count == len(doc):
	    oneliner = doc
	    doc = ''
	else:
	    oneliner = ''
    return (oneliner, doc)


class rexpiter:
    def __init__(self, rexp, text):
	if type(rexp) == type(''):
	    rexp = regex.compile(rexp)
	self.rexp = rexp
	self.text = text
    def __getitem__(self, index):
	count = self.rexp.match(self.text)
	if count == -1: raise IndexError # Stop iteration
	self.text = self.text[count:]
	return self.rexp

class rexpitersearch:
    def __init__(self, rexp, text):
	if type(rexp) == type(''):
	    rexp = regex.compile(rexp)
	self.rexp = rexp
	self.text = text
    def __getitem__(self, index):
	count = self.rexp.match(self.text)
	if count == -1: raise IndexError # Stop iteration
	self.text = self.text[count:]
	return self.rexp

class StructuredTextParser:
    def __init__(self, manpage, section, text):
	self.text = text
	self.manpage = manpage
	self.topsection = self.subsection = self.section = section
	self.list = None

    def parse(self):
	paragraphs = regsub.split(self.text, StructuredText.paragraph_divider)
	paragraphs = map(StructuredText.indent_level, paragraphs)

	structure = StructuredText.structure(paragraphs)
	self._walk(structure) # level = 1

    def _walk(self, structure, level=1, in_list=0, pre=0):
	r=''
	listtype = None
	list = []
	for par, sub in structure:

	    # Check for hyperlink definition
	    found = 0
	    for rexp in rexpiter(docregex.hyperdef_regex, par):
		found = 1
		if listtype:
		    self.end_list(listtype, list, in_list)
		    list = []
		listtype = None
		text, url = rexp.group(1,2)
		self.visit_hyperdef(url, text)

	    if found: continue

	    # Check for bullet lists
	    if docregex.bullet_regex.match(par) >= 0:
		first_item = listtype != UN_LIST
		if first_item and listtype:
		    self.end_list(listtype, list, in_list)
		    list = []
		listtype = UN_LIST

		# Allow bullet list without intervening '\n\n'
		l = string.splitfields(par, docregex.bullet_regex.group(1))[1:]
		list = list + l

		sublist = self._walk(sub, level+1, in_list=1)
		if sublist[0]:
		    list.append(sublist)
		continue

	    # Check for ordered lists
	    elif docregex.ol_regex.match(par) >= 0:
		first_item = listtype != OR_LIST
		if first_item and listtype:
		    self.end_list(listtype, list, in_list)
		    list = []
		listtype = OR_LIST

		text = docregex.ol_regex.group(3)
		list.append(text)
		sublist = self._walk(sub, level+1, in_list=1)
		if sublist[0]:
		    list.append(sublist)

	    # Check for ordered lists (alternative syntax)
	    elif docregex.olp_regex.match(par) >= 0:
		first_item = listtype != OR_LIST
		if first_item and listtype:
		    self.end_list(listtype, list, in_list)
		    list = []
		listtype = OR_LIST

		text = docregex.olp_regex.group(3)
		list.append(text)
		sublist = self._walk(sub, level+1, in_list=1)
		if sublist[0]:
		    list.append(sublist)

	    # Check for definition lists
	    elif docregex.dl_regex.match(par) >= 0:
		t,d = docregex.dl_regex.group(1,2)
		first_item = listtype != DEF_LIST
		if first_item and listtype:
		    self.end_list(listtype, list, in_list)
		    list = []
		listtype = DEF_LIST
		#
		count = docregex.dl_regex.search(d)
		while count >= 0:
		    list.append((t, d[:count-1]))
		    t,d = docregex.dl_regex.group(1,2)
		    count = docregex.dl_regex.search(d)
		    
		list.append((t,d))
		sublist = self._walk(sub, level+1, in_list=1)
		if sublist[0]:
		    list.append(sublist)

	    # Check for 'example' headings
	    elif docregex.example_regex.search(par) >= 0 and sub:
		if listtype:
		    self.end_list(listtype, list, in_list)
		    list = []
		listtype = None
		self.visit_head(par, level)
		self._walk(sub, level+1, in_list, pre=1)

	    # Check for standard headings
	    elif docregex.nl_regex.search(par) < 0 and sub:
		# Treat as a heading
		if listtype:
		    self.end_list(listtype, list, in_list)
		    list = []
		listtype = None
		self.visit_head(par, level)
		self._walk(sub, level+1, in_list, pre)

	    else:
		if listtype:
		    self.end_list(listtype, list, in_list)
		    list = []
		listtype = None
		if pre:
		    self.visit_pre(par, level)
		else:
		    self.visit_normal(par, level)
		self._walk(sub, level+1, in_list)

	if listtype:
	    self.end_list(listtype, list, in_list)
	return listtype, list

    def end_list(self, listtype, list, in_list):
	if not in_list:
	    self.manpage.list(listtype, list[:], self.subsection)

    def visit_head(self, heading, level):
	if level > 1:
	    self.subsection = self.manpage.section(heading, self.section)
	else:
	    self.section = self.manpage.section(heading, self.topsection)
	    self.subsection = self.section
	
    def visit_hyperdef(self, url, text):
	self.manpage.add_hyperlink(url, text)

    def visit_normal(self, text, level):
	self.manpage.paragraph(text, self.subsection)

    def visit_pre(self, text, level):
	self.manpage.code(text, self.subsection)

	
def docregex_parse(manpage, section, doc):
    """Parse doc for docregex markup.

    Looks for markups in *doc*, and inserts the
    appropriate paragraphs/headings, etc, into *section*
    of *manpage*."""
    
    StructuredTextParser(manpage, section, doc).parse()
    #print manpage._doctree_
