merged 2.2 branch

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@7748 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Bryan Petty
2000-07-15 19:51:35 +00:00
parent 8a693e6e04
commit f6bcfd974e
1835 changed files with 237729 additions and 67990 deletions

View File

@@ -0,0 +1,4 @@
__init__.pyc
editor.pyc
py_editor.pyc
tokenizer.pyc

View File

@@ -0,0 +1,10 @@
PLEASE NOTE: This is experimental code. It needs an overhall in the
drawing and update code, and there is occasionally a
mysteriously disappearing line...
I am working on a StyledTextEditor that will likely
render this editor obsolete... But this one is at
least somewhat functional now while the other is still
vapor.
- Robin

View File

@@ -0,0 +1,18 @@
#----------------------------------------------------------------------
# Name: wxPython.lib.editor
# Purpose: A package containing a colourizable text editror
#
# Author: Robin Dunn
#
# Created: 30-Dec-1999
# RCS-ID: $Id$
# Copyright: (c) 1999 by Total Control Software
# Licence: wxWindows license
#----------------------------------------------------------------------
# This file makes this directory into a Python package
# import the main classes into the package namespace.
from editor import wxEditor
from py_editor import wxPyEditor

View File

@@ -0,0 +1,670 @@
#----------------------------------------------------------------------
# Name: wxPython.lib.editor.wxEditor
# Purpose: An intelligent text editor with colorization capabilities.
#
# Author: Dirk Holtwic, Robin Dunn
#
# Created: 15-Dec-1999
# RCS-ID: $Id$
# Copyright: (c) 1999 by Dirk Holtwick, 1999
# Licence: wxWindows license
#----------------------------------------------------------------------
# PLEASE NOTE: This is experimental code. It needs an overhall in the
# drawing and update code, and there is occasionally a
# mysteriously disappearing line...
#
# I am working on a StyledTextEditor that will likely
# render this editor obsolete... But this one is at
# least somewhat functional now while the other is still
# vapor.
#
# - Robin
from wxPython.wx import *
from string import *
from keyword import *
from regsub import *
from tokenizer import *
#---------------------------------------------------------------------------
class Line:
def __init__(self, text=""):
self.text = text # the string itself
self.syntax = [] # the colors of the line
self.editable = true # edit?
self.visible = 0 # will be incremented if not
self.indent = 0 # not used yet
#----------------------------------------------------------------------
class wxEditor(wxScrolledWindow):
def __init__(self, parent, id,
pos=wxDefaultPosition, size=wxDefaultSize, style=0):
###############################################################
"""
Alles hat einen Anfang
"""
wxScrolledWindow.__init__(self, parent, id,
pos, size,
style|wxWANTS_CHARS)
# the syntax informations, if they don't exist,
# all syntax stuff will be ignored
# cursor pos
self.cx = 0
self.cy = 0
# the lines that are visible
self.lines = []
self.line = 0
self.len = 0
self.ocy = 0
# border pos
#self.bx = 0
#self.by = 0
# screen
self.sx = 0
self.sy = 0
self.sw = 0
self.sh = 0
self.osx= 0
self.osy= 0
# font
dc = wxClientDC(self)
if wxPlatform == "__WXMSW__":
self.font = wxFont(10, wxMODERN, wxNORMAL, wxNORMAL)
else:
self.font = wxFont(12, wxMODERN, wxNORMAL, wxNORMAL, false)
dc.SetFont(self.font)
# font weight, height
self.fw = dc.GetCharWidth()
self.fh = dc.GetCharHeight()
# back, for colour
self.bcol = wxNamedColour('white')
self.fcol = wxNamedColour('black')
self.cfcol = wxNamedColour('black')
self.cbcol = wxNamedColour('red')
# nicht edierbare zeile (hintergrund)
self.nedcol = wxNamedColour('grey')
self.SetBackgroundColour(self.bcol)
#dc.SetForegroundColour(self.fcol)
# events
EVT_LEFT_DOWN(self, self.OnMouseClick)
EVT_RIGHT_DOWN(self, self.OnMouseClick)
EVT_SCROLLWIN(self, self.OnScroll)
EVT_CHAR(self, self.OnChar)
EVT_PAINT(self, self.OnPaint)
self.o_cx = self.cx
self.o_cy = self.cy
self.o_sx = self.sx
self.o_sy = self.sy
self.o_line = self.line
self.sco_x = 0
self.sco_y = 0
self.tabsize = 4
self.update = true
self.in_scroll =FALSE
self.inUpdate = FALSE
bw,bh = self.GetSizeTuple()
# double buffering
self.mdc = wxMemoryDC()
self.mdc.SelectObject(wxEmptyBitmap(bw,bh))
# disable physical scrolling because invisible parts are not drawn
self.EnableScrolling(FALSE, FALSE)
# the ordinary text as it is
self.SetText()
self.SetFocus()
#---------------------------------------------------------------------------
def CalcLines(self):
###############################################################
self.lines = []
x =maxlen =0
for line in self.text:
if line.visible==0:
self.lines.append(x)
else:
if len(line.text) >maxlen:
maxlen =len(line.text)
x = x + 1
self.len = len(self.lines)
self.max_linelength =maxlen
def SetFontTab(self, fonttab):
###############################################################
""" Fonttabelle zum schnellen Zugriff """
self.ftab = fonttab
def SetText(self, text = [""]):
###############################################################
""" Text mittels Liste setzen """
self.cx = 0
self.cy = 0
self.text = []
for t in text:
self.text.append(Line(t))
for l in range(0,len(text)-1):
#self.UpdateSyntax(l)
self.OnUpdateHighlight(l)
self.OnInit()
self.update = true
self.UpdateView(None, true)
# show new text
def GetText(self):
###############################################################
""" Der gesamte Text als Liste """
text = []
for line in self.text:
text.append(line.text)
return text
def IsEmpty(self):
###############################################################
"""see if at least one text line is not empty"""
for line in self.text:
if line.text: return 0
return 1
def IsLine(self, line):
###############################################################
""" Schauen, ob alles im gr<67>nen Bereich ist """
return (line>=0) and (line<self.len)
def IsEditable(self, line):
###############################################################
return self.text[self.GetLine(line)].editable
def GetLine(self, line):
###############################################################
return self.lines[line]
def GetTextLine(self, line):
###############################################################
""" Text holen """
if self.IsLine(line):
return self.text[self.GetLine(line)].text
return ""
def SetTextLine(self, line, text):
###############################################################
""" Nur den Text <20>ndern """
if self.IsLine(line):
l = self.GetLine(line)
self.text[l].text = text
#self.UpdateSyntax(l)
self.OnUpdateHighlight(l)
self.update = true
#---------------------------------------------------------------------------
def OnMouseClick(self, event):
###############################################################
"""
Wenn es Click gemacht hat => Cursor setzen
"""
self.SetFocus()
self.cy = self.sy + (event.GetY() / self.fh)
if self.cy >= self.len: self.cy =max(self.len -1, 0)
linelen =len(self.text[self.GetLine(self.cy)].text)
self.cx = self.sx + (event.GetX() / self.fw)
# allow positioning right behind the last character
if self.cx > linelen: self.cx =linelen
if event.GetEventType() ==wxEVT_RIGHT_DOWN:
self.update = true
self.OnFold()
self.UpdateView()
def DrawCursor(self, dc = None):
###############################################################
"""
Auch der Cursor mu<6D> ja irgendwie gezeichnet werden
"""
if not dc:
dc = wxClientDC(self)
if (self.len)<self.cy: #-1 ?
self.cy = self.len-1
s = self.text[self.GetLine(self.cy)].text
x = self.cx - self.sx
y = self.cy - self.sy
self.DrawSimpleCursor(x, y, dc)
def DrawSimpleCursor(self, xp, yp, dc = None, old=false):
###############################################################
"""
Auch der Cursor mu<6D> ja irgendwie gezeichnet werden
"""
if not dc:
dc = wxClientDC(self)
if old:
xp = self.sco_x
yp = self.sco_y
szx = self.fw
szy = self.fh
x = xp * szx
y = yp * szy
dc.Blit(x,y,szx,szy,dc,x,y,wxSRC_INVERT)
self.sco_x = xp
self.sco_y = yp
def OnScroll(self, event):
dir =event.GetOrientation()
evt =event.GetEventType()
if dir ==wxHORIZONTAL:
if evt ==wxEVT_SCROLLWIN_LINEUP: self.sx =self.sx -1
elif evt ==wxEVT_SCROLLWIN_LINEDOWN: self.sx =self.sx +1
elif evt ==wxEVT_SCROLLWIN_PAGEUP: self.sx =self.sx -self.sw
elif evt ==wxEVT_SCROLLWIN_PAGEDOWN: self.sx =self.sx +self.sw
elif evt ==wxEVT_SCROLLWIN_TOP: self.sx =self.cx =0
elif evt ==wxEVT_SCROLLWIN_BOTTOM:
self.sx =self.max_linelength -self.sw
self.cx =self.max_linelength
else:
self.sx =event.GetPosition()
if self.sx >(self.max_linelength -self.sw +1):
self.sx =self.max_linelength -self.sw +1
if self.sx <0: self.sx =0
if self.cx >(self.sx +self.sw -1): self.cx =self.sx +self.sw -1
if self.cx <self.sx: self.cx =self.sx
else:
if evt ==wxEVT_SCROLLWIN_LINEUP: self.sy =self.sy -1
elif evt ==wxEVT_SCROLLWIN_LINEDOWN: self.sy =self.sy +1
elif evt ==wxEVT_SCROLLWIN_PAGEUP: self.sy =self.sy -self.sh
elif evt ==wxEVT_SCROLLWIN_PAGEDOWN: self.sy =self.sy +self.sh
elif evt ==wxEVT_SCROLLWIN_TOP: self.sy =self.cy =0
elif evt ==wxEVT_SCROLLWIN_BOTTOM:
self.sy =self.len -self.sh
self.cy =self.len
else:
self.sy =event.GetPosition()
if self.sy >(self.len -self.sh +1):
self.sy =self.len -self.sh +1
if self.sy <0: self.sy =0
if self.cy >(self.sy +self.sh -1): self.cy =self.sy +self.sh -1
if self.cy <self.sy: self.cy =self.sy
self.UpdateView()
def AdjustScrollbars(self):
# there appears to be endless recursion:
# SetScrollbars issue EvtPaint which calls UpdateView
# which calls AdjustScrollbars
if not self.in_scroll:
self.in_scroll =TRUE
self.SetScrollbars(self.fw, self.fh, self.max_linelength +1,
# it seem to be a bug in scrollbars:
# the scrollbar is hidden
# even if current position >0
max(self.len +1, self.sy +self.sh),
self.sx, self.sy)
self.osx, self.osy = self.sx, self.sy
self.in_scroll =FALSE
# adapts the output to what it should be
def UpdateView(self, dc = None, doup=false):
###############################################################
"""
Diese Routine wird immer dann aufgerufen, wenn
sich etwas ver<65>ndert hat
"""
if self.inUpdate:
return
self.inUpdate = true
self.CalcLines()
if not dc:
dc = wxClientDC(self)
self.bw,self.bh = self.GetSizeTuple()
self.sw = self.bw / self.fw
self.sh = self.bh / self.fh
if self.cy<self.sy:
self.sy = self.cy
elif self.cy>(self.sy+self.sh-1):
self.sy = self.cy-self.sh+1
if self.cx<self.sx:
self.sx = self.cx
elif self.cx>(self.sx+self.sw-1):
self.sx = self.cx-self.sw+1
# left line? change syntax!
if self.ocy!=self.cy:
self.OnUpdateSyntax(self.ocy)
self.ocy = self.cy
# alles beim alten
if self.osx != self.sx or self.osy != self.sy:
self.AdjustScrollbars()
self.DrawSimpleCursor(0,0,dc, true)
# [als] i don't really understand how the following condition works
#if self.update or doup:
self.Draw(dc)
# self.update = false
#else:
# self.DrawCursor(dc)
self.o_cx = self.cx
self.o_cy = self.cy
self.o_sx = self.sx
self.o_sy = self.sy
self.o_line = self.line
self.inUpdate = false
def DrawEditText(self, t, x, y, dc = None):
###############################################################
""" Einfache Hilfsroutine um Text zu schreiben
"""
if not dc:
dc = wxClientDC(self)
dc.SetFont(self.font)
dc.DrawText(t, x * self.fw, y * self.fh)
def DrawLine(self, line, dc=None):
###############################################################
"""
Hier wird einfach die Ansicht der ganzen Seite
wiederhergestellt.
!!! Kann modifiziert werden !!!
"""
if not dc:
dc = wxClientDC(self)
dc.SetBackgroundMode(wxSOLID)
dc.SetTextBackground(self.bcol)
dc.SetTextForeground(self.fcol)
#dc.Clear()
# delimiter
ll = self.sx
lr = self.sx + self.sw
y = line - self.sy
# text + syntax
if self.IsLine(line):
l = self.GetLine(line)
t = self.text[l].text
syn = self.text[l].syntax
if not self.text[l].editable:
dc.SetTextBackground(self.nedcol)
else:
dc.SetTextBackground(self.bcol)
dc.SetTextForeground(self.fcol)
pos = ll
for h in syn:
xp, col = h
if xp>=ll:
self.DrawEditText(t[pos:xp], (pos-ll), y, dc)
pos = xp
dc.SetTextForeground(self.ftab[col])
self.DrawEditText(t[pos:], (pos-ll), y, dc)
def Draw(self, odc=None):
###############################################################
"""
Hier wird einfach die Ansicht der ganzen Seite
wiederhergestellt.
!!! Kann modifiziert werden !!!
"""
if not odc:
odc = wxClientDC(self)
dc = self.mdc
dc.SelectObject(wxEmptyBitmap(self.bw,self.bh))
dc.SetBackgroundMode(wxSOLID)
dc.SetTextBackground(self.bcol)
dc.SetTextForeground(self.fcol)
dc.Clear()
for line in range(self.sy, self.sy + self.sh): self.DrawLine(line, dc)
odc.Blit(0,0,self.bw,self.bh,dc,0,0,wxCOPY)
self.DrawCursor(odc)
def cVert(self, num):
###############################################################
""" Vertikale Cursorverschiebung
"""
cy = self.cy + num
if cy <0: cy =0
elif cy >(self.len -1): cy =self.len -1
# scroll when edge hit
if cy >(self.sy +self.sh -1): self.sy =cy -self.sh +1
elif cy <self.sy: self.sy =cy
self.cy =cy
# disallow positioning behind the end of the line
linelen =len(self.text[self.GetLine(cy)].text)
if self.cx >linelen: self.cx =linelen
def cHoriz(self, num):
###############################################################
""" Horizontale Cursorverschiebung
"""
cx = self.cx + num
linelen =len(self.text[self.GetLine(self.cy)].text)
if cx <0: cx =0
elif cx >linelen: cx =linelen
# scroll when edge hit
if cx >(self.sx +self.sw -2): self.sx =cx -self.sw +2
elif cx <self.sx: self.sx =cx
self.cx =cx
def InsertText(self, text):
###############################################################
"""
Simple Routine um Text - auch <20>ber mehrere
Zeilen - einzuf<75>gen
"""
if self.IsEditable(self.cy):
tis = split(text, "\n")
t = self.GetTextLine(self.cy)
if len(tis)==1:
t = t[:self.cx] + text + t[self.cx:]
self.SetTextLine(self.cy, t)
self.cHoriz(len(text))
else:
rest = t[self.cx:]
t = t[:self.cx] + tis[0]
self.SetTextLine(self.cy, t)
for i in range(1,len(tis)):
self.text.insert(self.GetLine(self.cy)+1, Line())
self.lines.insert(self.cy+1,self.GetLine(self.cy)+1)
self.cVert(+1)
self.SetTextLine(self.cy, tis[i])
t = self.GetTextLine(self.cy)
self.cx = len(t)
t = t + rest
self.SetTextLine(self.cy, t)
self.update = true
#self.UpdateView()
#-----------------------------------------------------------------------------------------
def RemoveLine(self, line):
pass
def OnChar(self, event):
###############################################################
"""
Wenn eine Taste gedr<64>ckt wird,
kann an dieser Stelle die Auswertung stattfinden
"""
# get code
key = event.KeyCode()
# if event.ControlDown:
# if chr(key)=="k":
# print "weg"
# movements
if key==WXK_DOWN:
self.cVert(+1)
elif key==WXK_UP:
self.cVert(-1)
elif key==WXK_LEFT:
self.cHoriz(-1)
elif key==WXK_RIGHT:
self.cHoriz(+1)
elif key==WXK_NEXT:
self.cVert(self.sh)
elif key==WXK_PRIOR:
self.cVert(-self.sh)
elif key==WXK_HOME:
self.cx = 0
elif key==WXK_END:
self.cx = len(self.GetTextLine(self.cy))
elif key==WXK_BACK:
t = self.GetTextLine(self.cy)
if self.cx>0:
t = t[:self.cx-1] + t[self.cx:]
self.SetTextLine(self.cy, t)
self.cHoriz(-1)
elif key==WXK_DELETE:
t = self.GetTextLine(self.cy)
if self.cx<len(t):
t = t[:self.cx] + t[self.cx+1:]
self.SetTextLine(self.cy, t)
elif key==WXK_RETURN:
self.InsertText("\n")
elif key==WXK_TAB:
self.OnTabulator(event)
# clipboard (buggy)
elif key==WXK_F10:
if wxTheClipboard.Open():
data = wxTheClipboard.GetData()
wxTheClipboard.Close()
print data
# folding (buggy)
elif key==WXK_F12:
self.update = true
self.OnFold()
# regular ascii
elif (key>31) and (key<256):
self.InsertText(chr(key))
self.UpdateView()
return 0
def OnPaint(self, event):
dc = wxPaintDC(self)
self.bw,self.bh = self.GetSizeTuple()
self.UpdateView(dc, true)
#-----------------------------------------------------------------------------------------
def GetIndent(self, line):
p = 0
for c in line:
if c==" ": p = p + 1
elif c=="\t": p =(p /self.tabsize +1) *self.tabsize
else: break
return p
def Goto(self, pos):
self.cVert(pos-self.cy-1)
self.UpdateView()
# --------------------------------------------------------
# to be overloaded
def OnUpdateHighlight(self, line = -1):
pass
def OnUpdateSyntax(self, line = -1):
pass
def OnTabulator(self, event):
pass
def OnInit(self):
pass
def OnFold(self):
pass

View File

@@ -0,0 +1,211 @@
# (C)opyright by Dirk Holtwick, 1999
# ----------------------------------
# holtwick@spirito.de
# http://www.spirito.de/pyde
from editor import *
from string import *
from keyword import *
from tokenizer import *
"""
This module will be loaded by the main
window. It implements some methods that
are typical for Python sources.
"""
class wxPyEditor(wxEditor):
# ------------------------------------------------------------------
def __init__(self, parent, id,
pos=wxDefaultPosition, size=wxDefaultSize, style=0):
wxEditor.__init__(self, parent, id, pos, size, style)
self.SetFontTab([
wxNamedColour('black'),
wxNamedColour('blue'),
wxNamedColour('red'),
wxNamedColour('darkgreen'),
wxNamedColour('brown')
])
# ------------------------------------------------------------------
def OnUpdateHighlight(self, line = -1):
if line>=0:
t = self.text[line].text
syn = []
toks = Tokenizer(t).tokens()
for type, string, begin, end in toks:
if type == "KEY":
syn.append((begin, 1))
syn.append((end, 0))
elif type == "COMMENT":
syn.append((begin, 2))
elif type == "STRING":
syn.append((begin, 3))
syn.append((end, 0))
elif type == "NUMBER":
syn.append((begin, 4))
syn.append((end, 0))
elif type == "NAME":
if string=="self":
syn.append((begin, 4))
syn.append((end, 0))
else:
pass
self.text[line].syntax = syn
# ------------------------------------------------------------------
def OnUpdateSyntax(self, line = -1):
if line>=0:
"""
tx, syn, m = self.text[line]
pre = 0
for i in range(0,len(tx)):
if tx[i] != " ":
pre = i
break
t = tx[pre:]
t = Tokenizer(t).line()
t = tx[:pre] + t
self.text[line] = t, syn, m
"""
self.OnUpdateHighlight(line)
# ------------------------------------------------------------------
def OnTabulator(self, event):
add = +1
if event.ShiftDown():
add = -1
t = self.GetTextLine(self.cy)
if strip(t):
indent = self.GetIndent(t)
# print indent
t = t[indent:]
tabs = indent / self.tabsize
# for i in range(0,tabs+add):
t = (" " * 4 * (tabs+add)) + t
self.SetTextLine(self.cy, t)
elif add>0:
self.InsertText(" ")
# ------------------------------------------------------------------
def FindQuote(self, lineno, quote_type='"""', direction=1):
"""find line containing the matching quote"""
l =lineno +direction
while (l < len(self.text)-1) and (l >= 0):
if find(self.text[l].text, quote_type) >=0: return l
l =l +direction
return None
def FindNextLine(self, lineno, direction=1):
"""get the next line of code (skipping comment lines and empty lines)"""
l =lineno +direction
while (l < len(self.text)-1) and (l >= 0):
str =lstrip(self.text[l].text)
if (len(str) >0) and (str[0] !="#"): return l
l =l +direction
return None
def Fold(self):
l = self.GetLine(self.cy)
line = self.text[l]
t = line.text
# fold ...
if line.editable:
# 3*quotes
qpos =find(t, '"""')
if qpos >=0: qtype ='"""'
else:
qpos =find(t, "'''")
if qpos >=0: qtype ="'''"
if (qpos >=0) and (find(t[qpos+3:], qtype) <0):
closing_quote =self.FindQuote(l, qtype)
if closing_quote !=None:
line.editable = not line.editable
l =l +1
while l <= closing_quote:
self.text[l].visible =self.text[l].visible +1
l =l +1
else: # try normal fold on leading whitespace
lim = self.GetIndent(t)
lnext =self.FindNextLine(l)
if (lnext !=None) \
and (self.GetIndent(self.text[lnext].text) >lim):
line.editable =FALSE
lstart =l +1
l =self.FindNextLine(l)
while (l !=None) \
and (self.GetIndent(self.text[l].text) >lim):
l =self.FindNextLine(l)
if l ==None:
# fold till the end
l =len(self.text)
for line in self.text[lstart:l]:
line.visible =line.visible +1
# ... or unfold
else:
lim = line.visible + 1
line.editable = not line.editable
l = l + 1
line = self.text[l]
while (l < (len(self.text) -1)) and (line.visible>=lim):
line.visible = line.visible - 1
l = l + 1
line = self.text[l]
def FoldAll(self):
self.CalcLines()
self.cx = 0
self.cy = len(self.lines) - 1
prev_indent =0
# following loop is exited in two cases:
# when self.cy becomes 0 (topmost level is not folded by FoldAll)
# or when FindNextLine() returns None (all remaining lines till
# the beginning of the text are empty or comments)
while self.cy:
t = self.GetTextLine(self.cy)
# indent-based folding
indent =self.GetIndent(t)
if indent <prev_indent:
self.Fold()
prev_indent =indent
# triple-quote folding
qpos =find(t, '"""')
if qpos >=0: qtype ='"""'
else:
qpos =find(t, "'''")
if qpos >=0: qtype ="'''"
if (qpos >=0) and (find(t[qpos+3:], qtype) <0):
closing_quote =self.FindQuote(self.cy, qtype, -1)
if closing_quote !=None:
# XXX potential bug: unmatched triple quotes
self.cy =closing_quote
self.Fold()
self.cy =self.FindNextLine(self.cy, -1)
if self.cy ==None: self.cy =0
# ------------------------------------------------------------------
def OnFold(self):
self.Fold()
# ------------------------------------------------------------------
def OnInit(self):
#self.FoldAll()
pass

View File

@@ -0,0 +1,60 @@
from tokenize import *
from keyword import *
from string import *
class Tokenizer:
"""
Simple class to create a list of token-tuples like:
(type, string, first, last)
Example:
t = Tokenizer('def hallo(du): # juchee')
print t.tokens()
"""
def __init__(self, text):
self.text = text
self.toks = []
try:
tokenize(self.readline, self.get)
except TokenError:
pass
def tokens(self):
return self.toks
def get(self, type, string, begin, end, l):
#print begin,end
h1, b = begin
h2, e = end
tname = tok_name[type]
if iskeyword(string):
tname = "KEY"
self.toks.append(tname, string, b, e)
def readline(self):
t = self.text
self.text = ""
return t
def line(self):
pre = ""
out = ""
for type, string, begin, end in self.toks:
if (pre in ["NAME","KEY"]) and (not string in [".",",","("]):
out = out + " "
if type in ["NAME","KEY"]:
out = out + string
elif type=="OP":
if string in [",",":"]:
out = out + string + " "
else:
out = out + string
else:
out = out + string
pre = type
return out