"""Basic support for keeping your project files synchronized with files on disk. To install: - Place this file somewhere on your scripts path, as set in Preferences:Scripting. To use: - Bring up the contextual menu for an item in the project pane. - Select "Add new files" to recursively scan for new files, If the selected item is a folder, then new files will be searched for recursively in this folder. If the selected item is a file, then new files will be searched for recursively from the parent directory of the file. - Select "Remove deleted files" to remove any files from your project that cannot be found on disk. Output: - A list of all files added or removed appears in the Messages panel. Warning: you may have to select "Scripts" instead of "All" to see these messages (due to a known bug in WingIDE 2.0.3). - Brief messages appear in the status bar at the bottom of the window. Based on projectupkeep 0.2 by Ken Kinder. Changes include: - Added a script to remove files that are missing on disk (e.g. have been deleted). - Runs much faster (but not in the background). - Uses 3 short lists instead of one long "stop list" to control which files and directories to include. - Uses the sets module, so requires Python 2.3 or later. To Do: - Use WingIDE's idea of what files to include and exclude (is it practical to get hold of this information?) - If there is ever a way to tell if a project is supposed to include exactly the files in a particular directory (e.g. a python package) then scan that entire directory instead of just the directory the user clicks on. History: 2005-05-25 Russell Owen based on projectupkeep 0.2 by Ken Kinder I am not sure if Ken Kinder would claim copyright for this code, since it is so loosely based on projectupkeep. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ----------------------------- With minor modifications by Wingware """ import glob import os import sets import wingapi # The following lists control which directories are excluded # which files are excluded and which files are included. # They are applied in order, thus: # - Files in excluded directories are never examined # - Excluded files are never checked for inclusion # # The entries are fnmatch patterns: # * matches everything # ? matches any single character # [seq] matches any character in seq # [!seq] matches any character not in seq # for more details see the fnmatch module # # ExclFiles can be left blank unless you want # to excluded particular source files for some reason) ExclDirs = [ '*.svn', 'CVS', '#*#', '.#*', 'build', ] ExclFiles = [ ] InclFiles = [ '*.py', '*.c', '*.c++', '*.cpp', '*.h', '*.htm', '*.html', '*.sh', '*.txt', ] class ProjectScript: def __init__(self): self.app = wingapi.gApplication def showMsg(self, msg): print msg self.app.SetStatusMessage(msg) class AddNewFilesScript(ProjectScript): def start(self, begPath): """Start scanning for new files. If begPath is a directory, recursively search the directory. If begPath is a file, recursively search its parent directory. """ if not os.path.isdir(begPath): begPath = os.path.dirname(begPath) self.begPath = begPath self.showMsg('Add new files: scanning: %s' % (begPath,)) self.proj = self.app.GetProject() self.desFileSet = sets.Set() self._scanForFiles() def startAvailable(self, update_path=wingapi.kArgFilename): return len(update_path) > 0 def _addFiles(self): """Add any new files found in self.desFileSet to the project. """ currFiles = self.proj.GetAllFiles() filesToAdd = list(self.desFileSet - sets.Set(currFiles)) filesToAdd.sort() if len(filesToAdd) > 0: print "Add new files: adding:" for fname in filesToAdd: print fname self.proj.AddFiles(filesToAdd) self.showMsg('Add new files: added %s files' % (len(filesToAdd)),) def _scanForFiles(self): """Scan the disk for project files starting from self.begPath Add all files found to self.desFileSet. """ for root, dirs, files in os.walk(self.begPath): # Remove directories we don't want to walk. # Warning: we must actually modify the list "dirs" # for the exclusion to have any effect. dirsCopy = dirs[:] for d in dirsCopy: for exclPattern in ExclDirs: if glob.fnmatch.fnmatch(d, exclPattern): dirs.remove(d) break # add desired files to desFileSet for f in files: for exclPattern in ExclFiles: if glob.fnmatch.fnmatch(f, exclPattern): # excluded file; on to the next break else: # not exluded; see if included for inclPattern in InclFiles: if glob.fnmatch.fnmatch(f, inclPattern): # included file; add full path to desFileSet fullPath = os.path.join(root, f) self.desFileSet.add(fullPath) break self._addFiles() class RemoveDeletedFilesScript(ProjectScript): def start(self): self.showMsg('Remove deleted files: running') self.proj = self.app.GetProject() currFiles = self.proj.GetAllFiles() filesToRemove = [] for filePath in currFiles: if not os.path.exists(filePath): filesToRemove.append(filePath) filesToRemove.sort() if len(filesToRemove) > 0: print "Remove deleted files: removing:" for fname in filesToRemove: print fname self.proj.RemoveFiles(filesToRemove) self.showMsg('Remove deleted files: removed %s files' % (len(filesToRemove),)) _addNewFilesScript = AddNewFilesScript() def add_new_files(begPath=wingapi.kArgFilename): _addNewFilesScript.start(begPath[0]) add_new_files.contexts = [ wingapi.kContextProject(10), ] add_new_files.available = _addNewFilesScript.startAvailable _removeDeletedFilesScript = RemoveDeletedFilesScript() def remove_deleted_files(begPath=wingapi.kArgFilename): _removeDeletedFilesScript.start() remove_deleted_files.contexts = [ wingapi.kContextProject(10), ]