Login | Register
My pages Projects Community openCollabNet

Discussions > cvs > CVS update: /leo/plugins/

leo
Discussion topic

Back to topic list

CVS update: /leo/plugins/

Author bwmulder
Full name Bernhard Mulder
Date 2006-05-03 09:00:05 PDT
Message User: bwmulder
Date: 2006-05-03 09:00:05-0700
Added:
   leo/plugins/mod_shadow_core.py

Modified:
   leo/plugins/mod_shadow.py

Log:
 Fixed some long standing issue with picking up insertion.
 Split the plugin into two parts: the algorithmic core, and the
 embedding into Leo. This makes testing the algorithms easier.
 Changed the embedding so that trying to go to an non-existing line
 will put up an alert, instead of producing an internal error.

File Changes:

Directory: /leo/plugins/
========================

File [changed]: mod_shadow.py
Url: http://leo.tigris.or​g/source/browse/leo/​plugins/mod_shadow.p​y?r1=1.1&r2=1.2
Delta lines: +462 -1066
------------------------
--- mod_shadow.py 2006-04-09 14:56:41-0700 1.1
+++ mod_shadow.py 2006-05-03 09:00:03-0700 1.2
@@ -1,9 +1,9 @@
 #@+leo-ver=4-thin
-#@+node:bwmulder.20​041017125718:@thin mod_shadow.py
-# Warning: this code has not been heavily tested.
+#@+node:bwmulder.20​060418131356.17:@thi​n mod_shadow.py
+# Some unit tests for the core file exist.
 
 #@<<docstring>>
-#@+node:bwmulder.20​041017125718.1:<​< docstring >>
+#@+node:bwmulder.20​060418131356.18:<​< docstring >>
 """
 Use a subfolder for files with Leo comments.
 
@@ -48,7 +48,7 @@
 does not pick up test scripts twice (once the file without Leo sentinels, once the
 shadow file).
 """
-#@-node:bwmulder.20​041017125718.1:<​< docstring >>
+#@-node:bwmulder.20​060418131356.18:<​< docstring >>
 #@nl
 
 # Terminology:
@@ -60,7 +60,7 @@
 
 __version__ = "0.9"
 #@<< version history >>
-#@+node:ekr.2004111​0090700:<< version history >>
+#@+node:bwmulder.20​060418131356.19:<​< version history >>
 #@@killcolor
 #@+at
 #
@@ -79,34 +79,27 @@
 # - fixed assertion in original_replaceTarg​etFileIfDifferent
 #
 # 0.9.1 Added prefix option.
+#
+# 0.9.2 Split up the most complicated functions into two:
+# propagate_changes_fr​om_file_without_sent​inels_to_file_with_s​entinels
+# operates on files.
+# propagate_changes_fr​om_lines_without_sen​tinels_to_lines_with​_sentinels
+# operates on lines.
+# The latter can be tested without files, and outside Leo.
+#
 #@-at
-#@nonl
-#@-node:ekr.2004111​0090700:<< version history >>
+#@-node:bwmulder.20​060418131356.19:<​< version history >>
 #@nl
 #@<< globals >>
-#@+node:ekr.2004111​0091737:<< globals >>
-testing = True
-
-print_copy_operations = True # True: tell when files are copied.
-
-do_backups = False # True: always make backups of each file.
-
-shadow_subdir = "Leo" # subdirectory for shadow files.
-
-prefix = "" # Prefix to be used for shadow files, if any.
-
-active = True # The plugin can be switched off by this switch
-
-verbosity = 1
-
-__version__ = "$Rev: 1765 $"
+#@+node:bwmulder.20​060418131356.20:<​< globals >>
+__version__ = "$Rev: 2927 $"
 __author__ = "Bernhard Mulder"
-__date__ = "$Date: 2006/04/09 21:56:41 $"
-__cvsid__ = "$Id: mod_shadow.py,v 1.1 2006/04/09 21:56:41 edream Exp $"
-#@-node:ekr.2004111​0091737:<< globals >>
+__date__ = "$Date: 2006/05/03 16:00:03 $"
+__cvsid__ = "$Id: mod_shadow.py,v 1.2 2006/05/03 16:00:03 bwmulder Exp $"
+#@-node:bwmulder.20​060418131356.20:<​< globals >>
 #@nl
 #@<< Notes >>
-#@+node:bwmulder.20​041231171842:<​< Notes >>
+#@+node:bwmulder.20​060418131356.21:<​< Notes >>
 #@+doc
 # 1. Not sure if I should do something about read-only files. Are they a
 # problem? Should I check for them?
@@ -127,11 +120,43 @@
 # 5. Introduced a new function "applyLineNumberMappingIfAny" in
 # gotoLineNumber. The default implementation returns the argument.
 #@-doc
-#@-node:bwmulder.20​041231171842:<​< Notes >>
+#@-node:bwmulder.20​060418131356.21:<​< Notes >>
+#@nl
+#@<< Implementation Notes >>
+#@+node:bwmulder.20​060418131356.22:<​< Implementation notes >>
+#@+doc
+# The plugin deals with two set of files:
+# 1. The sourcefiles as seen by Leo.
+# 2. The sourcefiles as seen by the user.
+# The sourcefiles as seen by Leo live in a subdirectory,
+# the sourcefiles as seen by the user live in the regular directory.
+#
+# When a file is first read by Leo, we have to create a file without
+# sentinels in the regular directory.
+#
+# That's all fairly easy, once the places within Leo have been identified
+# where
+# the changes have to be made.
+#
+# The slightly hard thing to do is to pick up changes from the file without
+# sentinels, and put them into the file with sentinels.
+#
+# We have two invariants:
+# 1. We NEVER delete any sentinels.
+# 2. As a slighly weaker condition, insertions which could be put either
+# at the end of a node, or the beginning of the next node, should be
+# put
+# at the end of the node.
+# The exception to this rule is the last node. Insertions should
+# *always*
+# be done within sentinels.
+#
+#@-doc
+#@-node:bwmulder.20​060418131356.22:<​< Implementation notes >>
 #@nl
    
 #@+others
-#@+node:bwmulder.20​041017130018:imports​
+#@+node:bwmulder.20​060418131356.23:impo​rts
 import leoGlobals as g
 
 import leoAtFile
@@ -140,680 +165,16 @@
 import leoPlugins
 
 import ConfigParser
-import difflib
 import os
 import shutil
-#@nonl
-#@-node:bwmulder.20​041017130018:imports​
-#@+node:bwmulder.20​041018233934.1:plugi​n core
-#@+node:bwmulder.20​041017130118:auxilar​y functions
-#@+node:bwmulder.20​041017125718.4:copy_​time
-def copy_time (sourcefilename,targ​etfilename):
- """
- Set the modification time of the targetfile the same
- as the sourcefilename
- """
- st = os.stat(sourcefilename)
- if hasattr(os,'utime'):
- os.utime(targetfilen​ame,(st.st_atime,st.​st_mtime))
- elif hasattr(os,'mtime'):
- os.mtime(targetfilen​ame,st.st_mtime)
- else:
- assert 0, "Sync operation can't work if no modification time can be set"
-#@-node:bwmulder.20​041017125718.4:copy_​time
-#@+node:bwmulder.20​041017125718.5:marke​r_from_extension
-def marker_from_extension (filename):
- """
- Tries to guess the sentinel leadin
- comment from the filename extension.
-
- This code should probably be shared
- with the main Leo code.
- """
- root, ext = os.path.splitext(filename)
- if ext=='.tmp':
- root, ext = os.path.splitext(root)
- if ext in('.h','.c'):
- marker = "//@"
- elif ext in(".py",".cfg",".ba​t",".ksh",".txt"):
- marker = '#'+'@'
- else:
- assert 0, "extension %s not handled by this plugin"%ext
- return marker
-#@-node:bwmulder.20​041017125718.5:marke​r_from_extension
-#@+node:bwmulder.20​041017125718.3:write​_if_changed
-def write_if_changed (lines,sourcefilenam​e,targetfilename):
- """
-
- Checks if 'lines' matches the contents of
- 'targetfilename'. Refreshes the targetfile with 'lines' if not.
-
- Produces a message, if wanted, about the overrite, and optionally
- keeps the overwritten file with a backup name.
-
- """
- if not os.path.exists(targetfilename):
- copy = True
- else:
- copy = lines!=file(targetfi​lename).readlines()
- if copy:
- if print_copy_operations:
- print "Copying ", sourcefilename, " to ", targetfilename, " without sentinals"
-
- if do_backups:
- # Keep the old file around while we are debugging this script
- if os.path.exists(targetfilename):
- count = 0
- backupname = "%s.~%s~"%(targetfil​ename,count)
- while os.path.exists(backupname):
- count+=1
- backupname = "%s.~%s~"%(targetfil​ename,count)
- os.rename(targetfile​name,backupname)
- if print_copy_operations:
- print "backup file in ", backupname
- outfile = open(targetfilename,"w")
- for line in lines:
- outfile.write(line)
- outfile.close()
- copy_time(sourcefile​name,targetfilename)​
- return copy
-#@-node:bwmulder.20​041017125718.3:write​_if_changed
-#@+node:bwmulder.20​041017125718.2:is_se​ntinel
-def is_sentinel (line,marker):
- """
- Check if line starts with a Leo marker.
-
- Leo markers are filtered away by this script.
-
- Leo markers start with a comment character, which dependends
- on the language used. That's why the marker is passed in.
- """
- return line.lstrip().starts​with(marker)
-#@-node:bwmulder.20​041017125718.2:is_se​ntinel
-#@+node:bwmulder.20​041017125718.6:class​ sourcereader
-
-
-# The following classes have a very limited functionality. They help write
-# code
-# which processes a list of lines slightly more succinctly.
-#
-# You might consider expanding the code inline.
-
-class sourcereader:
- """
- A simple class to read lines sequentially.
-
- The class keeps an internal index, so that each
- call to get returns the next line.
-
- Index returns the internal index, and sync
- advances the index to the the desired line.
-
- The index is the *next* line to be returned.
-
- The line numbering starts from 0.
-
- """
- #@ @+others
- #@+node:bwmulder.200​41017125718.7:__init​__
- def __init__ (self,lines):
- self.lines = lines
- self.length = len(self.lines)
- self.i = 0
- #@-node:bwmulder.200​41017125718.7:__init​__
- #@+node:bwmulder.200​41017125718.8:index
- def index (self):
- return self.i
- #@-node:bwmulder.200​41017125718.8:index
- #@+node:bwmulder.200​41017125718.9:get
- def get (self):
- result = self.lines[self.i]
- self.i+=1
- return result
- #@-node:bwmulder.200​41017125718.9:get
- #@+node:bwmulder.200​41017125718.10:sync
- def sync (self,i):
- self.i = i
- #@-node:bwmulder.200​41017125718.10:sync
- #@+node:bwmulder.200​41017125718.11:size
- def size (self):
- return self.length
- #@-node:bwmulder.200​41017125718.11:size
- #@+node:bwmulder.200​41017125718.12:atEnd​
- def atEnd (self):
- return self.index>=self.length
- #@-node:bwmulder.200​41017125718.12:atEnd​
- #@+node:bwmulder.200​50102143357:clone
- def clone(self):
- sr = sourcereader(self.lines)
- sr.i = self.i
- return sr
- #@nonl
- #@-node:bwmulder.200​50102143357:clone
- #@-others
-#@-node:bwmulder.20​041017125718.6:class​ sourcereader
-#@+node:bwmulder.20​041017125718.13:clas​s sourcewriter
-class sourcewriter:
- """
- Convenience class to capture output to a file.
- """
- #@ @+others
- #@+node:bwmulder.200​41017125718.14:__ini​t__
- def __init__ (self):
- self.i = 0
- self.lines =[]
- #@-node:bwmulder.200​41017125718.14:__ini​t__
- #@+node:bwmulder.200​41017125718.15:push
- def push (self,line):
- self.lines.append(line)
- self.i+=1
- #@-node:bwmulder.200​41017125718.15:push
- #@+node:bwmulder.200​41017125718.16:index​
- def index (self):
- return self.i
- #@-node:bwmulder.200​41017125718.16:index​
- #@+node:bwmulder.200​41017125718.17:getli​nes
- def getlines (self):
- return self.lines
- #@-node:bwmulder.200​41017125718.17:getli​nes
- #@-others
-#@-node:bwmulder.20​041017125718.13:clas​s sourcewriter
-#@+node:bwmulder.20​041017125718.18:push​_file
-# The following functions filter out Leo comments.
-#
-# Push file makes sure that the target file is only touched if there are real
-# differences.
-def push_file (sourcefilename,targ​etfilename):
- outlines, sentinel_lines = push_filter(sourcefilename)
- write_if_changed(out​lines,sourcefilename​,targetfilename)
-#@-node:bwmulder.20​041017125718.18:push​_file
-#@+node:bwmulder.20​041017125718.19:push​_filter
-def push_filter (sourcefilename):
- """
-
- Removes sentinels from the lines of 'sourcefilename'.
-
- """
-
- return push_filter_lines(fi​le(sourcefilename).r​eadlines(),marker_fr​om_extension(sourcef​ilename))
-#@-node:bwmulder.20​041017125718.19:push​_filter
-#@+node:bwmulder.20​041017125718.20:push​_filter_lines
-def push_filter_lines (lines,marker):
- """
-
- Removes sentinels from lines.
-
- """
- result, sentinel_lines =[],[]
- for line in lines:
- if is_sentinel(line,marker):
- sentinel_lines.append(line)
- else:
- result.append(line)
- return result, sentinel_lines
-#@-node:bwmulder.20​041017125718.20:push​_filter_lines
-#@+node:bwmulder.20​041017125718.30:push​_filter_mapping
-def push_filter_mapping (filelines,marker):
- """
- Given the lines of a file, filter out all
- Leo sentinels, and return a mapping:
-
- stripped file -> original file
-
- Filtering should be the same as
- push_filter_lines
- """
-
- mapping =[None]
- for linecount, line in enumerate(filelines):
- if not is_sentinel(line,marker):
- mapping.append(linecount+1)
- return mapping
-#@-node:bwmulder.20​041017125718.30:push​_filter_mapping
-#@+node:bwmulder.20​041017125718.35:clas​s writeclass
-
-
-
-class writeclass(file):
- """
- Small class to remove the sentinels from the Leo
- file and write the result back to the derived file.
- This happens at the close operation of the file.
- """
- #@ @+others
- #@+node:bwmulder.200​41017125718.36:__ini​t__
- def __init__ (self,leofilename,filename):
- self.leo_originalname = filename
- self.leo_filename = leofilename
- file.__init__(self,l​eofilename,'wb')
- #@-node:bwmulder.200​41017125718.36:__ini​t__
- #@+node:bwmulder.200​41017125718.37:close​
- def close (self):
- file.close(self)
- leo_filename, leo_originalname = self.leo_filename, self.leo_originalname
- assert leo_filename.endswith(".tmp")
- shutil.copy2(leo_fil​ename,leo_filename[:​-4])
- # we update regular file in the Leo subdirectory, otherwise structural changes get lost.
- push_file(sourcefile​name=self.leo_filena​me,targetfilename=se​lf.leo_originalname)​
- os.unlink(leo_filename)
- #@-node:bwmulder.200​41017125718.37:close​
- #@-others
-#@-node:bwmulder.20​041017125718.35:clas​s writeclass
-#@-node:bwmulder.20​041017130118:auxilar​y functions
-#@+node:bwmulder.20​041017125718.21:clas​s sentinel_squasher
-# The pull operation is more complicated than the pull operation: we must copy
-# back the sources into a Leo files, making sure that the code is in the
-# proper places between the Leo comments.
-
-class sentinel_squasher:
- """
- The heart of the script.
+import sys
     
- Creates files without sentinels from files with sentinels.
-
- Propagates changes in the files without sentinels back
- to the files with sentinels.
-
- """
- #@ @+others
- #@+node:bwmulder.200​41017125718.22:check​_lines_for_equality
- def check_lines_for_equality (self,lines1,lines2,​message,lines1_messa​ge,lines2_message):
- """
- Little helper function to get nice output if something goes wrong.
- """
- if lines1==lines2:
- return
- print "===================​=============="
- print message
- print "===================​=============="
- print lines1_message
- print "-------------------​--------------"
- f1 = file("mod_shadow.tmp1", "w")
- for line in lines1:
- print line,
- f1.write(line)
- f1.close()
- print "===================​==============="
- print lines2_message
- print "-------------------​--------------"
- f1 = file("mod_shadow.tmp2", "w")
- for line in lines2:
- print line,
- f1.write(line)
- f1.close()
- g.es("'push' did not re-create the output file; please check mod_shadow.tmp1 and mod_shadow.tmp2 for differences")
- #@-node:bwmulder.200​41017125718.22:check​_lines_for_equality
- #@+node:bwmulder.200​41017125718.23:creat​e_back_mapping
- def create_back_mapping (self,sourcelines,marker):
- """
-
- 'sourcelines' is a list of lines of a file with sentinels.
-
- Creates a new list of lines without sentinels, and keeps a
- mapping which maps each source line in the new list back to its
- original line.
-
- Returns the new list of lines, and the mapping.
-
- To save an if statement later, the mapping is extended by one
- extra element.
-
- """
- mapping, resultlines =[],[]
-
- si, l = 0, len(sourcelines)
- while si<l:
- sline = sourcelines[si]
- if not is_sentinel(sline,marker):
- resultlines.append(sline)
- mapping.append(si)
- si+=1
-
- # for programing convenience, we create an additional mapping entry.
- # This simplifies the programming of the copy_sentinels function below.
- mapping.append(si)
- return resultlines, mapping
- #@-node:bwmulder.200​41017125718.23:creat​e_back_mapping
- #@+node:bwmulder.200​41017125718.24:copy_​sentinels
- def copy_sentinels (self,writer_new_sou​rcefile,reader_leo_f​ile,mapping,startlin​e,endline, sentinels_to_copy):
- """
-
- Sentinels are NEVER deleted by this script. They are changed as
- a result of user actions in the Leo.
-
- If code is replaced, or deleted, then we must make sure that the
- sentinels are still in the Leo file.
-
- Taking lines from reader_leo_file, we copy lines to writer_new_sourcefile,
- if those lines contain sentinels.
-
- We copy all sentinels up to, but not including, mapped[endline].
-
- We copy only the sentinels *after* the current position of reader_leo_file.
-
- We have two options to detect sentinel lines:
- 1. We could detect sentinel lines by examining the lines of the leo file.
- 2. We can check for gaps in the mapping.
-
- Since there is a complication in the detection of sentinels (@verbatim), we
- are choosing the 2. approach. This also avoids duplication of code.
- ???This has to be verified later???
- """
-
- old_mapped_line = mapping[startline]
- unmapped_line = startline+1
-
- sentinels_copied = 0
- while unmapped_line<=endline:
- mapped_line = mapping[unmapped_line]
- if old_mapped_line+1!=mapped_line:
- reader_leo_file.sync​(old_mapped_line+1)
- # There was a gap. This gap must have consisted of sentinels, which have
- # been deleted.
- # Copy those sentinels.
- while reader_leo_file.inde​x()<mapped_line:
- line = reader_leo_file.get()
- if sentinels_to_copy> 0:
- if testing:
- print "Copy sentinel:", line
- writer_new_sourcefil​e.push(line)
- elif sentinels_to_copy == 0:
- self.uncopied_sentin​els.append(line)
- if testing:
- print "Delay Copy sentinels:", line,
- sentinels_to_copy -= 1
- sentinels_copied += 1
- old_mapped_line = mapped_line
- unmapped_line+=1
- reader_leo_file.sync​(mapping[endline])
- return sentinels_copied
-
- #@nonl
- #@-node:bwmulder.200​41017125718.24:copy_​sentinels
- #@+node:bwmulder.200​50102142213.1:copy_a​ll_but_last_sentinel​_block
- def copy_all_but_last_se​ntinel_block (self,writer_new_sou​rcefile,reader_leo_f​ile,mapping,startlin​e,endline):
- """
- Copy all but the last sentinel block.
-
- Put the last sentinel block into self.uncopied_sentinels
- """
- self.uncopied_sentinels = []
- if testing:
- print "copy_all_but_last_s​entinel_block: dry run"
- nr_sentinel_blocks = self.copy_sentinels(​g.nullObject(), reader_leo_file.clone(), mapping, startline, endline, -1)
- if testing:
- print "copy_all_but_last_s​entinel_block: Actual copy",nr_sentinel_blocks -1
- self.copy_sentinels(​writer_new_sourcefil​e, reader_leo_file, mapping, startline, endline, nr_sentinel_blocks -1)
- #@nonl
- #@-node:bwmulder.200​50102142213.1:copy_a​ll_but_last_sentinel​_block
- #@+node:bwmulder.200​50102142213.2:copy_d​elayed_sentinel_bloc​k
- def copy_delayed_sentine​l_block(self, writer_new_sourcefile):
- if testing:
- if self.uncopied_sentinels:
- print "Copying uncopied sentinels"
- for line in self.uncopied_sentinels:
- writer_new_sourcefil​e.push(line)
- self.uncopied_sentinels = []
- #@nonl
- #@-node:bwmulder.200​50102142213.2:copy_d​elayed_sentinel_bloc​k
- #@+node:bwmulder.200​41017125718.25:pull_​source
- def pull_source (self,sourcefile,targetfile):
- """
- Propagate the changes of targetfile back to sourcefile.
- Assume that sourcefile has sentinels, and targetfile does not.
- This is the heart of the script.
- """
- if testing: g.trace(sourcefile, targetfile)
- #@ << init vars >>
- #@+node:ekr.20041110​094810:<< init vars >>
- marker = marker_from_extensio​n(sourcefile)
- sourcelines = file(sourcefile).readlines()
- targetlines = file(targetfile).readlines()
-
- internal_sourcelines, mapping = self.create_back_map​ping(sourcelines,mar​ker)
-
- sm = difflib.SequenceMatc​her(None,internal_so​urcelines,targetline​s)
-
- writer_new_sourcefile = sourcewriter()
- # collects the contents of the new file.
-
- reader_modified_file = sourcereader(targetlines)
- # reader_modified_file contains the changed source code.
- # There are no sentinels in 'targetlines'
-
- reader_internal_file = sourcereader(interna​l_sourcelines)
- # This is the same file as reader_leo_file, without sentinels.
-
- reader_leo_file = sourcereader(sourcelines)
- # This is the file which is currently produced by Leo, with sentinels.
-
- #@+at
- #@nonl
- # We compare the 'targetlines' with 'internal_sourcelines' and
- # propagate
- # the changes back into 'writer_new_sourcefile' while making sure that
- # all sentinels of 'sourcelines' are copied as well.
- #
- # An invariant of the following loop is that all three readers are in
- # sync.
- # In addition, writer_new_sourcefile has accumulated the new file,
- # which
- # is going to replace reader_leo_file.
- #@-at
- #@@c
-
- # Check that all ranges returned by get_opcodes() are contiguous
- i2_internal_old, i2_modified_old = -1,-1
-
- self.uncopied_sentinels = []
- # The copying of the last sentinel block is delayed: if
- # an insert follows, then the insert is done before the sentinel
- # block is copied.
- # This way, the insertion can happen *inside* the sentinels, not outside.
- #@nonl
- #@-node:ekr.20041110​094810:<< init vars >>
- #@nl
- #@ << copy the sentinels at the beginning of the file >>
- #@+node:ekr.20041110​095546:<< copy the sentinels at the beginning of the file >>
- while reader_leo_file.inde​x()<mapping[0]:
- line = reader_leo_file.get()
- writer_new_sourcefil​e.push(line)
- #@nonl
- #@-node:ekr.20041110​095546:<< copy the sentinels at the beginning of the file >>
- #@nl
- for tag, i1_internal_file, i2_internal_file, i1_modified_file, i2_modified_file in sm.get_opcodes():
- #@ << trace the params >>
- #@+node:ekr.20041110​094555:<< trace the params >>
- if testing:
- print "tag:", tag,\
- "i1, i2 (internal file):", i1_internal_file, i2_internal_file,\
- "i1, i2 (modified file)", i1_modified_file, i2_modified_file
- #@nonl
- #@-node:ekr.20041110​094555:<< trace the params >>
- #@nl
- #@ << check loop invariants >>
- #@+node:ekr.20041110​094555.1:<< check loop invariants >>
- # We need the ranges returned by get_opcodes to completely cover the source lines being compared.
- # We also need the ranges not to overlap.
- if i2_internal_old!=-1:
- assert i2_internal_old==i1_​internal_file
- assert i2_modified_old==i1_​modified_file
- i2_internal_old, i2_modified_old = i2_internal_file, i2_modified_file
- #@+at
- # Loosely speaking, the loop invariant is that we have processed
- # everything up to,
- # but not including, the lower bound of the ranges returned by the
- # iterator.
- #
- # We have to check the three readers, reader_internal_file,
- # reader_modified_file, and reader_leo_file.
- #
- # For the writer, the filter must reproduce the modified file
- # up until, but not including, i1_modified_file.
- #
- # In addition, all the sentinels of the original Leo file, up
- # until
- # mapping[i1_internal_file], must be present in the
- # new_source_file.
- #@-at
- #@@c
-
- # Check the loop invariant.
- assert reader_internal_file​.i==i1_internal_file​
- assert reader_modified_file​.i==i1_modified_file​
- assert reader_leo_file.i==m​apping[i1_internal_f​ile]
-
- # These checks are a little bit costly.
- if testing and 0:
- # Must check if that still holds.
- t_sourcelines, t_sentinel_lines = push_filter_lines(wr​iter_new_sourcefile.​lines,marker)
- # Check that we have all the modifications so far.
- assert t_sourcelines==reade​r_modified_file.line​s[:i1_modified_file]​
- # Check that we kept all sentinels so far.
- assert t_sentinel_lines==pu​sh_filter_lines(read​er_leo_file.lines[:r​eader_leo_file.i],ma​rker)[1]
- #@nonl
- #@-node:ekr.20041110​094555.1:<< check loop invariants >>
- #@nl
- if tag=='equal':
- #@ << handle 'equal' op >>
- #@+node:ekr.20041110​095546.1:<< handle 'equal' op >>
- # nothing is to be done. Leave the Leo file alone.
-
- self.copy_delayed_se​ntinel_block(writer_​new_sourcefile)
- # Copy the lines from the leo file to the new sourcefile.
- # This loop copies both text and sentinels.
- while reader_leo_file.inde​x()<=mapping[i2_i​nternal_file-1]:
- line = reader_leo_file.get()
- if testing:
- print "Equal: copying ", line,
- writer_new_sourcefil​e.push(line)
-
- if testing:
- print "Equal: syncing internal file from ", reader_internal_file.i, " to ", i2_internal_file
- print "Equal: syncing modified file from ", reader_modified_file.i, " to ", i2_modified_file
- reader_internal_file​.sync(i2_internal_fi​le)
- reader_modified_file​.sync(i2_modified_fi​le)
-
- # now we must copy the sentinels which might follow the lines which were equal.
- self.copy_all_but_la​st_sentinel_block(wr​iter_new_sourcefile,​reader_leo_file,mapp​ing,i2_internal_file​-1,i2_internal_file)​
- #@nonl
- #@-node:ekr.20041110​095546.1:<< handle 'equal' op >>
- #@nl
- elif tag=='replace':
- #@ << handle 'replace' op >>
- #@+node:ekr.20041110​095546.2:<< handle 'replace' op >>
- #@+at
- #@nonl
- # We have to replace lines that span several sections of
- # sentinels.
- #
- # For now, we put all the new contents after the first
- # sentinels.
- # Different strategies may be possible later.
- #
- # We might, for example, run the difflib across the different
- # lines and try to construct a mapping changed line => orignal
- # line.
- #@-at
- #@@c
- self.copy_delayed_se​ntinel_block(writer_​new_sourcefile)
-
- while reader_modified_file​.index()<i2_modif​ied_file:
- line = reader_modified_file.get()
- if testing:
- print "Replace: copy modified line:", line,
- writer_new_sourcefil​e.push(line)
-
- # Take care of the sentinels which might be between the changed code.
- self.copy_all_but_la​st_sentinel_block(wr​iter_new_sourcefile,​reader_leo_file,mapp​ing,i1_internal_file​,i2_internal_file)
- reader_internal_file​.sync(i2_internal_fi​le)
- #@nonl
- #@-node:ekr.20041110​095546.2:<< handle 'replace' op >>
- #@nl
- elif tag=='delete':
- #@ << handle 'delete' op >>
- #@+node:ekr.20041110​095546.3:<< handle 'delete' op >>
- # We have to delete lines.
- # However, we NEVER delete sentinels, so they must be copied over.
-
- # sync the readers
- self.copy_delayed_se​ntinel_block(writer_​new_sourcefile)
-
- if testing:
- print "delete: syncing modified file from ", reader_modified_file.i, " to ", i1_modified_file
- print "delete: syncing internal file from ", reader_internal_file.i, " to ", i1_internal_file
-
- reader_modified_file​.sync(i2_modified_fi​le)
- reader_internal_file​.sync(i2_internal_fi​le)
-
- self.copy_all_but_la​st_sentinel_block(wr​iter_new_sourcefile,​reader_leo_file,mapp​ing,i1_internal_file​,i2_internal_file)
- #@nonl
- #@-node:ekr.20041110​095546.3:<< handle 'delete' op >>
- #@nl
- elif tag=='insert':
- #@ << handle 'insert' op >>
- #@+node:ekr.20041110​095546.4:<< handle 'insert' op >>
-
- while reader_modified_file​.index()<i2_modif​ied_file:
- line = reader_modified_file.get()
- if testing:
- print "insert: copy line:", line,
- writer_new_sourcefil​e.push(line)
-
- # Since (only) lines are inserted, we do not have to reposition any reader.
- self.copy_delayed_se​ntinel_block(writer_​new_sourcefile)
-
- #@-node:ekr.20041110​095546.4:<< handle 'insert' op >>
- #@nl
- else: assert 0
- #@ << copy the sentinels at the end of the file >>
- #@+node:ekr.20041110​095546.6:<< copy the sentinels at the end of the file >>
- self.copy_delayed_se​ntinel_block(writer_​new_sourcefile)
-
- while reader_leo_file.inde​x()<reader_leo_fi​le.size():
- writer_new_sourcefil​e.push(reader_leo_fi​le.get())
- #@-node:ekr.20041110​095546.6:<< copy the sentinels at the end of the file >>
- #@nl
- written = write_if_changed(wri​ter_new_sourcefile.g​etlines(),targetfile​,sourcefile)
- if written:
- #@ << check that the output makes sense >>
- #@+node:ekr.20041110​095546.5:<< check that the output makes sense >>
- #@+at
- #@nonl
- # We check two things:
- # - Applying a 'push' operation will produce the modified file.
- # - Our new sourcefile still has the same sentinels as the replaced
- # one.
- #@-at
- #@@c
-
- s_outlines, sentinel_lines = push_filter(sourcefile)
-
- # Check that 'push' will re-create the changed file.
- self.check_lines_for_equality(
- s_outlines,targetlines,
- "Pull did not work as expected (source: %s, target: %s):" % (sourcefile, targetfile),
- "Content of sourcefile:",
- "Content of modified file:")
-
- # Check that no sentinels got lost.
- old_sentinel_lines = push_filter_lines(re​ader_leo_file.lines[​:reader_leo_file.i],​marker)[1]
- self.check_lines_for_equality(
- sentinel_lines,old_s​entinel_lines,
- "Pull modified sentinel lines (source: %s, target: %s):" % (sourcefile, targetfile),
- "Current sentinel lines:",
- "Old sentinel lines:")
- #@nonl
- #@-node:ekr.20041110​095546.5:<< check that the output makes sense >>
- #@nl
- #@nonl
- #@-node:bwmulder.200​41017125718.25:pull_​source
- #@-others
-#@-node:bwmulder.20​041017125718.21:clas​s sentinel_squasher
-#@-node:bwmulder.20​041018233934.1:plugi​n core
-#@+node:bwmulder.20​041018233934.2:inter​face
-#@+node:bwmulder.20​041019071205:plugin specific functions
-#@+node:bwmulder.20​041017125718.27:putI​nHooks
+import mod_shadow_core
+#@nonl
+#@-node:bwmulder.20​060418131356.23:impo​rts
+#@+node:bwmulder.20​060418131356.68:inte​rface
+#@+node:bwmulder.20​060418131356.69:plug​in specific functions
+#@+node:bwmulder.20​060418131356.70:putI​nHooks
 def putInHooks ():
     """Modify methods in Leo's core to support this plugin."""
     
@@ -827,31 +188,29 @@
     g.funcToMethod(openF​orWrite,leoAtFile.at​File)
     g.funcToMethod(gotoL​ineNumberOpen,leoCom​mands.Commands)
     g.funcToMethod(apply​LineNumberMappingIfA​ny, leoCommands.Commands)
-#@-node:bwmulder.20​041017125718.27:putI​nHooks
-#@+node:bwmulder.20​041017125718.26:appl​yConfiguration
+#@-node:bwmulder.20​060418131356.70:putI​nHooks
+#@+node:bwmulder.20​060418131356.71:appl​yConfiguration
 def applyConfiguration (config=None):
 
     """Called when the user presses the "Apply" button on the Properties form.
    
     Not sure yet if we need configuration options for this plugin."""
 
- global active, testing, print_copy_operations, do_backups, shadow_subdir, verbosity, prefix
-
     if config is None:
         fileName = os.path.join(g.app.l​oadDir,"..","plugins​","mod_shadow.ini")
         if os.path.exists(fileName):
             config = ConfigParser.ConfigParser()
             config.read(fileName)
     if config:
- active = config.getboolean("M​ain","Active")
- testing = config.getboolean("Main", "testing")
- verbosity = config.getint("Main", "verbosity")
- prefix = config.get("Main", "prefix")
- print_copy_operations = config.get("Main", "print_copy_operations")
- shadow_subdir = config.get("Main", "shadow_subdir")
+ mod_shadow_core.active = config.getboolean("M​ain","Active")
+ mod_shadow_core.testing = config.getboolean("Main", "testing")
+ mod_shadow_core.verbosity = config.getint("Main", "verbosity")
+ mod_shadow_core.prefix = config.get("Main", "prefix")
+ mod_shadow_core.prin​t_copy_operations = config.get("Main", "print_copy_operations")
+ mod_shadow_core.shadow_subdir = config.get("Main", "shadow_subdir")
 
-#@-node:bwmulder.20​041017125718.26:appl​yConfiguration
-#@+node:bwmulder.20​041017184636:check_f​or_shadow_file
+#@-node:bwmulder.20​060418131356.71:appl​yConfiguration
+#@+node:bwmulder.20​060418131356.72:chec​k_for_shadow_file
 def check_for_shadow_file (self,filename):
     """
     Check if there is a shadow file for filename.
@@ -863,23 +222,23 @@
     dir, simplename = os.path.split(filename)
     rootname, ext = os.path.splitext(simplename)
     if ext=='.tmp':
- shadow_filename = os.path.join(dir,sha​dow_subdir,prefix + rootname)
+ shadow_filename = os.path.join(dir, mod_shadow_core.shadow_subdir, mod_shadow_core.prefix + rootname)
         if os.path.exists(shado​w_filename):
- resultname = os.path.join(dir,sha​dow_subdir,prefix + simplename)
+ resultname = os.path.join(dir, mod_shadow_core.shadow_subdir, mod_shadow_core.prefix + simplename)
             return resultname, False
         else:
             return '', False
     else:
- shadow_filename = os.path.join(dir,sha​dow_subdir,prefix + simplename)
+ shadow_filename = os.path.join(dir,mod​_shadow_core.shadow_​subdir,mod_shadow_co​re.prefix + simplename)
         if os.path.exists(shado​w_filename):
             return shadow_filename, os.path.getsize(filename)<= 2
         else:
             return '', False
-#@-node:bwmulder.20​041017184636:check_f​or_shadow_file
-#@-node:bwmulder.20​041019071205:plugin specific functions
-#@+node:bwmulder.20​041019071909:additio​nal core functions
-#@+node:bwmulder.20​041017135327:openFor​Read
-def openForRead (self,filename,rb):
+#@-node:bwmulder.20​060418131356.72:chec​k_for_shadow_file
+#@-node:bwmulder.20​060418131356.69:plug​in specific functions
+#@+node:bwmulder.20​060418131356.73:addi​tional core functions
+#@+node:bwmulder.20​060418131356.74:open​ForRead
+def openForRead (self, filename, rb):
     """
     Replaces the standard open for reads.
     Checks and handles shadow files:
@@ -890,18 +249,21 @@
     """
     try:
         dir, simplename = os.path.split(filename)
- shadow_filename = os.path.join(dir,sha​dow_subdir,prefix + simplename)
+ shadow_filename = os.path.join(dir,mod​_shadow_core.shadow_​subdir, mod_shadow_core.prefix + simplename)
         if os.path.exists(shado​w_filename):
             file_to_read_from = shadow_filename
- if os.path.exists(filename)and os.path.getsize(file​name)<=2:
- if verbosity >= 2:
- g.es("Copy %s to %s without sentinels"%(shadow_f​ilename,filename))
- push_file(sourcefile​name=shadow_filename​,targetfilename=file​name)
- else:
- sq = sentinel_squasher()
- if verbosity >= 2:
- g.es("reading in shadow directory %s"% shadow_subdir,color="orange")
- sq.pull_source(sourc​efile=shadow_filenam​e,targetfile=filenam​e)
+ newfile = os.path.exists(filename)and os.path.getsize(filename)<=2
+ if newfile:
+ if mod_shadow_core.verbosity >= 2:
+ g.es("Copy %s to %s without sentinels"%(shadow_filename, filename))
+ mod_shadow_core.copy​_file_removing_senti​nels(sourcefilename=​shadow_filename, targetfilename=filename)
+ else:
+ sq = mod_shadow_core.sent​inel_squasher(g.es, g.nullObject)
+ if mod_shadow_core.verbosity >= 2:
+ g.es("reading in shadow directory %s"% mod_shadow_core.shad​ow_subdir,color="ora​nge")
+ written = sq.propagate_changes​_from_file_without_s​entinels_to_file_wit​h_sentinels(with_sen​tinels=shadow_filena​me, without_sentinels=filename)
+ if written:
+ g.es("file %s updated from %s" % (shadow_filename, filename), color="orange")
         else:
             file_to_read_from = filename
         return open(file_to_read_from,'rb')
@@ -910,9 +272,9 @@
         g.es_exception()
         raise
 #@nonl
-#@-node:bwmulder.20​041017135327:openFor​Read
-#@+node:bwmulder.20​041017131319:openFor​Write
-def openForWrite (self,filename,wb):
+#@-node:bwmulder.20​060418131356.74:open​ForRead
+#@+node:bwmulder.20​060418131356.75:open​ForWrite
+def openForWrite (self, filename, wb):
     """
     Replaces the standard open for writes:
         - Check if filename designates a file
@@ -923,18 +285,18 @@
     dir, simplename = os.path.split(filename)
     rootname, ext = os.path.splitext(simplename)
     assert ext=='.tmp'
- shadow_filename = os.path.join(dir,sha​dow_subdir,prefix + rootname)
+ shadow_filename = os.path.join(dir,mod​_shadow_core.shadow_​subdir, mod_shadow_core.prefix + rootname)
     self.writing_to_shad​ow_directory = os.path.exists(shadow_filename)
     if self.writing_to_shad​ow_directory:
         self.shadow_filename = shadow_filename
- if verbosity >= 2:
- g.es("Using shadow file in folder %s" % shadow_subdir,color="orange")
- file_to_use = os.path.join(dir,sha​dow_subdir,prefix + simplename)
+ if mod_shadow_core.verbosity >= 2:
+ g.es("Using shadow file in folder %s" % mod_shadow_core.shad​ow_subdir,color="ora​nge")
+ file_to_use = os.path.join(dir,mod​_shadow_core.shadow_​subdir,mod_shadow_co​re.prefix + simplename)
     else:
         file_to_use = filename
     return open(file_to_use,'wb')
-#@-node:bwmulder.20​041017131319:openFor​Write
-#@+node:bwmulder.20​041018075528:gotoLin​eNumberOpen
+#@-node:bwmulder.20​060418131356.75:open​ForWrite
+#@+node:bwmulder.20​060418131356.76:goto​LineNumberOpen
 def gotoLineNumberOpen (self,filename):
     """
     Open a file for "goto linenumber" command and check if a shadow file exists.
@@ -944,10 +306,10 @@
     """
     try:
         dir, simplename = os.path.split(filename)
- shadow_filename = os.path.join(dir,sha​dow_subdir,prefix + simplename)
+ shadow_filename = os.path.join(dir,mod​_shadow_core.shadow_​subdir,mod_shadow_co​re.prefix + simplename)
         if os.path.exists(shado​w_filename):
             lines = file(shadow_filename​).readlines()
- self.line_mapping = push_filter_mapping(​lines,marker_from_ex​tension(simplename))​
+ self.line_mapping = mod_shadow_core.push​_filter_mapping(line​s, mod_shadow_core.mark​er_from_extension(si​mplename))
         else:
             self.line_mapping ={}
             lines = file(filename).readlines()
@@ -957,23 +319,27 @@
         g.es_exception()
         raise
 #@nonl
-#@-node:bwmulder.20​041018075528:gotoLin​eNumberOpen
-#@-node:bwmulder.20​041019071909:additio​nal core functions
-#@+node:bwmulder.20​041019071205.1:Leo overwrites
-#@+node:bwmulder.20​041231222931:gotoLin​eNumber
-#@+node:bwmulder.20​041231222931.1:apply​LineNumberMappingIfA​ny
+#@-node:bwmulder.20​060418131356.76:goto​LineNumberOpen
+#@-node:bwmulder.20​060418131356.73:addi​tional core functions
+#@+node:bwmulder.20​060418131356.77:Leo overwrites
+#@+node:bwmulder.20​060418131356.78:goto​LineNumber
+#@+node:bwmulder.20​060418131356.79:appl​yLineNumberMappingIf​Any
 def applyLineNumberMappi​ngIfAny(self, n):
     """
     Hook for mod_shadow plugin.
     """
     if self.line_mapping:
+ if 0 <= n < len(self.line_mapping):
         return self.line_mapping[n]
     else:
+ g.alert("Only %s lines" % len(self.line_mapping))
+ return 1
+ else:
         return n
 #@nonl
-#@-node:bwmulder.20​041231222931.1:apply​LineNumberMappingIfA​ny
-#@-node:bwmulder.20​041231222931:gotoLin​eNumber
-#@+node:bwmulder.20​041019071205.3:writi​ng
+#@-node:bwmulder.20​060418131356.79:appl​yLineNumberMappingIf​Any
+#@-node:bwmulder.20​060418131356.78:goto​LineNumber
+#@+node:bwmulder.20​060418131356.80:writ​ing
 #@+doc
 # Just like for reading, we redirect the writing to the shadow file, if
 # one is there.
@@ -988,7 +354,7 @@
 # -When Leo renames the file(replaceTargetFi​leIfDifferent).
 #@-doc
 #@nonl
-#@+node:bwmulder.20​041018224835:atFile.​replaceTargetFileIfD​ifferent
+#@+node:bwmulder.20​060418131356.81:atFi​le.replaceTargetFile​IfDifferent
 original_replaceTarg​etFileIfDifferent = leoAtFile.atFile.rep​laceTargetFileIfDiff​erent
 
 def replaceTargetFileIfDifferent (self):
@@ -1004,9 +370,9 @@
             # Original_replaceTarg​etFileIfDifferent should be oblivious
             # to the existance of the shadow directory.
             if self.writing_to_shad​ow_directory:
- if verbosity >= 2:
- g.es("Updating file from shadow folder %s" % shadow_subdir,color='orange')
- push_file(self.shado​w_filename,targetFil​eName)
+ if mod_shadow_core.verbosity >= 2:
+ g.es("Updating file from shadow folder %s" % mod_shadow_core.shad​ow_subdir,color='ora​nge')
+ mod_shadow_core.copy​_file_removing_senti​nels(self.shadow_fil​ename,targetFileName​)
 
     finally:
         if self.writing_to_shad​ow_directory:
@@ -1021,46 +387,76 @@
         self.targetFileName = targetFileName
         self.outputFileName = outputFileName
 #@nonl
-#@-node:bwmulder.20​041018224835:atFile.​replaceTargetFileIfD​ifferent
-#@-node:bwmulder.20​041019071205.3:writi​ng
-#@+node:bwmulder.20​041017125718.33:mass​ageComment
+#@-node:bwmulder.20​060418131356.81:atFi​le.replaceTargetFile​IfDifferent
+#@-node:bwmulder.20​060418131356.80:writ​ing
+#@+node:bwmulder.20​060418131356.82:mass​ageComment
 def massageComment (self,s):
 
- """Leo has no busines changing comments!"""
+ """Leo has no business changing comments!"""
 
     return s
-#@-node:bwmulder.20​041017125718.33:mass​ageComment
-#@-node:bwmulder.20​041019071205.1:Leo overwrites
-#@-node:bwmulder.20​041018233934.2:inter​face
-#@+node:bwmulder.20​041017125718.39:stop​_testing
+#@-node:bwmulder.20​060418131356.82:mass​ageComment
+#@-node:bwmulder.20​060418131356.77:Leo overwrites
+#@-node:bwmulder.20​060418131356.68:inte​rface
+#@+node:bwmulder.20​060418131356.83:test​_support
+#@+node:bwmulder.20​060418131356.84:test​_propagate_changes_L​eo
+def test_propagate_changes_Leo(c):
+ """
+ Leo version of the test procedure.
+ Gets the arguments from Leo nodes.
+ """
+ from leoTest import testUtils
+
+ def get_node_lines(title):
+ unicode_string = u.findNodeInTree(p, title).bodyString()
+ return unicode_string.split("\n")
+ global testing
+ old_testing = testing
+ testing = 5
+ try:
+ p = c.currentPosition()
+ u = testUtils(c)
+
+ before_with_sentinels_lines = get_node_lines ("before with sentinels")
+ changed_without_sentinels_lines = get_node_lines ("changed without sentinels")
+ after_with_sentinel_lines = get_node_lines ("after with sentinels")
+
+ sq = mod_shadow_core.sent​inel_squasher(g.es, g.nullObject)
+ mod_shadow_core.test​_propagate_changes (before_with_sentinels_lines, changed_without_sent​inels_lines, after_with_sentinel_lines, "#@", g.es, g.nullObject)
+ finally:
+ testing = old_testing
+
+#@-node:bwmulder.20​060418131356.84:test​_propagate_changes_L​eo
+#@-node:bwmulder.20​060418131356.83:test​_support
+#@+node:bwmulder.20​060418131356.86:stop​_testing
 def stop_testing ():
    global testing
    testing = False
-#@-node:bwmulder.20​041017125718.39:stop​_testing
-#@+node:bwmulder.20​050120235957:main
+#@-node:bwmulder.20​060418131356.86:stop​_testing
+#@+node:bwmulder.20​060418131356.87:main​
 def main():
- global active, verbosity
+ pass
 try:
     g.app
     assert g.app is not None
     # if g.app is not defined, we are not
     # imported from Leo
 except:
- active = False
+ mod_shadow_core.active = False
 else:
     applyConfiguration()
 
-if active and not g.app.unitTesting: # Not safe for unit testing: changes Leo's core.
+if mod_shadow_core.active and not g.app.unitTesting: # Not safe for unit testing: changes Leo's core.
    putInHooks() # Changes Leo's core.
    # leoPlugins.registerH​andler("idle", autosave)
- if verbosity >= 1:
+ if mod_shadow_core.verbosity >= 1:
       g.es("Shadow plugin enabled!",color="orange")
     
 #@nonl
-#@-node:bwmulder.20​050120235957:main
+#@-node:bwmulder.20​060418131356.87:main​
 #@-others
 
 main()
 #@nonl
-#@-node:bwmulder.20​041017125718:@thin mod_shadow.py
+#@-node:bwmulder.20​060418131356.17:@thi​n mod_shadow.py
 #@-leo

File [added]: mod_shadow_core.py
Url: http://leo.tigris.or​g/source/browse/leo/​plugins/mod_shadow_c​ore.py?rev=1.1&c​ontent-type=text/vnd​.viewcvs-markup
Added lines: 0
--------------

« Previous message in topic | 1 of 1 | Next message in topic »

Messages

Show all messages in topic

CVS update: /leo/plugins/ bwmulder Bernhard Mulder 2006-05-03 09:00:05 PDT
Messages per page: