--- a/vim/.vimrc Tue Nov 16 17:53:55 2010 -0500
+++ b/vim/.vimrc Mon Nov 22 14:32:21 2010 -0500
@@ -272,8 +272,10 @@
map <F4> :TlistToggle<cr>
map <leader>T :!/usr/local/bin/ctags --exclude='**/ckeditor' -R . $(test -f .venv && echo ~/lib/virtualenvs/`cat .venv`)<CR>
-" Rope
-source $HOME/.vim/sadness/ropevim/rope.vim
+" Rope and Bike.
+let g:bike_exceptions=1
+source $HOME/.vim/sadness/sadness.vim
+
let ropevim_enable_shortcuts = 0
let ropevim_guess_project = 1
noremap <leader>rr :RopeRename<CR>
--- a/vim/colors/molokai.vim Tue Nov 16 17:53:55 2010 -0500
+++ b/vim/colors/molokai.vim Mon Nov 22 14:32:21 2010 -0500
@@ -58,7 +58,7 @@
hi Macro guifg=#C4BE89 gui=italic
hi SpecialKey guifg=#66D9EF gui=italic
-hi MatchParen guifg=#E4E400 guibg=NONE gui=bold
+hi MatchParen guifg=#E4E400 guibg=#232728 gui=bold
hi ModeMsg guifg=#E6DB74
hi MoreMsg guifg=#E6DB74
hi Operator guifg=#F92672
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/AUTHORS Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,24 @@
+Authors
+-------
+
+Phil Dawes <pdawes@users.sourceforge.net> - Current Maintainer
+ and Main Author
+Shae Erisson <shae@lapland.fi> - Original Maintainer
+
+
+The following people have contributed code, bugfixes and patches:
+
+Jürgen Hermann <jh@web.de>
+Canis Lupus
+Syver Enstad <syver-en@online.no> Windows emacs patches
+Mathew Yeates <mathew@comma.jpl.nasa.gov> VIM support and bug fixes
+Marius Gedminas <mgedmin@delfi.lt> More VIM support
+François Pinard <pinard@iro.umontreal.ca> Pymacs + help with
+ emacs integration
+Ender <ender@objectrealms.net>
+Jonathan <jonathan@infogen.net.nz>
+Steve <thepindropper@yahoo.com.au>
+Peter Astrand <peter@cendio.se>
+
+See ChangeLog for more details.
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/COPYING Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,32 @@
+COPYRIGHT AND PERMISSION NOTICE
+
+Copyright (c) 2000 by Shae Erisson <shae@lapland.fi>
+Copyright (c) 2001 by Phil Dawes <pdawes@users.sourceforge.net>
+
+All rights reserved.
+
+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, and/or sell copies of the Software, and to permit persons
+to whom the Software is furnished to do so, provided that the above
+copyright notice(s) and this permission notice appear in all copies of
+the Software and that both the above copyright notice(s) and this
+permission notice appear in supporting documentation.
+
+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
+OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR 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.
+
+Except as contained in this notice, the name of a copyright holder
+shall not be used in advertising or otherwise to promote the sale, use
+or other dealings in this Software without prior written authorization
+of the copyright holder.
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/ChangeLog Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,2034 @@
+2004-02-18 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/getTypeOf.py: Added feature to
+ resolveImportedModuleOrPackage that searches the package hierarchy
+ of the scope even if it isn't in the pythonpath.
+
+2004-02-11 Phil Dawes <pdawes@users.sf.net>
+
+ * bike/query/getTypeOf.py: rewrote resolveImportedModuleOrPackage
+ to use purely getModuleOrPackageUsingFQN searches. (doesnt use
+ getTypeOf Root searching any more)
+
+ * bike/query/common.py: rewrote getLogicalLine to handle
+ multilines that parse seperately. (But was submitted by Peter
+ Astrand)
+
+2004-02-10 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/getTypeOf.py: Added functionality to search from the
+ current directory if looking for an import and scope is a module
+
+2004-02-10 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/moveToModule.py: Added handling of individual
+ 'from a.foo import theFunction'.
+
+ * bike/parsing/fastparserast.py: Added Peter Astrand's patch to
+ fix following bug:
+ 'Find-References (and probably others) fails if there is more
+ whitespace than a single space between the "class"/"def" keyword
+ and the class/def name.'
+
+ * bike/query/common.py: fixed bug in just-written-code where no
+ dest in Printnl causes npe.
+
+ * bike/query/common.py: Fixed a bug in MatchFinder. Printnl ast
+ nodes (print >>foo, bah) look at there children the opposito way
+ to the way the text comes. (i.e. they do visit(bah), visit(foo) in
+ the example above). Wrote code to get round this.
+
+2004-02-09 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/moveToModule.py: added more functionality to
+ moveFunction
+
+ * bike/bikefacade.py: added expanduser to normalizeFilename so
+ that ~/src/foo gets turned into a proper full path.
+
+ * bike/bikefacade.py: exposed moveClassToNewModule.
+
+ * bike/refactor/moveClass.py: started move-class-to-new-module
+ refactoring.
+
+2004-02-05 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/pathutils.py: new module containing all the
+ functions that search all the files in the pythonpath.
+
+ * bike/parsing/extended_ast.py: Removed this module. SourceFile is
+ now in load.py, the rest is in fastparserast.py
+
+ * ide-integration/BicycleRepairMan_Idle.py : Added patch from
+ Steve <thepindropper@yahoo.com.au> to allow easier saving of files
+ before refactoring in IDLE
+
+2004-02-04 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/extended_ast.py: lots of cleanup. Renamed Source
+ class to SourceFile. Slimmed down Package and Root. Am planning on
+ removing this module completely, and moving the classes into other
+ modules.
+
+
+ * bike/refactor/extractVariable.py: refactored and removed
+ extractLocalVariable_old
+
+ * bike/refactor/extractMethod.py: refactored and removed
+ extractMethod_old
+
+ * bike/refactor/test_rename*.py: Removed the 'rename twice'
+ tests. They are no longer applicable (since BRM saves to disk
+ after each refactoring)
+
+ * bike/transformer/: Moved save.py and undo.py into the
+ transformer package
+
+ * bike/parsing/newstuff.py: moved generatePackageDependencies into
+ the parsing package, because it needs to be used by other modules
+ in the parsing package.
+
+2004-02-03 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/relationships.py: Added generatePackageDependencies
+ which given a file in a package hierarchy, yields a list of
+ external packages and modules used by the package.
+
+ * bike/parsing/newstuff.py: Rewrote
+ generateModuleFilenamesInPythonPath wrt new ref search scheme.
+ Scheme: If the module you are searching from is in a package
+ hierarchy, scan every module in the hierarchy, and the files in
+ the directory above it (since they can reach the package without
+ including it in PYTHONPATH).
+ If the module is in a non-package directory, search in all files
+ in the directory, and in any packages below that directory.
+ N.B. this is in addition to directories in the PYTHONPATH.
+
+2004-02-02 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/newstuff.py: Removed getRegexMatchesInPythonPath
+
+ * bike/parsing/newstuff.py: Modified getSourceNodesContainingRegex
+ to search the pythonpath + the set of python files in the
+ directory above the root package of the current file. This should
+ remove the requirement to add the root directory to the python
+ path, and thus prevent BRM from finding references in other
+ package structures.
+
+ * bike/query/common.py: Removed scanASTSourceNodesForMatches and walkSourceNodes
+
+ * bike/globals.py: Added True/False declaration for python 2.2
+ back-compatibility.
+
+2004-01-26 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/common.py: Added code to check for \ in a previous
+ line when locating logical lines
+
+2004-01-25 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/common.py: Added a test and fixed a bug that meant
+ find-definitions from multiline statements wouldn't work if the
+ braces were equalised on the following line.
+ (thanks again to Detlev for reporting this bug)
+
+2004-01-14 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/common.py: Added a test and fixed a bug that meant
+ find-definitions from multiline statements wouldn't work.
+ (thanks to Detlev for reporting this bug)
+
+2004-01-12 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/load.py: Added Detlevs patch to not recurse into
+ subversion directories.
+
+2004-01-11 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/newstuff.py: Added code in
+ generateModuleFilenamesInPythonPath to ensure each filename is
+ generated once. (and added test to check for it)
+ (Thanks to Detlev & Syver for this bug report)
+
+ * bike/query/common.py (globalScanForMatches): Removed old
+ ast-based code from globalScanForMatches
+
+2004-01-08 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/getTypeOf.py: Fixed bug where 'from foo import aou'
+ would cause an assert to fail if foo didn't exist. (Thanks Syver)
+
+2004-01-06 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/bikefacade.py: Added code to normalize paths coming into
+ BRM. (hopefully fixes Syver's problems on windows - Thanks Syver)
+
+
+2003-09-04 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/BicycleRepairMan_Idle.py: Fixed bug in
+ Bicyclerepair_idle.
+
+----------------------- 0.9 BETA4 -------------------------------
+
+2003-09-02 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/BicycleRepairMan_Idle.py: Fixed bug where brmctx
+ was being tested for a __nonzero__ operator due to new wrapper
+ stuff.
+ Also made the matches window a singleton
+
+ * bike/query/findReferences.py: Rewrote attribute reference
+ finding algorithm to locate a possible match, and then check if
+ the match class shares a common ancestor with the target. (rather
+ than finding all classes in the target hierarchy and then testing
+ each match class against the hierarchy).
+ This results in a 10000%
+
+2003-09-02 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/relationships.py: Added buildClassHierarchy
+ functionality to guess a class hierarchy, but found that it's
+ still much too slow to handle the wxpython class hierarchy of 1900
+ odd classes under 1 root!
+
+
+2003-09-01 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/fastparserast.py: Added maskedsrc to the cache
+
+ * bike/query/getTypeOf.py: Added caching of type lookups
+
+ * bike/bikefacade.py: Added a generic wrapper which purges the
+ caches before and after each brm ctx call
+
+2003-08-31 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/newstuff.py: Added simple caching mechanism for
+ sourceNodes
+
+2003-08-30 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/extended_ast.py: Removed some crufty methods and
+ base classes for managing the ast tree hierarchy
+
+ * bike/bikefacade.py: Removed a couple of ast related methods
+
+ * bike/query/findReferences.py: Modified the findRefs
+ functionality to exclude matches where the confidence is high that
+ it's *not* the right type.
+
+ * bike/parsing/load.py: removed the load function (and made
+ necessary refactorings to remove it)
+
+2003-08-25 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/bikefacade.py: Added setWarningLogger
+
+2003-08-25 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/extended_ast.py: Fixed bug where "../.." was being
+ added to sys.path. This was because extended_ast.py was adding
+ importing setpath.py.
+
+2003-08-25 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/bikefacade.py: Added warning code to print message if
+ sys.path is changed by setpath.py
+
+ * ide-integration/test/README: Added ide tests for inline and
+ extract variable
+
+ * bike/bikefacade.py: Removed the load() function, and fixed
+ inline and extract local variable
+
+----------------------- 0.9 BETA3 -------------------------------
+
+2003-08-22 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/bikefacade.py: Fixed removeLibdirsFromPath to work with
+ windows python lib directories
+
+----------------------- 0.9 BETA2 -------------------------------
+
+2003-08-21 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/*: Made it backward compatible with python 2.2
+
+ * ide-integration/bike.vim: Adapted vim plugin to work with new
+ api.
+
+----------------------- 0.9 BETA1 -------------------------------
+
+2003-08-20 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/test_*: Misc fixes for windows paths
+
+ * ide-integration/BicycleRepairMan_Idle.py: Cleaned up error
+ handling for undo stack
+
+ * bike/query/*: Misc fixes for tests
+
+ * bike/refactor/extractMethod.py: Updated to work with new
+ non-tree system
+
+2003-08-19 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/testutils.py: Converted createSourceNodeAt to create a
+ directory structure rather than an ast. (and broke a load of tests)
+
+2003-08-18 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/testutils.py: added test setup fixture to change directory
+ before executing tests
+
+2003-08-17 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/getTypeOf.py: Fixed bug which was stopping sub
+ packages from being found properly
+
+ * bike/query/relationships.py: Fixed getAllClassesInHierarchy and
+ getAllDerivedClasses to work without AST. Uses a smart algorithm
+ which scans the text of files with regexes for baseclassnames and
+ import aliases to narrow down the likely candidates.
+
+
+2003-08-12 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * README.idle: Updated docs for python2.3/idlefork
+
+2003-08-11 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/BicycleRepairMan_Idle.py: Added code to just use
+ 1 window for matches, and to reopen it if needed after it is closed
+
+2003-08-04 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/load.py: Fixed bug which cause BRM to descend into
+ non-package directorys
+
+ * bike/parsing/extended_ast.py: Added a couple of new functions to
+ handle pythonpaths for queries and refactorings
+
+2003-08-01 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/common.py: modified globalScanForMatches to scan
+ files in addition to walking the ast.
+
+ * bike/query/findReferences.py: Added code to work with new
+ stateless design.
+
+2003-07-31 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/findDefinition.py: Removed module locating code from
+ findDefinition, and made it use resolveImportedModuleOrPackage
+ instead.
+
+ * bike/query/getTypeOf.py: Modified resolveImportedModuleOrPackage
+ to use the newstuff function getModuleOrPackageUsingFQN if it cant
+ locate the module in the AST tree.
+
+ * bike/parsing/newstuff.py: modified getModuleUsingFQN
+ to handle packages. renamed to getModuleOrPackageUsingFQN
+
+2003-07-25 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/findDefinition.py: Made necessary modifications
+ enable finding of function definitions without importing code
+ (uses the python path to determine where to look). Doesnt work for
+ methods yet.
+
+2003-07-10 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/inlineVariable.py: Applied Jonathan's patch which
+ fixes bug when multiple instances of a variable are on one line.
+
+2003-06-12 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/findReferences.py: changed findReferences interface
+ to take a filename instead of a srcnode
+
+ * bike/query/findReferences.py: Fixed bug where error wasnt being
+ reported properly
+
+2003-06-11 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/findReferences.py: renamed findReferences to
+ findReferences_old, and created a new findReferences which takes a
+ filename instead of the srcnode
+
+ * bike/bikefacade.py: Removed getFullyQualifiedNameOfScope - it's
+ not used anywhere, and relies on the fqn stuff working.
+
+ * all tests: attempted to remove all usage of fqn from
+ tests. Instead, replaced with name and filename tests. This is
+ because fqn is one of the major things binding the scopes
+ together.
+
+ * bike/bikefacade.py: removed getTypeOfExpression. It's not used
+ anywhere. Will reinstate when it is required again.
+
+ * bike/query/getTypeOf.py: Fixed bug where a recursive function
+ could cause BRM to stack overflow
+
+2003-06-10 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/getTypeOf.py: Added resolveImportedModuleOrPackage fn
+ to try and split the number of calls to getTypeOf()
+
+ * bike/query/getTypeOf.py: Fixed bug where a[0].theMethod() would
+ cause brm to barf.
+
+ * bike/parsing/fastparserast.py: Beefed up the __str__ members for
+ fastparser nodes
+
+2003-06-06 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/output.py: Removed output.py. (functionality now in
+ save.py)
+
+2003-06-05 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/extractMethod.py: removed the ismodified stuff, in
+ an attempt to make the xast hierarchy a read-only resource.
+ Added a hook in save.py to update the xast whenever somebody
+ queues an update to be saved.
+
+ * bike/parsing/save.py: Added new save module which maintains its
+ own queue of src to be written back to disk. Replaces output.py
+
+2003-05-30 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/undo.py (UndoStack.undo): refactored to use dictionary
+
+ * bike/query/findDefinition.py: Refactored main function to have a
+ 'stateless' interface (i.e. no need to pass a context).
+ findAllPossibleDefinitionsByCoords(filepath,lineno,col)
+
+2003-05-29 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/extended_ast.py (getRoot): Made the Root object a
+ singleton. Use the getRoot() method to get it. This is an
+ intermediate step in making the parser stateless. (idea is to make
+ it pretend to be stateless by being a singleton, then once the
+ interfaces have changed, transition the parser code to actually
+ make it stateless)
+
+2003-04-02 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/bikeemacs.py: Added exception catching and error
+ reporting to bike-emacs integration
+
+2003-03-31 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * Applied Jonathan's patches
+ - InlineVariable handles multiline statements
+ - Fix to bike vim integration
+
+2003-03-31 Marius Gedminas <mgedmin@delfi.lt>
+
+ * ide-integration/bike.vim: Removed unnecessary argument
+ to BikeRename().
+
+ * bike/query/common.py: Fixed handling of lambdas in MatchFinder.
+
+2003-03-17 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/extractVariable.py: Made a start on the
+ extract-local-variable refactoring
+
+2003-03-13 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/inlineVariable.py: Made a start on the
+ inline-local-variable refactoring
+
+----------------------- 0.8 BETA2 -------------------------------
+
+2003-03-11 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/BicycleRepairMan_Idle.py: Fixed bug where
+ paths with spaces meant that you couldn't select a reference when
+ doing a findReferences. (Thanks to Andy Bulka for the report)
+
+ * bike/query/getTypeOf.py: Added infinite recursion protection to
+ getTypeOf
+
+ * bike/query/relationships.py: Added some performance improving
+ code when searching for a classhierarchy. Rather than doing a
+ getTypeOf() on every base class, it finds the strings likely to be
+ base classes (i.e. the name of the base class, and any 'import
+ name as foo' lines) to narrow the search.
+
+2003-03-10 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/extractMethod.py: Fixed bug in extract method,
+ where blank lines were messing up the indentation in the resultant
+ block
+
+
+----------------------- 0.8 BETA1 -------------------------------
+
+2003-03-10 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/bikeemacs.py: Fixed bug in brm with emacs on
+ windows, where the mark can be active and nil at the same time.
+
+ * bike/bikefacade.py (BRMContext_impl.load): Fixed bug which
+ affected windows users. Path needs to be saved after it's been
+ normalized.
+
+2003-03-06 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/findDefinition.py: fixed bug in attribute finding
+ code - was only searching the first function
+
+ * bike/query/getTypeOf.py: Fixed bug where x = x.bah() would cause
+ recursion error. (just catches the stack overflow)
+
+2003-03-05 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/*: implemented automatic importing of changed files. This
+ means that ide integration stuff no longer has to import code into
+ brm whenever it is saved.
+
+2003-02-25 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/bike.vim: Consolidated RenameMethod,
+ RenameFunction and RenameClass into 1 menu option. This is because
+ brm now supports renaming of variables and attributes and I didn't
+ want to add another 2 menu items.
+
+
+2003-02-24 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/bikeemacs.py: Fixed rename 'prompt'
+ bug. Consolidated all the rename stuff into 1 menu option.
+
+2003-02-19 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/*: removed getReferencesToClass/Function/Method and
+ replaced with findReferences
+
+2003-02-13 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/findReferences.py: Added findReferencesIncludingDefn
+ and made 'vanilla' findReferences not return the definition. This
+ paves the way for a unified rename that can be used to rename
+ anything.
+
+2003-02-11 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/findDefinition.py: Fixed bug reported by Marius - if
+ class is declared in __init__.py, it blows up.
+
+ * bike/query/findReferences.py: Fixed bugs in
+ 'getDefinitionAndScope' logic, so can handle nested classes etc..
+
+2003-02-10 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/getReferencesToClass.py: Replaced module logic
+ with call to findReferences()
+
+ * tests: modified or removed tests that tested for the erroneous
+ 'import a.b.bah.TheClass'. (A class or function can't be imported
+ - only a module)
+
+2003-01-24 Marius Gedminas <mgedmin@delfi.lt>
+
+ * ide-integration/bike.vim: Show the line itself after finding
+ references/definition.
+
+2003-01-23 Marius Gedminas <mgedmin@delfi.lt>
+
+ * bike/query/common.py: Fixed a trivial NameError in
+ MatchFinder.visitGlobal().
+
+2003-01-23 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/getTypeOf.py: Refactored and cleaned up the code in
+ this module.
+
+2003-01-22 Marius Gedminas <mgedmin@delfi.lt>
+
+ * bike/query/common.py: Fixed another ValueError, this time on
+ "from foo import bar" lines.
+
+2003-01-20 Marius Gedminas <mgedmin@delfi.lt>
+
+ * bike/query/common.py, bike/query/findDefinition.py,
+ bike/query/findReferences.py: Fixed ValueError: list.index(x): x not
+ in list" error caused by several visitFunction methods visiting
+ their child nodes (argument names and default values) out-of-order.
+
+2003-01-16 Marius Gedminas <mgedmin@delfi.lt>
+
+ * bike/refactor/extractMethod.py: Now puts spaces after commas in
+ generated code.
+
+2003-01-15 Marius Gedminas <mgedmin@delfi.lt>
+
+ * ide-integration/bike.vim: Better load failure diagnostics.
+
+2003-01-14 Marius Gedminas <mgedmin@delfi.lt>
+
+ * bike/bikefacade.py, bike/parsing/fastparserast.py,
+ ide-integration/BicycleRepairMan_Idle.py: CRLF -> LF translation
+
+2003-01-13 Marius Gedminas <mgedmin@delfi.lt>
+
+ * bike/bikefacade.py: Added a function to check whether a given file
+ was ever imported.
+
+ * bike/test_bikefacade.py: Unit test for the above.
+
+ * ide-integration/bike.vim: Added code to watch for modified files
+ and reimport them into if they had been imported previously.
+
+2003-01-13 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/getReferencesToModule.py: Added Ender's
+ getReferencesToModule module (and test module)
+
+2003-01-10 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/findDefinition.py: Added some find class attribute
+ definition functionality
+
+2003-01-09 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/findDefinition.py: fixed bug where 'import'
+ statements in fn scopes weren't being searched
+
+ * ide-integration/bike.vim: Modified Marius' bike.vim vi
+ integration to support new rename and findReferences calls.
+
+2003-01-06 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/bikeemacs.py: Added fix to redisplay frame in
+ emacs, and to test for mark correctly in emacs.
+
+2003-01-05 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * AUTHORS: Added Mathew and Marius
+
+2003-01-03 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * README.emacs: Added simple instructions for installing pymacs.
+
+ * ide-integration/Pymacs-0.20: I'm going to ship pymacs-0.20 with
+ bicyclerepairman. I've modified it a little to make installation
+ easier.
+
+ * ide-integration/bikeemacs.py: Moved bikeemacs back to its
+ original place.
+
+
+2003-01-02 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/extended_ast.py: Cleaned this up a bit
+
+ * bike/parsing/fastparser.py: Fixed bug reported by Mathew Yeates
+ where more than one space between 'class' and the name foxed the
+ parser.
+
+2002-12-22 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/findReferences.py: Started work on unified
+ findReferences code. (uses findDefinition to compare matches)
+
+ * ide-integration/emacs/bikeemacs.py: Rewrote emacs integration
+ using the excellent pymacs package
+
+2002-12-09 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/BicycleRepairMan_Idle.py: Fixed findDefinition
+ cosmetic bug
+
+ * ide-integration/bike.el, bikeemacs: Added Syver Enstad's patch
+ for allowing filenames with spaces
+
+2002-12-06 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/BicycleRepairMan_Idle.py: Completed support for
+ findDefinition
+
+ * ide-integration/bike.el: added code to handle the fact that if
+ you select a region, the point is one greater than the end of the
+ region and so misses it.
+
+2002-12-03 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/findDefinition.py: Added code to scan for other
+ method matches after locating the 100% one
+
+2002-12-02 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/BicycleRepairMan_Idle.py: Added basic
+ finddefinition support to idle
+
+ * ide-integration/bikeemacs.py: Added finddefinition support to xemacs
+
+ * bike/bikefacade.py: Exposed findDefinition through bikefacade
+
+ * bike/query/findDefinition.py: Added new query interface for
+ finding the definition of a reference, given its line/col position
+ in a module. Just supports function, method, and class references
+ for now.
+
+---------------------- 0.7 ---------------------------------------
+
+2002-11-27 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/bike.el: Fixed bug where saving buffers caused
+ the new emacs protocol to get in a tangle. My solution was to add
+ a disableMessages capability
+
+---------------------- 0.7RC3 ------------------------------------
+
+2002-11-22 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/bikeemacs.py: Added acknowledgement protocol, to
+ get round synchronisation problems in windows emacs
+
+2002-11-20 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing: decommissioned addtypeinfo, typeinfo, tokenutils,
+ tokenhandler, matchTokensToAST, doublelinkedlist, testdata
+
+ * bike/query/getReferencesToClass.py: Fixed bug where
+ 'from foo import *' would cause an exception if searching for a
+ class called 'foo'
+
+ * bike/*/test_*.py: Removed dependency on brmtransformer.
+
+2002-11-15 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/extractMethod.py: Rewrote extract method module to
+ use all the new cool stuff (and operate or strings). Doesnt use
+ linked lists, iterators, tokens, brmtransformer or any of that
+ shite any more.
+
+2002-11-11 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/getTypeOf.py: Fixed bug where classscope was being
+ searched if the name wasnt found in methodscope. This is incorrect
+ because class scope isn't visible except through 'self' or a fully
+ qualified classname.
+
+2002-11-10 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/common.py: Re-wrote MatchFinder to scan along the
+ source code text when visiting ast nodes. This removes the need to
+ match tokens in order to find the source code location of an ast
+ node.
+
+---------------------- 0.7RC1 ------------------------------------
+
+
+2002-11-01 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/relationships.py: Added performance enhancement to
+ getAllDerivedClasses. Now does string search on file before ast
+ searching the classes.
+
+2002-10-30 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/BicycleRepairMan_Idle.py: Finished
+ findReferencesByCoordinates support in idle.
+
+2002-10-29 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/BicycleRepairMan_Idle.py: Added trace
+ console. Started integration of findReferencesByCoordinates into
+ idle.
+
+ * ide-integration/bike-emacs.py: Added xemacs support for
+ getFullyQualifiedNameOfScope, getTypeOfExpression and
+ findReferencesByCoordinates
+
+
+2002-10-28 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/bikefacade.py: Added getFullyQualifiedNameOfScope,
+ getTypeOfExpression and findReferencesByCoordinates
+
+2002-10-08 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/bikefacade.py: changed promptForRename callback signature
+ to be linenumbers and columnnumbers
+
+ * bike/refactor/renameMethod.py: Added prompt
+ functionality.
+
+2002-10-02 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/getTypeOf.py: Added functionality to handle scanning
+ for types of references (needed by getReferencesToMethod)
+
+ * bike/query/getReferencesToMethod.py: Implemented to use
+ common.py stuff
+
+2002-09-30 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/common.py: Split out common query code into common.py
+
+2002-09-27 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/renameFunction.py: Now uses the getReferences stuff
+
+2002-09-26 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/transformer/WordRewriter.py: Split this class out of the
+ renameClass module.
+
+ * bike/transformer: Added new transformer package to contain code
+ which rewrites the internal representation of the sourcecode
+
+ * bike/bikefacade.py: Implemented locateNode to use the new
+ fastparserast stuff.
+
+ * bike/parsing/extended_ast.py: Added code to set fqn (fully
+ qualified name) attributes on the fastparser ast
+
+2002-09-25 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/renameClass.py: Finished modification to use
+ getReferencesToClass.
+
+2002-09-06 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/getTypeOf.py: Refactored getType() functionality out
+ of ast classes and into a query module.
+
+ * bike/parsing/fastparser.py: Added parsing of import and from
+ lines to fastparser, and optimised a little.
+
+2002-09-04 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/renameClass.py: Started modification to use
+ getReferencesToClass.
+
+2002-08-30 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/getReferencesToClass.py: New module which returns a
+ sequence of references to a class
+
+ * bike/parsing/fastparser.py: Wrote new parser which builds a tree
+ of functions and classes, very quickly.
+
+2002-08-14 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/tokenutils.py: Fixed bug where attempting to
+ tokenize small strings would result in attributeerror
+
+2002-08-05 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/logging.py: Logging module. Eventually will be replaced by
+ import logging, but for now is a copy of the code by Vinay Sajip
+ (to save people from having to download the module)
+
+ * bike/parsing/brmtransformer.py: Added simple fix - tuple being
+ concatinated to list was causing problems
+
+
+---------------------- 0.6.7 ------------------------------------
+
+2002-07-31 Phil Dawes <pdawes@users.sourceforge.net>
+
+
+ * ide-integration/BicycleRepairMan_Idle.py: Added Canis fix for
+ the bug which stopped brm from displaying the menu when starting
+ idle via right-click file.
+
+ * bike/bikefacade.py: Added Canis windows compatability fix for
+ dealing with .pyw files
+
+
+2002-07-25 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/extractMethod.py: Fixed bug where extracting an
+ expression from a line just below a block opener (e.g. if foo:)
+ would cause a parse error.
+
+2002-07-21 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * README.idle: Doc about problems on windows
+
+---------------------- 0.6.6 ------------------------------------
+
+2002-07-19 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/doublelinkedlist.py: Optimised to use a list rather
+ than a class instance for each link element.
+
+2002-07-17 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/typeinfo.py: Fixed bug - package wasn't checking
+ parent packages (and Root) for types. This meant that modules
+ imported from higher up the tree weren't being found.
+
+ * bike/parsing/extended_ast.py: Renamed 'Top' to 'Root'
+
+---------------------- 0.6.5 ------------------------------------
+
+2002-07-15 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/bike.el: Minor bug fix
+
+2002-07-12 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/bikeemacs.py: Added code so that it waits for
+ buffer to be reloaded in emacs before sending the request to
+ update the next one.
+
+ * ide-integration/bike.el: Added bug fix - if prompt for rename
+ was on file not loaded, emacs would send a message back to brm
+ when it ran find-file, which buggered up the 'rename-this-method?'
+ interaction.
+
+2002-07-11 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/bike.el: Added support for GNU emacs
+
+ * README.emacs: renamed from README.xemacs
+
+ *
+
+2002-07-09 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/bikefacade.py: Added code to handle more windows filename
+ brokenness
+
+2002-07-05 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/renameMethod.py: Added same optimisation to
+ renameMethod.
+
+ * bike/refactor/renameClass.py: Added optimisation: source string
+ is scanned for classname before visiting ast. (with the lazy ast
+ creation this means that files aren't parsed unless they contain
+ the keyword (or are linked to the source via function call).
+
+ * bike/parsing/extended_ast.py: Refactored .source member into
+ .getSource() method and implemented so that tokens are
+ synchronised with source if they're modified.
+
+2002-07-03 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/extended_ast.py: Added lazy parsing of ast when
+ source.getModule() is called.
+
+ * bike/parsing/typeinfo.py: Added PackageTypeInfo which deduces
+ child types by interrogating the package rather than relying on
+ children to register themselves in the typeinfo. This was required
+ to facilitate lazy parsing of ast. (since parse would have had to
+ be done to add module node to package). A lookup of a module now
+ causes getModule() to be called on the sourcefile, thus triggering
+ parse of source into ast.
+
+---------------------- 0.6.4 ------------------------------------
+
+2002-06-27 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/tokenutils.py: Added bug fix for superflous
+ brackets. Basically, if brackets are unecessary (e.g. '(a).foo()')
+ then they aren't in the parse tree but are in the token
+ stream. This buggers up the token matching.
+ This fix just ignores unmatching ')'s. It was the simplist thing
+ that could possibly work, but may cause problems later - needs
+ lots of testing.
+
+---------------------- 0.6.3 ------------------------------------
+
+2002-06-26 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/tokenhandler.py: Fixed bug where getattr renamed
+ all occurences of the methodattr in the rest of the tokens
+
+2002-06-21 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/extractMethod.py: Fixed bug where commend before
+ the extracted block would make brm think it should be extracting
+ an expression rather than a block.
+
+ * bike/parsing/addtypeinfo.py: Fixed the same bug in _getImportedType
+
+ * bike/refactor/renameClass.py: Fixed bug where the module search
+ routine in the 'From foo import *' code was using the parent scope
+ as the search position. This doesnt work if 'from' is nested in a
+ class since parent scope is a module, not a package. Added method
+ _getParentPackage() to handle this correctly.
+
+2002-06-13 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/extractMethod.py: Fixed the 'loop' bug. extract
+ method will now recognise variables assigned to by the extracted
+ function and used in a loop by the calling function.
+
+2002-06-12 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/extractMethod.py: Lots of refactoring prior to
+ fixing a bug.
+
+2002-06-10 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/doublelinkedlist.py: Rewrote 'insert' so that it
+ doesnt move the iterator. Renamed old method to
+ insertAndPositionIteratorOnNewElement.
+
+2002-06-07 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/extractMethod.py: Fixed the parse error when
+ extracting code from an if...else... block. The problem was that
+ the code below the extraction contained the 'else', but not the
+ 'if'. This is hacked around by inserting an 'if 1: <indent>
+ pass'. This seems to work, but I welcome a better way of handling
+ this.
+
+ * bike/parsing/output.py: Added code to ensure that indent never
+ dips below 0. This fixes a bug in parsing during method extracts
+ out of nested blocks.
+
+ * bike/refactor/extractMethod.py: Fixed the 'don't know what type
+ this isAssAttr(Name('item'), 'decl', 'OP_ASSIGN')' bug
+
+---------------------- 0.6 --------------------------------------
+
+2002-05-28 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/test/*: Added manual test script for testing ide
+ integration before a release
+
+ * ide-integration/BicycleRepairMan_Idle.py: Now imports code into
+ brm when loaded, or saved for the first time.
+
+ * ide-integration/bike.el: Now imports all loaded python files
+ into brm. Also checks to see if file was just updated to avoid
+ superflous reloads.
+
+2002-05-27 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/bike.el: Added menu for brm
+
+ * bike/parsing/undo.py: Added an internal undo buffer size
+
+ * ide-integration/BicycleRepairMan_Idle.py: Added generic
+ exception reporting to idle
+
+ * ide-integration/bikeemacs.py: Added generic exception
+ reporting. All exceptions are handled and reported back to the
+ user.
+
+2002-05-24 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/BicycleRepairMan_Idle.py: Exposed undo to idle
+
+ * ide-integration/bikeemacs.py: Exposed undo to emacs
+
+ * bike/bikefacade.py: Exposed undo functionality to facade
+
+ * bike/parsing/undo.py: Added undo functionality
+
+2002-05-15 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/test_extractMethod.py: Fixed bug in expression
+ extracting code (which caused a parse exception with correct code)
+
+ * bike/bikefacade.py: Added public interface
+
+---------------------- 0.5 --------------------------------------
+
+ * ide-integration: Created new directory for integration modules
+
+2002-05-14 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/ui/BicycleRepairMan_Idle.py: Exposed extract function to idle
+
+ * bike/ui/bikeemacs.py: Exposed extract function to emacs
+
+ * bike/refactor/extractMethod.py: Added support for extracting
+ expressions into methods (e.g. a = 1+2 -> a = self.oneplustwo())
+ Added support for extracting functions.
+
+2002-05-13 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/extractMethod.py: Extract Method pretty much
+ finished. Now on with the testing...
+
+2002-05-03 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/ui/BicycleRepairMan_Idle.py: Added extract method support
+ for idle
+
+2002-05-02 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/ui/bike.el: implemented extract-method for emacs
+
+ * bike/bikefacade.py: Exposed extract-method to the outside world
+
+
+2002-05-01 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/extractMethod.py: Implemented simple extract
+ method
+
+2002-04-28 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/matchTokensToAST.py: The 'new' addtokens. Instead
+ of each AST node getting its own slice of tokens, there is only
+ one token list and each node has 2 linked list iterators pointing
+ to the start and end of its tokens in within it.
+ Thus the process of calculating the position of these iterators is
+ now called 'token matching' rather than 'adding tokens to ast'.
+
+ Having only 1 token stream vastly simplifies things. There are no
+ consistency problems when renaming or moving tokens, and no need
+ to 'divide' the comments amongst the AST nodes.
+ This enables one matching algorithm to be able to cater for all
+ ast nodes without specialized logic.
+
+ * bike/parsing/vector.py, bike/parsing/doublelinkedlist.py:
+ containers which support the same bi-directional iterator
+ interface. Used in refactoring the parsing module to use a linked
+ list of tokens
+
+2002-04-12 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/bikefacade.py: Removed use of tokens
+
+---------------------- 0.4.3 ------------------------------------
+
+2002-04-11 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/ui/BicycleRepairMan_Idle.py: fixed import problem in
+ windows
+
+ * bike/parsing/load.py: Added code so that if you select a
+ directory it will load all the modules and packages in it even if
+ it isnt a package (i.e. doesnt have a __init__.py), but would
+ recurse into non-package sub directories.
+
+ * bike/ui/*: This is no longer a package, since the only 2 files
+ are installed elsewhere.
+
+ * setup.py: Made BicycleRepairMan_Idle.py a root level
+ module. This means it can be used with idle by just adding
+ [BicycleRepairMan_Idle] to your .idle file.
+
+
+2002-04-06 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/addtokens.py: Addtokens class now takes tokens in
+ constructor. The addTokens function is renamed to
+ addTokensToASTFromSrc. This will pave the way for Source nodes to
+ hold tokens rather than the source as a string
+
+ * bike/parsing/tokenhandler.py: Fixed bug which caused name ast
+ node to not be tokenized
+
+ * bike/parsing/load.py: Added check for __init__.py before
+ recursing into sub directories. This means that non-package sub
+ directories will not be recursed into. (i.e. the user must load
+ those seperately)
+
+
+2002-03-29 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * NEWS: consolidated all README-version files into one NEWS file
+
+ * README*: updated documentation
+
+ * bike/ui/BicycleRepairMan_Idle.py: Fixed another windows filename
+ isnt normalized bug
+
+ * bike/ui/bike.el: Added same functionality as for idle
+
+ * bike/ui/BicycleRepairMan_Idle.py: Added load on save
+ functionality - means each saved python file gets automatically
+ imported into bicyclerepairman.
+
+2002-03-27 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/load.py: Added functionality to deduce the package
+ of the loaded module/package by inspecting parent directories for
+ __init__.py modules. This allows adding of new modules to an
+ existing AST.
+
+2002-03-24 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * README.idle: Added Evelyn's ammendment to doc
+
+---------------------- 0.4.2 ------------------------------------
+
+2002-03-23 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * setup.py: Removed Icons stuff from setup
+
+ * bike/ui/BicycleRepairMan_Idle.py: Fixed some bugs with idle
+ integration in windows:
+ - Ask if should rename dialogs dont take focus (which
+ makes selection disappear in windows)
+ - Filename normalizing means that filenames get compared correctly.
+
+2002-03-21 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/testutils.py: Changed the test package structure generation
+ code to add __init__.py modules to the packages. This will be used
+ to work with the new loader code (when I write it).
+
+2002-03-20 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/ui/BicycleRepairMan_Idle.py: Fixed bug which was stopping
+ changed files from being reloaded on windows
+
+---------------------- 0.4.1 ------------------------------------
+
+2002-03-19 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/ui/BicycleRepairMan_Idle.py: Fixed bug where rename method
+ prompts weren't visible to the user.
+
+ * bike/parsing/addtypeinfo.py: Fixed a bug which meant that
+ sourcenode wasnt getting added to default arguments, which caused
+ brm to crash when attempting to tokenize these.
+
+
+---------------------- 0.4 --------------------------------------
+
+2002-03-18 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * setup.py: Removed bikegui and pyxmi from the setup
+ program. These are now deprecated.
+
+ * bike/ui/BicycleRepairMan_Idle.py: Added the idle support module
+ to cvs.
+
+ * bike/parsing/tokenhandler.py: Simplified SimpleTokenHandler
+
+ * bike/parsing/extended_ast.py: Removed ExtendedNode base class -
+ it isnt needed (thanks Evelyn)
+
+ * bike/parsing/tokenutils.py: fixed bug in
+ _getOffsetOfTokensCorrespondingToParseTree which was including the
+ NL token in the offset it returned.
+
+ * bike/bikefacade.py: Changed signature of rename method
+ callback. It now sends filename and coords as args rather than
+ exposing the ast nodes to the client.
+
+2002-03-07 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/__init__.py: Moved bikefacade.py (from the ui package) into
+ the bike package. This is now referenced from __init__.py so a
+ client can do:
+ import bike; ctx = bike.load("mypath"); ctx.renameMethod()
+
+
+ * bike/parsing/typeinfo.py: ModuleTypeinfo.getTypeOf()
+ functionality now uses the imported module *name* to check other
+ modules, rather than a reference to the module itself. This is to
+ allow module reloading.
+
+ * bike/parsing/extended_ast.py: Added 'addtypeinfo' call to Source
+ node. This is now done when the sourcenode is added to the parent
+ node rather than being run once over the whole tree. This
+ facilitates adding additional trees to the ast without having to
+ run the whole addtypeinfo step again.
+
+ * bike/parsing/addtypeinfo.py: Removed the subclasses stuff from
+ the classtypeinfo functionality. Doing this at initial-parse time
+ is too brittle, since a reload of a child source node could remove
+ (or add) a subclass from a base class.
+ Instead, subclasses are calculated at refactoring time.
+
+
+
+2002-03-07 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/addtypeinfo.py: Removed addfqn,
+ addparentscopeToNode and addSourcenode to nodes functionality from
+ the SourceModule, and integrated them with the first 'addtypeinfo'
+ pass. This almost halves the initial parsing time.
+
+
+2002-03-04 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/renameMethod.py: Changed renameMethod signature to
+ take a method fqn rather than class and method parameters. This
+ allows the tests to take advantage of the same signature for
+ renameMethod,renameFunction and renameClass.
+
+ * bike/ui/bikeemacs.py: refactored to use the new bikefacade module
+
+ * bike/ui/bikefacade.py: New module which provides easy interface
+ into brm, for integrating into IDEs.
+
+ * bike/parsing/test_addtypeinfo.py: Added recursion handling for
+ things like a=a() (just catches the runtime error)
+
+2002-02-28 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/addtypeinfo.py: found a problem with lambdas - they
+ take the reference from the outer scope. If the reference is a
+ loop variable (e.g. for i in blah) then it is reassigned which
+ knackers the closure.
+ Solution - lambda i=i: doSomethingWith(i)
+
+ * bike/ui/bikegui.py: removed reliance on tokens
+
+ * bike/parsing/tokenhandler.py: added getStartCoords() and
+ getEndCoords() functions.
+
+
+2002-02-27 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * */setpath.py: code which insures pythonpath includes path
+ to the bike module (used for running test scripts). This is
+ imported by most modules.
+
+ * bike/parsing/extended_ast.py: moved addParentScopeToNodes()
+ functionality from addtypeinfo into Source class.
+
+ * bike/parsing/addtypeinfo.py: For deduced types (e.g. types got
+ from function calls), the closure of the type deducing function is
+ put in the typeinfo rather than the type itself. The closure is
+ then run at lookup.
+ This facilitates lazy loading, and means source modules can be
+ reloaded without invalidating the typeinfo objects.
+
+2002-02-18 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/ui/bikeemacs.py: Added reload source functionality
+
+ * bike/ui/bike.el: Added support for renameMethod - prompts user
+ for types that brm can't deduce
+
+2002-02-06 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/ui/bikeemacs.py: New support for emacs
+
+ * bike/parsing/addtypeinfo.py: Fixed bug which was causing
+ inheritance specs not to be renamed
+
+ * bike/parsing/output.py: Added some code to preserve level of
+ indent in multiline (comma ended) statements (e.g. function
+ arguments, class inheritence args etc...). This really needs
+ reworking into a general solution, but works 70% of the time for
+ now.
+
+2002-01-31 Phil Dawes <pdawes@bea.com>
+
+ * bike/parsing/output.py: Added comment indenting code which
+ should preserve the indent level of comments
+
+2002-01-28 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/renameClass.py: Added functionality to rename the
+ function and class ref in 'import a.b.theFunction and from a
+ import theClass' type statements
+
+ * bike/parsing/renameFunction.py: new refactoring
+
+ * bike/parsing/tokenhandler.py: Added code to update the typeinfo
+ objects of the parent scope and the Top node. This enables renames
+ to be carried out more than once on the same ast
+
+ * bike/parsing/addtypeinfo.py: Removed node.typeinfo.fqn. refactor
+ code should now use node.fqn
+
+2002-01-25 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/tokenhandler.py: Added token functionality to
+ Name. Removed the getPreceedingTokens() stuff - the getTokens()
+ now returns all tokens including preceeding comments, tokens
+ etc... getNodeTokens() returns just the tokens associated with the
+ node. This is consistent across all the tokenhandler base classes.
+
+ * bike/parsing/tokenutils.py: Fixed bug in _appendTokens where
+ the src 'raise foo' would result in 'raise' being picked up when
+ searching for 'foo' because both tokens are of the same type (name).
+
+2002-01-24 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/addtokens.py: Added code to class tokenizer to
+ delegate tokens to the baseclass spec (i.e. bah in
+ 'class foo(bah):').
+
+ * bike/parsing/tokenhandler.py: Class handler stuff to output
+ tokens (see above). This means that renameClass now renames class
+ refs in baseclass specs.
+
+2002-01-23 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/renameMethod.py: Fixed a bug which stopped classes
+ that inherited from classes not in the ast from having method
+ declarations renamed.
+
+2002-01-22 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/output.py: Added support for handling line breaks.
+ i.e. if foo and \
+ bah:
+
+ * bike/refactor/renameClass.py: Started new refactoring -
+ renameClass
+
+ * bike/parsing/addtypeinfo.py: removed 'typing indirect recursive
+ functions causes stack overflow' bug
+
+ * bike/refactor/renameMethod.py: Refactored the code so that the
+ renameMethodReferences is done once, with a list of all the
+ classes (in the hierarchy) to match.
+
+
+2002-01-21 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/renameMethod.py: Now renames methods on all
+ related classes (base classes and sub classes).
+
+ * doc/*: added some html documentation
+
+2002-01-19 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/renameMethod.py: Did some major refactoring and
+ removed all the code which is functionally duplicated in the
+ addtypeinfo module.
+
+ * bike/parsing/addtypeinfo.py: Spruced up the type inference
+ stuff.
+ ModuleTypeInfo now handles from foo import * by storing a list of
+ other modules to search.
+
+2002-01-17 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/output.py: Save now returns a list of the files modified
+
+2002-01-16 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/extended_ast.py: Added lazy tokenization. Source
+ node now parses the source (rather than being handed the ast).
+
+2002-01-15 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/output.py: Added lazy saving. Files are only saved
+ if source modified.
+
+ * bike/testutils.py: Added new utility to find an ast node in a
+ tree based on attributes of the node.
+ e.g. getMatchingASTNode(ast,"Function",name="foo")
+
+2002-01-14 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/renameMethod.py: Added functionality to look for
+ method and ref renames in sub classes
+
+2002-01-11 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/addtypeinfo.py: Added support for imports, import
+ from and import as statements
+
+2002-01-09 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/tokenutils.py: Fixed bug where method call was
+ split over 2 lines. The problem is that the tokenlist contains the
+ NL, but the parsetree doesnt, so the matching wasnt working. See
+ addtokens test_doesntBarfWhenMethodCallSplitOverTwoLines for details.
+
+ * bike/parsing/brmtransformer.py: Added 2 classes which override
+ Print and Println, and return the child nodes in the desired order
+ (they are the wrong way round as they come out of
+ ast.py). The print_stmt method returns these.
+
+ * bike/parsing/output.py: Added code to handle spacing with commas
+ and stream operators
+
+2002-01-08 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/test_renameMethod.py: Refactored tests so that
+ BikeGUI can use them (through template method). Tests are now
+ split into RenameMethodTests,
+ RenameMethodReferenceTests_ImportsClass and
+ RenameMethodReferenceTests_doesntImportClass. This is because
+ bikegui
+
+2002-01-07 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/ui/bikegui.py: Added dialog to ask user if want to rename
+ method reference (for cases where the type engine can't deduce the
+ type of the instance)
+
+2002-01-04 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/ui/bikegui.py: Added a simple gui for renameMethod
+
+2002-01-03 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/load.py: Added functionality to strip of preceeding
+ directories from package structures. (so if you load
+ '/usr/local/python2.2/compiler', the root package is 'compiler')
+
+ * bike/parsing/load.py: Added load_readonly(), which doesn't do the
+ addtokens step.
+ Also added code to enable load() to be called more than once, to
+ add new files and packages to the tree.
+ Moved ui messages into constants module
+
+2002-01-02 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/brmtransformer.py: removed addTokens() step from
+ parse(), so that it must be called seperately. This is because it
+ isn't complete, and imposes quite a performance penalty. (and it's
+ not needed by pyxmi). Updated all the tests to reflect this.
+
+2002-01-01 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/addtypeinfo.py : Added getSubClasses()
+ functionality to class typeinfo.
+
+2001-12-30 Phil Dawes <pdawes@users.sourceforge.net>
+ * bike/*: Fixed path bugs for windows platform. All tests run on
+ win32 python now.
+
+2001-12-29 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/addtypeinfo.py: Added 'self' type to function
+ typeinfo. This means renameMethod can handle self.theMethod()
+ automatically.
+
+2001-12-28 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * pyxmi: Wrote little tool to create xmi representation of python
+ code. Suitable for loading into argouml.
+
+ * bike/ui/bikecli.py: Added progress meter support. (also in
+ addtokens.py, renameMethod.py, load.py and output.py
+
+2001-12-26 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/renameMethod.py: Fixed bug where function call
+ returning instance resulted in crash. 'e.g. e().f()'
+
+2001-12-24 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/addtokens.py: Reworked addtokens scheme to 'get all
+ tokens for node, and then pass them to children to take their
+ nodes'. This makes for a much easier time processing tokens.
+
+2001-12-16 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/tokenhandler.py: Changed Getattr rename
+ functionality to work when attribute token isn't second token of
+ node tokenlist
+
+ * bike/refactor/test_renameMethod.py: refactored renameMethod
+ tests into generic abstract base class. These are now used by the
+ ui package (rather than vice versa)
+
+ * bike/ui/bikecli.py: Added code to tell you how many changes have
+ been made, and advice on what might be wrong if this number is 0.
+
+2001-12-15 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/tokenhandler.py: Added getNodeTokens()
+ implementation to miss out any preceeding cruft not picked up by
+ addtoken functionality on previous nodes.
+
+2001-12-14 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/renameMethod.py: Changed renameMethodDefinition to
+ work with nested methods (utilising the new typeinfo.fqn stuff).
+ Removed findclass and findmethod.
+
+2001-12-12 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/ui/bikecli.py: Modified prompt message a bit. Added actual
+ standard io stuff so that it works with a user input as well as
+ through the test harness.
+
+ * bike/refactor/renameMethod.py: Modified RenameMethodReferences
+ to create a stack of scopes. The code uses the typeinfo from each
+ scope to determine the type of references, and if it can't find
+ the type there, it checks its own references deduced from the
+ classname passed in. I.e. it assumes that the classname is a
+ class, even if it can't find the definition, and renames instances
+ derived from it.
+
+ Also ammended the 'prompt user callback' code to only ask the user
+ if the type engine doesnt know the type of the instance
+ expression. (before now it was prompting if the type was not the
+ same as the input classname)
+
+2001-12-11 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/ui/bikecli.py: Added code to handle prompting the user for
+ whether the method reference should be renamed.
+
+ * bike/refactor/renameMethod.py: Added code to prompt user when
+ type of object instance isn't known
+
+2001-12-08 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor.py: now takes advantage of typeinfo stuff to
+ deduce types of references
+
+2001-12-03 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/addtypeinfo.py: New classes to deduce the types of
+ references. Adds a typeinfo attribute to each scope ast node,
+ which maps reference names to their types (currently as strings).
+
+2001-11-26 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/renameMethod.py: Added stack to maintain
+ references to object instances while visiting package structure
+
+2001-11-25 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/renameMethod.py: Added support for 'from foo
+ import *'
+
+ * bike/ui/test_bikecli.py: Refactored tests so that all the single
+ file tests are exercised in a package hierarchy context.
+
+2001-11-24 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/renameMethod.py: Added support to rename methods
+ based on imported classes
+
+2001-11-23 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/renameMethod.py: Refactored code (took out
+ RenameMethod class, since wasnt using instance state). Added
+ support for compound references (a.b.c.foo)
+
+2001-11-21 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/load_application.py: removed. (see load.py instead)
+
+ * bike/refactor/renameMethod.py: Added support for fully qualified
+ classnames in _findClass. e.g. a.b.bah.foo. This means that the ui
+ now supports renaming package nested methods (but not their
+ references).
+
+2001-11-20 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing: Added fixes to parser module to reflect changes in
+ python 2.2 compiler module
+
+2001-11-16 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/output.py: new function 'save' saves an entire ast
+ tree
+
+ * bike/refactor/renameMethod.py: Adapted to take an ast rather
+ than source file
+
+2001-11-14 Phil Dawes <pdawes@bea.com>
+
+ * bike/parsing/load.py: Replacement for load_application.py using
+ Juergen's pythius loader code. load() hierarchically loads a bunch
+ of packages and modules into a big AST.
+
+ * bike/parsing/extended_ast.py: Fleshed out Package and Top nodes
+ to support hierarchical module ast
+
+
+2001-11-08 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/load_application.py: Fixed import visitor code to
+ handle comma seperated imports. (i.e. import foo,bar). Added code
+ to recognise imports via a from clause (i.e. from foo import bar)
+
+ Removed Imports.py. Integrated code into load_application
+
+
+2001-11-06 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/load_application.py: Added load_application and
+ Imports modules back into build. Started writing tests for them.
+
+
+2001-11-05 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/brmtransformer.py : Rewrote this to override the
+ methods which create nodes at the root of the parse. This means
+ that a few methods adding parser nodelists to ast nodes should
+ cater for practically all the AST nodes.
+
+ * bike/parsing/tokenhandler.py (GetattrTokenHander.renameAttr):
+ added renameAttr method which renames the attribute in the object
+ state, and in the corresponding tokens. This is a very brittle
+ implementation, but will be fixed as new functionality is
+ added. (it's the simplist thing right now)
+
+ * bike/parsing/addtokens.py (AddTokens.visitGetattr): added
+ token support for getattr
+
+ * bike/parsing/tokenutils.py: Added getTokensPreceedingNodes,
+ which takes a parsetree of nodes and returns all the tokens before
+ the node represented by the parsetree. This is important because
+ until there is an token-handling implementation of all the ast
+ nodes, some tokens will be missed. By eating the tokens in between
+ the implemented ast nodes, this ensures all the tokens will be
+ outputted at the end.
+
+
+2001-10-30 Phil Dawes <pdawes@users.sourceforge.net>
+
+ Created new CVS module, which makes use of distutils much more
+ uniform.
+
+ * setup.py: Created distutils setup stript:
+
+2001-10-25 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * parsing/output.py: Code to handle spacing in inline comments
+
+ * refactor/renameMethod.py: Started code to handle references when
+ object is created in same scope
+
+
+2001-10-24 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * parsing/output.py: spaces in conditionals (a == b etc..)
+
+ * parsing/tokenutils.py: Refactored code in TokenList to handle
+ rearranging indent/dedent tokens so that whole-line comments are
+ indented with the things they comment. The transformations are now
+ done after the initial scan (slower, but easier to follow)
+
+
+2001-10-23 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * parsing/output.py: ensured a.b not seperated by spaces.
+
+
+2001-10-22 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * parsing/output.py: Code to seperate a=b with spaces (a = b)
+
+ * ui/test_cli.py: Merged cli tests with renameMethod tests to
+ ensure all tests work through the cli
+
+2001-10-19 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * */testall.py: unified testing scripts so that all tests can be
+ run from one place
+
+2001-10-15 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ui/cli.py: Added simple command line interface.
+
+2001-10-14 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * parsing/output.py: Finished simple ast->string functionality
+
+2001-10-13 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * testall.py: added master test script
+
+ * parsing/tokenhandler.py (FunctionTokenHandler.rename): added
+ rename a function and keep tokens in sync functionality
+
+
+2001-10-12 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * parsing/output.py: started tokens -> string functionality
+
+2001-10-07 Phil Dawes <pdawes@users.sourceforge.net>
+ * refactor/renameMethod.py: very basic findClass, findMethod
+ functionality. Needs refactoring into analysis package, and
+ merging with existing code
+
+2001-10-05 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * refactor/simplediff.py: wrote a simple diff utility to support
+ writing top down tests. (i.e. do a renameMethod, then assert that
+ the diff is correct)
+
+
+2001-10-04 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * refactor/renameMethod.py: Got fedup with trying to secondguess
+ what the parser module should do. Decided to do some top-down
+ proper XP style development. Started with implementing the
+ renameMethod story.
+
+2001-10-01 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * parsing/test_addtokens.py: refactored tests so that they use
+ brmtransformer to get a node, rather than mocking it.
+
+2001-09-29 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * parsing/brmtransformer.py: Added nodelist to pass_stmt
+
+2001-09-28 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * parsing/addtokens.py (AddTokens.visitModule): finished method
+
+ * parsing/tokenutils.py
+ (TokenConverter.getTokensUptoLastNewlineBeforeCode): method added
+
+2001-09-27 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * parsing/addtokens.py: started visitmodule() impl
+
+ * parsing/extended_ast.py: Added tokens support to Module
+
+2001-09-26 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * parsing/test_addtokens.py Refactored tests. Made up better names.
+
+2001-09-25 Phil Dawes <pdawes@users.sourceforge.net>
+
+ Laptop broke. Continuing with an older version of the code (hence
+ the gap in changelog)
+
+ * parsing/tokenutils.py (TokenConverter.getTokensToColon): Added
+ function which just gobbles tokens to the next colon. Refactored
+ getTokensStartBlock() to use this instead of walking the nodelist,
+ since I think all
+
+ * parsing/addtokens.py (AddTokens.visitFunction): Now that
+ function and class no longer need to pass in partial nodelists,
+ the code is common. Refactored common code into
+ visitCompoundNode().
+
+ (hence the gap in the changelog)
+
+ * parsing/tokenhandler.py: removed the preceeding tokens code -
+ you ain't gonna need it!
+
+ * parsing/test_addtokens.py: changed visitclass tests to use
+ mockobjects
+
+2001-09-10 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * parsing/tokenhandler.py:
+
+ * parsing/*: Integrated code with bike parsing package
+
+ Split tokenhandler base classes into seperate module
+ Merged brmast stuff into extended_ast package
+
+
+2001-09-08 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * test_tokenutils.py: Added more unit tests
+
+2001-09-06 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * test_tokenutils.py: Added more unit tests
+
+
+2001-09-04 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * test_addtokens.py: Added some mock objects and tests
+
+2001-08-31 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * test_tokenutils.py: beefed up the unit tests a bit
+
+2001-08-29 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * brmtransformer.py: This module now just contains code to add the
+ nodelist member to each ast node as the compiler generates
+ it. This is so the code in addtokens has access to the original
+ python parser nodes to generate tokens from.
+
+ * addtokens.py: Moved the code to add the tokens to the ast nodes
+ to this module. It now relies on there being a 'nodelist' member
+ on each ast object, containing the original python parser
+ nodelist.
+
+ * brmast.py: Added hack to retrofit TokenHandler base class to
+ existing compiler.ast classes. This means that we no longer have
+ to include compiler classes with the distribution.
+ Not sure if this is completely legal - doesn't work with jython.
+
+ Removed stock Python compiler classes from src tree.
+
+
+2001-08-27 Phil Dawes <pdawes@users.sourceforge.net>
+ Set about reimplementing the token stuff - will now add tokens to
+ ast objects *after* the nodes have been processed by the compiler
+ module.
+
+2001-08-22 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * tokenutils.py: Added support for tokenising strings as well as
+ files.
+
+ * test_tokenutils.py: removed testdata directory, and moved all
+ the testdata into this module, now that the tokenutils stuff can
+ take strings as well as files
+
+ * brmtransformer.py: Renamed TransformerWithTokens to Transformer,
+ now that it's in a seperate module.
+
+ * brmast.py: Refactored the added token code out of ast module
+ into this one. Created a seperate mixin called TokenHandler rather
+ than sticking this code in the Node base class. Each class now
+ re-inherits this baseclass.
+ Unfortunately this means that I had to modify the transformer.py
+ file to import brmast rather than ast. Need to think of a better
+ way of doing this.
+
+2001-08-21 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * brmtransformer.py: Created new brmtransformer module and
+ refactored token code from transformer.Transformer into seperate
+ class TransformerWithTokens. Refactored common code from
+ funcdef() and classdef() into do_compound_stmt()
+
+ * transformer.py: Now that all new code is factored out, reverted
+ to original transformer module from Python-2.1/Tools/compiler.
+
+ * tokenutils.py: Added code to alter DEDENT token line numbers
+ when they are rearranged
+
+2001-08-19 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * tokenutils.py: Added code to do token reordering to ensure that
+ comments immediately before a block of code are placed after the
+ previous block DEDENT token.
+
+2001-08-15 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * transformer.py: tidied up code, and released prototype
+
+2001-08-14 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ast.py: Refactored management of tokens into base Node
+ class. Removed code from Class, and moved node->token conversion
+ to transformer.py
+
+ * transformer.py: See above
+
+2001-08-13 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ast.py: Added support for tokens in 'Class' nodes. 'Class' uses
+ nodes passed to convert to tokens using TokenConverter.
+
+
+2001-08-10 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * tokenutils.py: Started TokenConverter class to convert python
+ parser nodes to tokens.
+
+ * test_tokenutils.py: unit tests for above
+
+------------------ One year later... -------------------------
+
+
+2000-10-21 19:02 gwilder
+
+ * context.py, refactor/.cvsignore, refactor/NewClass.py,
+ refactor/Refactor.py, refactor/__init__.py:
+
+ first refactoring: NewClass
+
+2000-10-21 18:56 gwilder
+
+ * kickstand/: test_HasFromStar.py, test_IsClass.py,
+ test_IsGlobal.py, test_all.py, test_context.py,
+ test_load_application.py, testdata/HasFromStar_tf1.py,
+ testdata/HasFromStar_tf2.py, testdata/IsGlobal_tf1.py,
+ testdata/load_application_tf1.expected,
+ testdata/load_application_tf1.py,
+ testdata/rf_addclass_tf1.expected, testdata/rf_addclass_tf1.py:
+
+ more tests
+
+2000-10-21 18:55 gwilder
+
+ * analysis/: HasFromStar.py, IsClass.py, IsGlobal.py,
+ Superclasses.py, TODO:
+
+ more analysis functions
+
+2000-10-21 18:38 gwilder
+
+ * parsing/: .cvsignore, Imports.py, README, TODO, __init__.py,
+ extended_ast.py, load_application.py:
+
+ parse and load a whole app.
+
+2000-10-13 19:32 gwilder
+
+ * INSTALL, analysis/.cvsignore, analysis/IsClass.py,
+ analysis/IsGlobal.py, analysis/README, analysis/TODO,
+ analysis/__init__.py, assembly/genpy.py, kickstand/test_IsClass.py,
+ kickstand/test_IsGlobal.py, kickstand/test_all.py,
+ kickstand/testdata/.cvsignore, kickstand/testdata/IsClass_tf1.py,
+ kickstand/testdata/IsGlobal_tf1.py:
+
+ new analysis functions; install instructions; testsuite additions.
+
+2000-09-28 00:59 eris
+
+ * .cvsignore, CHANGES, README, assembly/.cvsignore,
+ disassembly/.cvsignore, kickstand/.cvsignore,
+ kickstand/test_common.py, kickstand/treefailtest.py,
+ kickstand/treematchtest.py, kickstand/unequalvars.py,
+ sprocket/.cvsignore, sprocket/common.py, sprocket/common.txt:
+
+
+ added .cvsignore files in each dir got the unit test for
+ sprocket/common.py working, added files for that test.
+
+2000-09-06 01:21 jhermann
+
+ * CHANGES, INSTALL, README, TODO:
+
+ Added administrative files
+
+2000-09-05 22:13 jhermann
+
+ * assembly/__init__.py, disassembly/__init__.py,
+ kickstand/__init__.py, sprocket/__init__.py:
+
+ Made the inits non-empty
+
+2000-09-05 22:11 jhermann
+
+ * __init__.py:
+
+ Minor correction
+
+2000-09-01 18:33 jhermann
+
+ * kickstand/__init__.py, kickstand/test_fsm.py,
+ kickstand/test_regast.py, sprocket/__init__.py, sprocket/common.py,
+ sprocket/fsm.py, sprocket/regast.py:
+
+ Initial source checkin (2000-09-01)
+
+2000-09-01 18:26 jhermann
+
+ * .cvsignore, __init__.py, assembly/__init__.py, assembly/genpy.py,
+ disassembly/__init__.py, disassembly/ast.py, disassembly/consts.py,
+ disassembly/transformer.py:
+
+ Initial source checkin (2000-09-01)
+
+2000-09-01 18:22 jhermann
+
+ * README:
+
+ Additions by shae
+
+2000-08-19 01:35 eris
+
+ * README:
+
+
+ new dir structure by snibril aka Jürgen Herrman first stab at a
+ tree matcher in common.py
+
+2000-08-01 01:44 jhermann
+
+ * README:
+
+ Added a readme dummy
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/INSTALL Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,17 @@
+Bicycle Repair Man - a Python Refactoring Browser
+=================================================
+
+$Id: INSTALL,v 1.4 2002/03/29 13:10:49 pdawes Exp $
+
+-----------------------------------------------------------------------------
+
+Bicycle Repair Man requires Python 2.2 and above.
+
+Run
+% python setup.py install
+
+
+Then look for at the relevant README file to integrate Bicycle Repair
+Man with your IDE.
+
+-----------------------------------------------------------------------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/NEWS Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,98 @@
+Main highlights of each new version. See ChangeLog for a more detailed
+description of changes.
+
+Version 0.9
+-----------
+
+This version removes the requirement to load files into
+bicyclerepairman before refactoring. Instead, it searches the
+PYTHONPATH (and the path in which the file being queried is in). This
+allows 'findDefinition' queries to be made on references
+(e.g. classes, methods) where the definition is in the python library.
+
+
+Version 0.8
+-----------
+
+This release improves on the internal type-deduction engine to handle
+variables and attributes. To reflect this, 'rename' now works on
+variables and attributes, in addition to methods, functions and
+classes. This release also adds vim support (thanks to Marius Gedminas
+and Matt Yeates for this)
+
+Version 0.7
+-----------
+
+This release includes a totally re-written type querying engine, which
+is much faster and paves the way for new refactorings. It also adds
+the 'FindReferences' and 'FindDefinition' query to emacs and idle.
+
+
+Version 0.6
+-----------
+
+This release adds undo functionality to the mix, and beefs up the idle
+and emacs integration so that code is automatically imported into brm
+when you load a file into a buffer.
+
+
+Version 0.5
+-----------
+
+This release adds the ExtractMethod refactoring
+
+
+Version 0.4
+-----------
+
+This release adds support for IDLE (see README.idle), and fixes a few
+bugs. The CLI and GUI interfaces are now deprecated and have been
+removed from this release.
+
+
+
+Version 0.3
+-----------
+
+This release adds the RenameClass and RenameFunction refactorings.
+It also contains the initial xemacs integration functionality (see
+README.xemacs).
+
+
+
+Version 0.2
+-----------
+
+This release adds a simple GUI for renaming methods - run bikegui.py
+after installation.
+
+There's also some upgrades to pyxmi. It should now be able to generate
+xmi to model all generalizations (including cross-package ones).
+N.B. pyxmi.py is now called py2xmi.py. See changelog for reasons!
+
+
+
+Version 0.1
+-----------
+
+This is the first release of Bicycle Repair Man. It requires python
+version 2.2 and above.
+
+This version supports a partial implementation of the RenameMethod
+refactoring through a command line interface.
+
+It automatically renames the method and references to the method that
+it can deduce. It asks you about method references it can't deduce the
+instance type of.
+
+This software should be considered alpha, and may damage your source
+files - backup your sources before use!
+
+See INSTALL for installation and usage instructions.
+
+
+N.B. This package also contains pyxmi - a little python -> xmi tool I
+cobbled together out of the bicycle repair man parsing package. It
+generates an xmi file out of a source-file or package-structure,
+suitable for loading into the argouml tool. See
+http://argouml.tigris.org.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/PKG-INFO Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,10 @@
+Metadata-Version: 1.0
+Name: bicyclerepair
+Version: 0.9
+Summary: Bicycle Repair Man, the Python refactoring tool
+Home-page: http://bicyclerepair.sourceforge.net
+Author: Phil Dawes
+Author-email: pdawes@users.sourceforge.net
+License: UNKNOWN
+Description: UNKNOWN
+Platform: UNKNOWN
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/README Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,70 @@
+Bicycle Repair Man - a Python Refactoring Browser
+=================================================
+
+Copyright (c) 2000 by Shae Erisson <shae@lapland.fi>
+Copyright (c) 2001-3 by Phil Dawes <pdawes@users.sourceforge.net>
+
+All rights reserved, see COPYING for details.
+
+$Id: README,v 1.7 2003/08/24 19:48:43 pdawes Exp $
+
+-----------------------------------------------------------------------------
+
+Bicycle Repair Man is the Python Refactoring Browser, helping
+Pythonistas everywhere glide over the gory details of refactoring their
+code. Watch him extract jumbled code into well ordered classes. Gasp, as
+he renames all occurrences of a method. Thank You, Bicycle Repair Man!
+
+execute ./testall.py to run all the tests
+
+see INSTALL for installation instructions (uses distutils).
+
+see README.idle, README.emacs etc.. for instructions on how to
+integrate bicyclerepairman into supported IDEs.
+
+-----------------------------------------------------------------------------
+
+
+What's Python?
+
+Python is a programming language. To find out more
+about it, go to the Python Homepage at http://www.python.org/
+
+
+What's a Refactoring Browser?
+
+A Refactoring Browser is an editor that automates Refactorings. The
+first Refactoring Browser was written by Dr. Don Roberts and Dr. John
+Brant at the University of Illinois in Urbana-Champagne. Dr. Don
+Roberts wrote his Ph.D. thesis on the design and implementation of the
+Refactoring Browser. For more detail, read the aforementioned thesis
+at http://st-www.cs.uiuc.edu/~droberts/thesis.pdf
+
+
+What's a Refactoring?
+
+A Refactoring is a behaviour preserving change to source code. Some
+Refactorings are RenameVariable, RenameClass, RenameMethod,
+PullUpMethod, and PushDownVariable. Lots of people say it's very easy
+to just type a different name in where your class, method, or variable
+is defined. That's not always a refactoring though. The Refactoring
+Browser is smart enough to rename every reference to your class,
+method or variable. If you've ever renamed a variable and broken
+classes in widely scattered parts of your system, you might be happier
+using a Refactoring Browser. A Refactoring Browser operates on any of
+method, function, class, or variable. It can add, delete, rename, move
+up down or sideways, inline and abstract. There are some operations
+that are specific to one of the three types, such as abstracting a
+variable into accessors, or turing several lines of code into a
+separate method.
+
+For more information on Refactoring, check out the websites of Martin
+Fowler at http://www.martinfowler.com/ and his Refactoring Site at
+http://www.refactoring.com/ .
+
+
+Why Bicycle Repair Man?
+
+The Bicycle Repair Man was a superhero in a Monty Python skit, his
+special power was repairing bicycles.
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/README.emacs Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,115 @@
+Instructions for running Bicycle Repair Man through emacs/xemacs
+----------------------------------------------------------------
+
+N.B. You need xemacs / emacs 21 or above.
+
+The emacs integration utilises the excellent 'Pymacs' package, written
+by François Pinard, which integrates python with emacs. A copy of this
+software is included with this package.
+
+
+There are 3 steps to installing bicyclerepairman for emacs:
+
+1) Install the base bicyclerepairman package
+
+2) Install the Pymacs package (if you haven't already got it)
+
+3) Modify your .emacs to active the bicyclerepairman functionality
+
+See the sections below for instructions on doing each of these.
+
+WINDOWS USERS:
+You need to have both the python executable and the scripts directory
+(e.g. c:\Python22/Scripts) in your path for Bicyclerepairman to work.
+
+There are a couple of niggles with brm/emacs on
+windows. See the comments at the end of this file.
+
+
+
+
+
+
+
+1) Installation of Base Bicyclerepairman:
+-----------------------------------------
+
+- install bicyclerepair man as per INSTALL
+
+
+2) Installation of Pymacs:
+--------------------------
+
+(You can skip this if you already have pymacs installed.
+
+- Go to the ide-integration/Pymacs-0.20 directory
+- Run
+ python setup.py install
+- Run
+ python setup-emacs.py -l <LISP DIR>
+ OR
+ python setup-emacs.py -E xemacs -l <LISP DIR>
+ Depending on your version of emacs.
+
+- Add the following into your .emacs or .xemacs/init.el:
+
+;; pymacs
+(autoload 'pymacs-load "pymacs" nil t)
+(autoload 'pymacs-eval "pymacs" nil t)
+(autoload 'pymacs-apply "pymacs")
+(autoload 'pymacs-call "pymacs")
+
+- Check that it has installed correctly:
+ (Taken from the pymacs README)
+
+
+ To check that `pymacs.el' is properly installed, start Emacs and give
+ it the command `M-x load-library RET pymacs': you should not receive
+ any error.
+
+ To check that `pymacs.py' is properly installed, start
+ an interactive Python session (e.g. from a command shell) and type
+ `from Pymacs import lisp': you should not receive any error.
+
+ To check that `pymacs-services' is properly installed, type
+ `pymacs-services' in a shell; you should then get a line ending
+ with "(pymacs-version VERSION)". Press ctrl-c to exit.
+
+
+If you have any problems, consult the README file included with the
+pymacs distribution. N.B. I renamed the setup script to setup-emacs.py
+to make it more intuitive and easier for windows users. I've also
+added a pymacs-services.bat file to allow it to run on windows.
+
+
+3) Activating the Bike/Emacs integration
+----------------------------------------
+
+Add the following to your .emacs or .xemacs/init.el, after the pymacs
+stuff:
+
+(pymacs-load "bikeemacs" "brm-")
+(brm-init)
+
+
+You need to be using python-mode for the bicyclerepairman menu to
+appear. If you haven't already, enable this with:
+
+(autoload 'python-mode "python-mode" "Python editing mode." t)
+(setq auto-mode-alist
+ (cons '("\\.py$" . python-mode) auto-mode-alist))
+
+
+
+Usage:
+------
+
+Load a python file into emacs. A BicycleRepairMan menu should appear.
+
+
+Windows GNU-Emacs users
+-----------------------
+
+The load dialog in windows GNU-Emacs doesn't seem to allow selection
+of directories. If this is the case for you, use 'M-x brm-load' to
+import a package hierarchy.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/README.idle Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,47 @@
+BicycleRepairMan_Idle.py is the name of the idle integration module.
+
+
+Installation on Python 2.3 or idlefork
+--------------------------------------
+1) Install Bicycle Repair Man (see INSTALL)
+
+2) Add the following to your ~/.idlerc/config-extensions.cfg:
+(NOTE - I had to put this in my
+<PYTHONLIBDIR>/idlelib/config-extensions.def for it to work correctly)
+
+[BicycleRepairMan_Idle]
+enable=1
+trace=0
+[BicycleRepairMan_Idle_cfgBindings]
+brm-find-references=
+brm-find-definition=
+brm-rename=
+brm-extract-method=
+brm-undo=
+
+Python sourcefile editor windows will now have a 'BicycleRepairMan'
+menu.
+
+
+
+Installation on python 2.2 version of idle
+------------------------------------------
+
+1) Install Bicycle Repair Man (see INSTALL)
+
+2) Add the following line to your <idle-installation>/config.txt or
+~/.idle:
+
+[BicycleRepairMan_Idle]
+
+
+Python sourcefile editor windows will now have a 'BicycleRepairMan'
+menu.
+
+
+
+Caveats
+-------
+
+Sometimes dialogs get lost behind windows. If things seem to have
+paused in a rename-method, try looking for a dialog.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/README.vim Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,2 @@
+Instructions for installing bike.vim are in the file itself.
+See ide-integration/bike.vim
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/__init__.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,10 @@
+# The root bicyclerepairman package
+# do:
+# ---------------------
+# import bike
+# ctx = bike.load()
+# ---------------------
+# to instantiate a bicyclerepairman context object
+
+
+from bikefacade import init, NotAPythonModuleOrPackageException, CouldntLocateASTNodeFromCoordinatesException, UndoStackEmptyException
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/bikefacade.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,274 @@
+import os
+import sys
+import compiler
+from parser import ParserError
+from bike.parsing.pathutils import getRootDirectory
+from bike.refactor import extractMethod
+from bike.refactor.rename import rename
+from bike.refactor.extractMethod import coords
+from bike.transformer.save import save as saveUpdates
+from bike.parsing.utils import fqn_rcar, fqn_rcdr
+from bike.parsing import visitor
+from bike.transformer.undo import getUndoStack, UndoStackEmptyException
+from bike.parsing.fastparserast import getRoot, Class, Function
+from bike.query.common import getScopeForLine
+from bike.query.getTypeOf import getTypeOfExpr, UnfoundType
+from bike.query.findReferences import findReferences
+from bike.query.findDefinition import findAllPossibleDefinitionsByCoords
+from bike.refactor import inlineVariable, extractVariable, moveToModule
+from bike.parsing.load import Cache
+from bike import log
+
+def init():
+ #context = BRMContext_impl()
+ context = BRMContext_wrapper()
+ return context
+
+# the context object public interface
+class BRMContext(object):
+
+
+ def save(self):
+ """ save the changed files out to disk """
+
+ def setRenameMethodPromptCallback(self, callback):
+ """
+ sets a callback to ask the user about method refs which brm
+ can't deduce the type of. The callback must be callable, and
+ take the following parameters:
+ - filename
+ - linenumber
+ - begin column
+ - end column
+ (begin and end columns enclose the problematic method call)
+ """
+
+ def renameByCoordinates(self, filename_path, line, col, newname):
+ """ an ide friendly method which renames a class/fn/method
+ pointed to by the coords and filename"""
+
+ def extract(self, filename_path,
+ begin_line, begin_col,
+ end_line, end_col,
+ name):
+ """ extracts the region into the named method/function based
+ on context"""
+
+ def inlineLocalVariable(self,filename_path, line, col):
+ """ Inlines the variable pointed to by
+ line:col. (N.B. line:col can also point to a reference to the
+ variable as well as the definition) """
+
+
+ def extractLocalVariable(self,filename_path, begin_line, begin_col,
+ end_line, end_col, variablename):
+ """ Extracts the region into a variable """
+
+ def setProgressLogger(self,logger):
+ """ Sets the progress logger to an object with a write method
+ """
+
+ def setWarningLogger(self,logger):
+ """ Sets the warning logger to an object with a write method
+ """
+
+ def undo(self):
+ """ undoes the last refactoring. WARNING: this is dangerous if
+ the user has modified files since the last refactoring.
+ Raises UndoStackEmptyException"""
+
+ def findReferencesByCoordinates(self, filename_path, line, column):
+ """ given the coords of a function, class, method or variable
+ returns a generator which finds references to it.
+ """
+
+ def findDefinitionByCoordinates(self,filename_path,line,col):
+ """ given the coordates to a reference, tries to find the
+ definition of that reference """
+
+ def moveClassToNewModule(self,filename_path, line,
+ newfilename):
+ """ moves the class pointed to by (filename_path, line)
+ to a new module """
+
+
+class NotAPythonModuleOrPackageException: pass
+class CouldntLocateASTNodeFromCoordinatesException: pass
+
+
+# Wrapper to ensure that caches are purged on each request
+class BRMContext_wrapper:
+ def __init__(self):
+ self.brmctx = BRMContext_impl()
+
+ def __getattr__(self,name):
+ return BRMContext_callWrapper(self.brmctx,name)
+
+
+class BRMContext_callWrapper:
+ def __init__(self,brmctx,methodname):
+ self.name = methodname
+ self.brmctx = brmctx
+
+ def __call__(self,*args):
+ Cache.instance.reset()
+ try:
+ return getattr(self.brmctx,self.name)(*args)
+ finally:
+ Cache.instance.reset()
+
+
+class BRMContext_impl(BRMContext):
+
+ def __init__(self):
+ self.ast = getRoot()
+
+ # Used because some refactorings delegate back to the user.
+ # this flag ensures that code isnt imported during those times
+ self.readyToLoadNewCode = 1
+ self.paths = []
+ getUndoStack(1) # force new undo stack
+ if not getRoot().unittestmode:
+ log.warning = sys.stderr
+ self.promptUserClientCallback = None
+
+ def _getAST(self):
+ return self.ast
+
+ # returns a list of saved filenames
+ def save(self):
+ savedfiles = saveUpdates()
+ return savedfiles
+
+ def setRenameMethodPromptCallback(self, callback):
+ self.promptUserClientCallback = callback
+
+
+ def normalizeFilename(self,filename):
+ filename = os.path.expanduser(filename)
+ filename = os.path.normpath(os.path.abspath(filename))
+ return filename
+
+ def extractMethod(self, filename_path,
+ begin_line, begin_column,
+ end_line, end_column,
+ methodname):
+ self.extract(filename_path, begin_line, begin_column,
+ end_line, end_column,methodname)
+
+ def extractFunction(self, filename_path,
+ begin_line, begin_column,
+ end_line, end_column,
+ methodname):
+ self.extract(filename_path, begin_line, begin_column,
+ end_line, end_column,methodname)
+
+ # does it based on context
+ def extract(self, filename_path,
+ begin_line, begin_col,
+ end_line, end_col,
+ name):
+ filename_path = self.normalizeFilename(filename_path)
+ extractMethod.extractMethod(filename_path,
+ coords(begin_line, begin_col),
+ coords(end_line, end_col), name)
+
+ def inlineLocalVariable(self,filename_path, line, col):
+ filename_path = self.normalizeFilename(filename_path)
+ inlineVariable.inlineLocalVariable(filename_path,line,col)
+
+ def extractLocalVariable(self,filename_path, begin_line, begin_col,
+ end_line, end_col, variablename):
+ filename_path = self.normalizeFilename(filename_path)
+ extractVariable.extractLocalVariable(filename_path,
+ coords(begin_line, begin_col),
+ coords(end_line, end_col),
+ variablename)
+
+ def moveClassToNewModule(self,filename_path, line,
+ newfilename):
+ filename_path = self.normalizeFilename(filename_path)
+ newfilename = self.normalizeFilename(newfilename)
+ moveToModule.moveClassToNewModule(filename_path, line,
+ newfilename)
+
+ def undo(self):
+ getUndoStack().undo()
+
+ def _promptUser(self, filename, lineno, colbegin, colend):
+ return self.promptUserClientCallback(filename, lineno, colbegin, colend)
+
+
+ # must be an object with a write method
+ def setProgressLogger(self,logger):
+ log.progress = logger
+
+ # must be an object with a write method
+ def setWarningLogger(self,logger):
+ log.warning = logger
+
+
+ # filename_path must be absolute
+ def renameByCoordinates(self, filename_path, line, col, newname):
+ filename_path = self.normalizeFilename(filename_path)
+ Cache.instance.reset()
+ try:
+ self._setNonLibPythonPath(filename_path)
+ rename(filename_path,line,col,newname,
+ self.promptUserClientCallback)
+ finally:
+ Cache.instance.reset()
+
+ def _reverseCoordsIfWrongWayRound(self, colbegin, colend):
+ if(colbegin > colend):
+ colbegin,colend = colend,colbegin
+ return colbegin,colend
+
+
+ def findDefinitionByCoordinates(self,filename_path,line,col):
+ filename_path = self.normalizeFilename(filename_path)
+ self._setCompletePythonPath(filename_path)
+ return findAllPossibleDefinitionsByCoords(filename_path,line,col)
+
+
+ # filename_path must be absolute
+ def findReferencesByCoordinates(self, filename_path, line, column):
+ filename_path = self.normalizeFilename(filename_path)
+ self._setNonLibPythonPath(filename_path)
+ return findReferences(filename_path,line,column)
+
+ def refreshASTFromFileSystem(self):
+ for path in self.paths:
+ self.ast = loadast(path, self.ast)
+
+ def _setCompletePythonPath(self,filename):
+ pythonpath = [] + sys.path # make a copy
+ self.ast.pythonpath = pythonpath
+
+ def _setNonLibPythonPath(self,filename):
+ if getRoot().unittestmode:
+ return
+ pythonpath = self._removeLibdirsFromPath(sys.path)
+ pythonpath = [os.path.abspath(p) for p in pythonpath]
+ self.ast.pythonpath = pythonpath
+
+ def _getCurrentSearchPath(self):
+ return self.ast.pythonpath
+
+ def _removeLibdirsFromPath(self, pythonpath):
+ libdir = os.path.join(sys.prefix,"lib").lower()
+ pythonpath = [p for p in pythonpath
+ if not p.lower().startswith(libdir)]
+ return pythonpath
+
+
+def _deducePackageOfFile(filename):
+ package = ""
+ dot = ""
+ dir = os.path.dirname(filename)
+ while dir != ""and \
+ os.path.exists(os.path.join(dir, "__init__.py")):
+ dir, dirname = os.path.split(dir)
+ package = dirname+dot+package
+ dot = "."
+ return package
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/globals.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,6 @@
+try:
+ True = 1
+ False = 0
+except:
+ pass
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/log.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,8 @@
+import sys
+class SilentLogger:
+ def write(*args):
+ pass
+
+progress = SilentLogger()
+warning = SilentLogger()
+#warning = sys.stderr
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/logging.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,1995 @@
+#! /usr/bin/env python
+#
+# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved.
+#
+# 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 Vinay Sajip
+# not be used in advertising or publicity pertaining to distribution
+# of the software without specific, written prior permission.
+# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# VINAY SAJIP 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.
+#
+# For the change history, see README.txt in the distribution.
+#
+# This file is part of the Python logging distribution. See
+# http://www.red-dove.com/python_logging.html
+#
+
+"""
+Logging module for Python. Based on PEP 282 and comments thereto in
+comp.lang.python, and influenced by Apache's log4j system.
+
+Should work under Python versions >= 1.5.2, except that source line
+information is not available unless 'inspect' is.
+
+Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved.
+
+To use, simply 'import logging' and log away!
+"""
+
+import sys, os, types, time, string, socket, cPickle, cStringIO
+
+from SocketServer import ThreadingTCPServer, StreamRequestHandler
+
+
+try:
+ import thread
+except ImportError:
+ thread = None
+try:
+ import inspect
+except ImportError:
+ inspect = None
+
+__author__ = "Vinay Sajip <vinay_sajip@red-dove.com>"
+__status__ = "alpha"
+__version__ = "0.4.5"
+__date__ = "4 June 2002"
+
+#---------------------------------------------------------------------------
+# Miscellaneous module data
+#---------------------------------------------------------------------------
+
+#
+#_srcfile is used when walking the stack to check when we've got the first
+# caller stack frame.
+#If run as a script, __file__ is not bound.
+#
+if __name__ == "__main__":
+ _srcfile = None
+else:
+ if string.lower(__file__[-4:]) in ['.pyc', '.pyo']:
+ _srcfile = __file__[:-4] + '.py'
+ else:
+ _srcfile = __file__
+ _srcfile = os.path.normcase(_srcfile)
+
+#
+#_startTime is used as the base when calculating the relative time of events
+#
+_startTime = time.time()
+
+#
+# Some constants...
+#
+
+DEFAULT_TCP_LOGGING_PORT = 9020
+DEFAULT_UDP_LOGGING_PORT = 9021
+DEFAULT_HTTP_LOGGING_PORT = 9022
+DEFAULT_SOAP_LOGGING_PORT = 9023
+DEFAULT_LOGGING_CONFIG_PORT = 9030
+SYSLOG_UDP_PORT = 514
+
+#---------------------------------------------------------------------------
+# Level related stuff
+#---------------------------------------------------------------------------
+#
+# Default levels and level names, these can be replaced with any positive set
+# of values having corresponding names. There is a pseudo-level, ALL, which
+# is only really there as a lower limit for user-defined levels. Handlers and
+# loggers are initialized with ALL so that they will log all messages, even
+# at user-defined levels.
+#
+CRITICAL = 50
+FATAL = CRITICAL
+ERROR = 40
+WARN = 30
+INFO = 20
+DEBUG = 10
+ALL = 0
+
+_levelNames = {
+ CRITICAL : 'CRITICAL',
+ ERROR : 'ERROR',
+ WARN : 'WARN',
+ INFO : 'INFO',
+ DEBUG : 'DEBUG',
+ ALL : 'ALL',
+ 'CRITICAL' : CRITICAL,
+ 'ERROR' : ERROR,
+ 'WARN' : WARN,
+ 'INFO' : INFO,
+ 'DEBUG' : DEBUG,
+ 'ALL' : ALL,
+}
+
+def getLevelName(lvl):
+ """
+ Return the textual representation of logging level 'lvl'. If the level is
+ one of the predefined levels (CRITICAL, ERROR, WARN, INFO, DEBUG) then you
+ get the corresponding string. If you have associated levels with names
+ using addLevelName then the name you have associated with 'lvl' is
+ returned. Otherwise, the string "Level %s" % lvl is returned.
+ """
+ return _levelNames.get(lvl, ("Level %s" % lvl))
+
+def addLevelName(lvl, levelName):
+ """
+ Associate 'levelName' with 'lvl'. This is used when converting levels
+ to text during message formatting.
+ """
+ _acquireLock()
+ try: #unlikely to cause an exception, but you never know...
+ _levelNames[lvl] = levelName
+ _levelNames[levelName] = lvl
+ finally:
+ _releaseLock()
+
+#---------------------------------------------------------------------------
+# Thread-related stuff
+#---------------------------------------------------------------------------
+
+#
+#_lock is used to serialize access to shared data structures in this module.
+#This needs to be an RLock because fileConfig() creates Handlers and so
+#might arbitrary user threads. Since Handler.__init__() updates the shared
+#dictionary _handlers, it needs to acquire the lock. But if configuring,
+#the lock would already have been acquired - so we need an RLock.
+#The same argument applies to Loggers and Manager.loggerDict.
+#
+_lock = None
+
+def _acquireLock():
+ """
+ Acquire the module-level lock for serializing access to shared data.
+ This should be released with _releaseLock().
+ """
+ global _lock
+ if (not _lock) and thread:
+ import threading #this had better work
+ _lock = threading.RLock()
+ if _lock:
+ _lock.acquire()
+
+def _releaseLock():
+ """
+ Release the module-level lock acquired by calling _acquireLock().
+ """
+ if _lock:
+ _lock.release()
+
+#---------------------------------------------------------------------------
+# The logging record
+#---------------------------------------------------------------------------
+
+class LogRecord:
+ """
+ LogRecord instances are created every time something is logged. They
+ contain all the information pertinent to the event being logged. The
+ main information passed in is in msg and args, which are combined
+ using msg % args to create the message field of the record. The record
+ also includes information such as when the record was created, the
+ source line where the logging call was made, and any exception
+ information to be logged.
+ """
+ def __init__(self, name, lvl, pathname, lineno, msg, args, exc_info):
+ """
+ Initialize a logging record with interesting information.
+ """
+ ct = time.time()
+ self.name = name
+ self.msg = msg
+ self.args = args
+ self.levelname = getLevelName(lvl)
+ self.levelno = lvl
+ self.pathname = pathname
+ try:
+ self.filename = os.path.basename(pathname)
+ self.module = os.path.splitext(self.filename)[0]
+ except:
+ self.filename = pathname
+ self.module = "Unknown module"
+ self.exc_info = exc_info
+ self.lineno = lineno
+ self.created = ct
+ self.msecs = (ct - long(ct)) * 1000
+ self.relativeCreated = (self.created - _startTime) * 1000
+ if thread:
+ self.thread = thread.get_ident()
+ else:
+ self.thread = None
+
+ def __str__(self):
+ return '<LogRecord: %s, %s, %s, %s, "%s">'%(self.name, self.levelno,
+ self.pathname, self.lineno, self.msg)
+
+ def getMessage(self):
+ """
+ Return the message for this LogRecord, merging any user-supplied
+ arguments with the message.
+ """
+ msg = str(self.msg)
+ if self.args:
+ msg = msg % self.args
+ return msg
+
+#---------------------------------------------------------------------------
+# Formatter classes and functions
+#---------------------------------------------------------------------------
+
+class Formatter:
+ """
+ Formatters need to know how a LogRecord is constructed. They are
+ responsible for converting a LogRecord to (usually) a string which can
+ be interpreted by either a human or an external system. The base Formatter
+ allows a formatting string to be specified. If none is supplied, the
+ default value of "%s(message)\\n" is used.
+
+ The Formatter can be initialized with a format string which makes use of
+ knowledge of the LogRecord attributes - e.g. the default value mentioned
+ above makes use of the fact that the user's message and arguments are pre-
+ formatted into a LogRecord's message attribute. Currently, the useful
+ attributes in a LogRecord are described by:
+
+ %(name)s Name of the logger (logging channel)
+ %(levelno)s Numeric logging level for the message (DEBUG, INFO,
+ WARN, ERROR, CRITICAL)
+ %(levelname)s Text logging level for the message ("DEBUG", "INFO",
+ "WARN", "ERROR", "CRITICAL")
+ %(pathname)s Full pathname of the source file where the logging
+ call was issued (if available)
+ %(filename)s Filename portion of pathname
+ %(module)s Module (name portion of filename)
+ %(lineno)d Source line number where the logging call was issued
+ (if available)
+ %(created)f Time when the LogRecord was created (time.time()
+ return value)
+ %(asctime)s Textual time when the LogRecord was created
+ %(msecs)d Millisecond portion of the creation time
+ %(relativeCreated)d Time in milliseconds when the LogRecord was created,
+ relative to the time the logging module was loaded
+ (typically at application startup time)
+ %(thread)d Thread ID (if available)
+ %(message)s The result of msg % args, computed just as the
+ record is emitted
+ """
+ def __init__(self, fmt=None, datefmt=None):
+ """
+ Initialize the formatter either with the specified format string, or a
+ default as described above. Allow for specialized date formatting with
+ the optional datefmt argument (if omitted, you get the ISO8601 format).
+ """
+ if fmt:
+ self._fmt = fmt
+ else:
+ self._fmt = "%(message)s"
+ self.datefmt = datefmt
+
+ def formatTime(self, record, datefmt=None):
+ """
+ This method should be called from format() by a formatter which
+ wants to make use of a formatted time. This method can be overridden
+ in formatters to provide for any specific requirement, but the
+ basic behaviour is as follows: if datefmt (a string) is specified,
+ it is used with time.strftime() to format the creation time of the
+ record. Otherwise, the ISO8601 format is used. The resulting
+ string is returned.
+ """
+ ct = record.created
+ if datefmt:
+ s = time.strftime(datefmt, time.localtime(ct))
+ else:
+ t = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(ct))
+ s = "%s,%03d" % (t, record.msecs)
+ return s
+
+ def formatException(self, ei):
+ """
+ Format the specified exception information as a string. This
+ default implementation just uses traceback.print_exception()
+ """
+ import traceback
+ sio = cStringIO.StringIO()
+ traceback.print_exception(ei[0], ei[1], ei[2], None, sio)
+ s = sio.getvalue()
+ sio.close()
+ if s[-1] == "\n":
+ s = s[:-1]
+ return s
+
+ def format(self, record):
+ """
+ The record's attribute dictionary is used as the operand to a
+ string formatting operation which yields the returned string.
+ Before formatting the dictionary, a couple of preparatory steps
+ are carried out. The message attribute of the record is computed
+ using msg % args. If the formatting string contains "%(asctime)",
+ formatTime() is called to format the event time. If there is
+ exception information, it is formatted using formatException()
+ and appended to the message.
+ """
+ record.message = record.getMessage()
+ if string.find(self._fmt,"%(asctime)") >= 0:
+ record.asctime = self.formatTime(record, self.datefmt)
+ s = self._fmt % record.__dict__
+ if record.exc_info:
+ if s[-1] != "\n":
+ s = s + "\n"
+ s = s + self.formatException(record.exc_info)
+ return s
+
+#
+# The default formatter to use when no other is specified
+#
+_defaultFormatter = Formatter()
+
+class BufferingFormatter:
+ """
+ A formatter suitable for formatting a number of records.
+ """
+ def __init__(self, linefmt=None):
+ """
+ Optionally specify a formatter which will be used to format each
+ individual record.
+ """
+ if linefmt:
+ self.linefmt = linefmt
+ else:
+ self.linefmt = _defaultFormatter
+
+ def formatHeader(self, records):
+ """
+ Return the header string for the specified records.
+ """
+ return ""
+
+ def formatFooter(self, records):
+ """
+ Return the footer string for the specified records.
+ """
+ return ""
+
+ def format(self, records):
+ """
+ Format the specified records and return the result as a string.
+ """
+ rv = ""
+ if len(records) > 0:
+ rv = rv + self.formatHeader(records)
+ for record in records:
+ rv = rv + self.linefmt.format(record)
+ rv = rv + self.formatFooter(records)
+ return rv
+
+#---------------------------------------------------------------------------
+# Filter classes and functions
+#---------------------------------------------------------------------------
+
+class Filter:
+ """
+ The base filter class. Loggers and Handlers can optionally use Filter
+ instances to filter records as desired. The base filter class only allows
+ events which are below a certain point in the logger hierarchy. For
+ example, a filter initialized with "A.B" will allow events logged by
+ loggers "A.B", "A.B.C", "A.B.C.D", "A.B.D" etc. but not "A.BB", "B.A.B"
+ etc. If initialized with the empty string, all events are passed.
+ """
+ def __init__(self, name=''):
+ """
+ Initialize with the name of the logger which, together with its
+ children, will have its events allowed through the filter. If no
+ name is specified, allow every event.
+ """
+ self.name = name
+ self.nlen = len(name)
+
+ def filter(self, record):
+ """
+ Is the specified record to be logged? Returns 0 for no, nonzero for
+ yes. If deemed appropriate, the record may be modified in-place.
+ """
+ if self.nlen == 0:
+ return 1
+ elif self.name == record.name:
+ return 1
+ elif string.find(record.name, self.name, 0, self.nlen) != 0:
+ return 0
+ return (record.name[self.nlen] == ".")
+
+class Filterer:
+ """
+ A base class for loggers and handlers which allows them to share
+ common code.
+ """
+ def __init__(self):
+ """
+ Initialize the list of filters to be an empty list.
+ """
+ self.filters = []
+
+ def addFilter(self, filter):
+ """
+ Add the specified filter to this handler.
+ """
+ if not (filter in self.filters):
+ self.filters.append(filter)
+
+ def removeFilter(self, filter):
+ """
+ Remove the specified filter from this handler.
+ """
+ if filter in self.filters:
+ self.filters.remove(filter)
+
+ def filter(self, record):
+ """
+ Determine if a record is loggable by consulting all the filters. The
+ default is to allow the record to be logged; any filter can veto this
+ and the record is then dropped. Returns a boolean value.
+ """
+ rv = 1
+ for f in self.filters:
+ if not f.filter(record):
+ rv = 0
+ break
+ return rv
+
+#---------------------------------------------------------------------------
+# Handler classes and functions
+#---------------------------------------------------------------------------
+
+_handlers = {} #repository of handlers (for flushing when shutdown called)
+
+class Handler(Filterer):
+ """
+ The base handler class. Acts as a placeholder which defines the Handler
+ interface. Handlers can optionally use Formatter instances to format
+ records as desired. By default, no formatter is specified; in this case,
+ the 'raw' message as determined by record.message is logged.
+ """
+ def __init__(self, level=ALL):
+ """
+ Initializes the instance - basically setting the formatter to None
+ and the filter list to empty.
+ """
+ Filterer.__init__(self)
+ self.level = level
+ self.formatter = None
+ #get the module data lock, as we're updating a shared structure.
+ _acquireLock()
+ try: #unlikely to raise an exception, but you never know...
+ _handlers[self] = 1
+ finally:
+ _releaseLock()
+ self.createLock()
+
+ def createLock(self):
+ """
+ Acquire a thread lock for serializing access to the underlying I/O.
+ """
+ if thread:
+ self.lock = thread.allocate_lock()
+ else:
+ self.lock = None
+
+ def acquire(self):
+ """
+ Acquire the I/O thread lock.
+ """
+ if self.lock:
+ self.lock.acquire()
+
+ def release(self):
+ """
+ Release the I/O thread lock.
+ """
+ if self.lock:
+ self.lock.release()
+
+ def setLevel(self, lvl):
+ """
+ Set the logging level of this handler.
+ """
+ self.level = lvl
+
+ def format(self, record):
+ """
+ Do formatting for a record - if a formatter is set, use it.
+ Otherwise, use the default formatter for the module.
+ """
+ if self.formatter:
+ fmt = self.formatter
+ else:
+ fmt = _defaultFormatter
+ return fmt.format(record)
+
+ def emit(self, record):
+ """
+ Do whatever it takes to actually log the specified logging record.
+ This version is intended to be implemented by subclasses and so
+ raises a NotImplementedError.
+ """
+ raise NotImplementedError, 'emit must be implemented '\
+ 'by Handler subclasses'
+
+ def handle(self, record):
+ """
+ Conditionally emit the specified logging record, depending on
+ filters which may have been added to the handler. Wrap the actual
+ emission of the record with acquisition/release of the I/O thread
+ lock.
+ """
+ if self.filter(record):
+ self.acquire()
+ try:
+ self.emit(record)
+ finally:
+ self.release()
+
+ def setFormatter(self, fmt):
+ """
+ Set the formatter for this handler.
+ """
+ self.formatter = fmt
+
+ def flush(self):
+ """
+ Ensure all logging output has been flushed. This version does
+ nothing and is intended to be implemented by subclasses.
+ """
+ pass
+
+ def close(self):
+ """
+ Tidy up any resources used by the handler. This version does
+ nothing and is intended to be implemented by subclasses.
+ """
+ pass
+
+ def handleError(self):
+ """
+ This method should be called from handlers when an exception is
+ encountered during an emit() call. By default it does nothing,
+ which means that exceptions get silently ignored. This is what is
+ mostly wanted for a logging system - most users will not care
+ about errors in the logging system, they are more interested in
+ application errors. You could, however, replace this with a custom
+ handler if you wish.
+ """
+ #import traceback
+ #ei = sys.exc_info()
+ #traceback.print_exception(ei[0], ei[1], ei[2], None, sys.stderr)
+ #del ei
+ pass
+
+class StreamHandler(Handler):
+ """
+ A handler class which writes logging records, appropriately formatted,
+ to a stream. Note that this class does not close the stream, as
+ sys.stdout or sys.stderr may be used.
+ """
+ def __init__(self, strm=None):
+ """
+ If strm is not specified, sys.stderr is used.
+ """
+ Handler.__init__(self)
+ if not strm:
+ strm = sys.stderr
+ self.stream = strm
+ self.formatter = None
+
+ def flush(self):
+ """
+ Flushes the stream.
+ """
+ self.stream.flush()
+
+ def emit(self, record):
+ """
+ If a formatter is specified, it is used to format the record.
+ The record is then written to the stream with a trailing newline
+ [N.B. this may be removed depending on feedback]. If exception
+ information is present, it is formatted using
+ traceback.print_exception and appended to the stream.
+ """
+ try:
+ msg = self.format(record)
+ self.stream.write("%s\n" % msg)
+ self.flush()
+ except:
+ self.handleError()
+
+class FileHandler(StreamHandler):
+ """
+ A handler class which writes formatted logging records to disk files.
+ """
+ def __init__(self, filename, mode="a+"):
+ """
+ Open the specified file and use it as the stream for logging.
+ By default, the file grows indefinitely. You can call setRollover()
+ to allow the file to rollover at a predetermined size.
+ """
+ StreamHandler.__init__(self, open(filename, mode))
+ self.maxBytes = 0
+ self.backupCount = 0
+ self.baseFilename = filename
+ #self.backupIndex = 0
+ self.mode = mode
+
+ def setRollover(self, maxBytes, backupCount):
+ """
+ Set the rollover parameters so that rollover occurs whenever the
+ current log file is nearly maxBytes in length. If backupCount
+ is >= 1, the system will successively create new files with the
+ same pathname as the base file, but with extensions ".1", ".2"
+ etc. appended to it. For example, with a backupCount of 5 and a
+ base file name of "app.log", you would get "app.log", "app.log.1",
+ "app.log.2", ... through to "app.log.5". When the last file reaches
+ its size limit, the logging reverts to "app.log" which is truncated
+ to zero length. If maxBytes is zero, rollover never occurs.
+ """
+ self.maxBytes = maxBytes
+ self.backupCount = backupCount
+ if maxBytes > 0:
+ self.mode = "a+"
+
+ def doRollover(self):
+ """
+ Do a rollover, as described in setRollover().
+ """
+# Old algorithm
+# if self.backupIndex >= self.backupCount:
+# self.backupIndex = 0
+# fn = self.baseFilename
+# else:
+# self.backupIndex = self.backupIndex + 1
+# fn = "%s.%d" % (self.baseFilename, self.backupIndex)
+# self.stream.close()
+# self.stream = open(fn, "w+")
+ self.stream.close()
+ if self.backupCount > 0:
+ for i in range(self.backupCount - 1, 0, -1):
+ sfn = "%s.%d" % (self.baseFilename, i)
+ dfn = "%s.%d" % (self.baseFilename, i + 1)
+ if os.path.exists(sfn):
+ #print "%s -> %s" % (sfn, dfn)
+ if os.path.exists(dfn):
+ os.remove(dfn)
+ os.rename(sfn, dfn)
+ dfn = self.baseFilename + ".1"
+ if os.path.exists(dfn):
+ os.remove(dfn)
+ os.rename(self.baseFilename, dfn)
+ self.stream = open(self.baseFilename, "w+")
+
+ def emit(self, record):
+ """
+ Output the record to the file, catering for rollover as described
+ in setRollover().
+ """
+ if self.maxBytes > 0: # are we rolling over?
+ msg = "%s\n" % self.format(record)
+ if self.stream.tell() + len(msg) >= self.maxBytes:
+ self.doRollover()
+ StreamHandler.emit(self, record)
+
+ def close(self):
+ """
+ Closes the stream.
+ """
+ self.stream.close()
+
+class SocketHandler(Handler):
+ """
+ A handler class which writes logging records, in pickle format, to
+ a streaming socket. The socket is kept open across logging calls.
+ If the peer resets it, an attempt is made to reconnect on the next call.
+ Note that the very simple wire protocol used means that packet sizes
+ are expected to be encodable within 16 bits (i.e. < 32767 bytes).
+ """
+
+ def __init__(self, host, port):
+ """
+ Initializes the handler with a specific host address and port.
+ """
+ Handler.__init__(self)
+ self.host = host
+ self.port = port
+ self.sock = None
+ self.closeOnError = 1
+
+ def makeSocket(self):
+ """
+ A factory method which allows subclasses to define the precise
+ type of socket they want.
+ """
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.connect((self.host, self.port))
+ return s
+
+ def send(self, s):
+ """
+ Send a pickled string to the socket. This function allows for
+ partial sends which can happen when the network is busy.
+ """
+ sentsofar = 0
+ left = len(s)
+ while left > 0:
+ sent = self.sock.send(s[sentsofar:])
+ sentsofar = sentsofar + sent
+ left = left - sent
+
+ def makePickle(self, record):
+ """
+ Pickles the record in binary format with a length prefix, and
+ returns it ready for transmission across the socket.
+ """
+ s = cPickle.dumps(record.__dict__, 1)
+ n = len(s)
+ slen = "%c%c" % ((n >> 8) & 0xFF, n & 0xFF)
+ return slen + s
+
+ def handleError(self):
+ """
+ An error has occurred during logging. Most likely cause -
+ connection lost. Close the socket so that we can retry on the
+ next event.
+ """
+ if self.closeOnError and self.sock:
+ self.sock.close()
+ self.sock = None #try to reconnect next time
+
+ def emit(self, record):
+ """
+ Pickles the record and writes it to the socket in binary format.
+ If there is an error with the socket, silently drop the packet.
+ If there was a problem with the socket, re-establishes the
+ socket.
+ """
+ try:
+ s = self.makePickle(record)
+ if not self.sock:
+ self.sock = self.makeSocket()
+ self.send(s)
+ except:
+ self.handleError()
+
+ def close(self):
+ """
+ Closes the socket.
+ """
+ if self.sock:
+ self.sock.close()
+ self.sock = None
+
+class DatagramHandler(SocketHandler):
+ """
+ A handler class which writes logging records, in pickle format, to
+ a datagram socket. Note that the very simple wire protocol used means
+ that packet sizes are expected to be encodable within 16 bits
+ (i.e. < 32767 bytes).
+
+ """
+ def __init__(self, host, port):
+ """
+ Initializes the handler with a specific host address and port.
+ """
+ SocketHandler.__init__(self, host, port)
+ self.closeOnError = 0
+
+ def makeSocket(self):
+ """
+ The factory method of SocketHandler is here overridden to create
+ a UDP socket (SOCK_DGRAM).
+ """
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ return s
+
+ def send(self, s):
+ """
+ Send a pickled string to a socket. This function allows for
+ partial sends which can happen when the network is busy.
+ """
+ sentsofar = 0
+ left = len(s)
+ addr = (self.host, self.port)
+ while left > 0:
+ sent = self.sock.sendto(s[sentsofar:], addr)
+ sentsofar = sentsofar + sent
+ left = left - sent
+
+class SysLogHandler(Handler):
+ """
+ A handler class which sends formatted logging records to a syslog
+ server. Based on Sam Rushing's syslog module:
+ http://www.nightmare.com/squirl/python-ext/misc/syslog.py
+ Contributed by Nicolas Untz (after which minor refactoring changes
+ have been made).
+ """
+
+ # from <linux/sys/syslog.h>:
+ # ======================================================================
+ # priorities/facilities are encoded into a single 32-bit quantity, where
+ # the bottom 3 bits are the priority (0-7) and the top 28 bits are the
+ # facility (0-big number). Both the priorities and the facilities map
+ # roughly one-to-one to strings in the syslogd(8) source code. This
+ # mapping is included in this file.
+ #
+ # priorities (these are ordered)
+
+ LOG_EMERG = 0 # system is unusable
+ LOG_ALERT = 1 # action must be taken immediately
+ LOG_CRIT = 2 # critical conditions
+ LOG_ERR = 3 # error conditions
+ LOG_WARNING = 4 # warning conditions
+ LOG_NOTICE = 5 # normal but significant condition
+ LOG_INFO = 6 # informational
+ LOG_DEBUG = 7 # debug-level messages
+
+ # facility codes
+ LOG_KERN = 0 # kernel messages
+ LOG_USER = 1 # random user-level messages
+ LOG_MAIL = 2 # mail system
+ LOG_DAEMON = 3 # system daemons
+ LOG_AUTH = 4 # security/authorization messages
+ LOG_SYSLOG = 5 # messages generated internally by syslogd
+ LOG_LPR = 6 # line printer subsystem
+ LOG_NEWS = 7 # network news subsystem
+ LOG_UUCP = 8 # UUCP subsystem
+ LOG_CRON = 9 # clock daemon
+ LOG_AUTHPRIV = 10 # security/authorization messages (private)
+
+ # other codes through 15 reserved for system use
+ LOG_LOCAL0 = 16 # reserved for local use
+ LOG_LOCAL1 = 17 # reserved for local use
+ LOG_LOCAL2 = 18 # reserved for local use
+ LOG_LOCAL3 = 19 # reserved for local use
+ LOG_LOCAL4 = 20 # reserved for local use
+ LOG_LOCAL5 = 21 # reserved for local use
+ LOG_LOCAL6 = 22 # reserved for local use
+ LOG_LOCAL7 = 23 # reserved for local use
+
+ priority_names = {
+ "alert": LOG_ALERT,
+ "crit": LOG_CRIT,
+ "critical": LOG_CRIT,
+ "debug": LOG_DEBUG,
+ "emerg": LOG_EMERG,
+ "err": LOG_ERR,
+ "error": LOG_ERR, # DEPRECATED
+ "info": LOG_INFO,
+ "notice": LOG_NOTICE,
+ "panic": LOG_EMERG, # DEPRECATED
+ "warn": LOG_WARNING, # DEPRECATED
+ "warning": LOG_WARNING,
+ }
+
+ facility_names = {
+ "auth": LOG_AUTH,
+ "authpriv": LOG_AUTHPRIV,
+ "cron": LOG_CRON,
+ "daemon": LOG_DAEMON,
+ "kern": LOG_KERN,
+ "lpr": LOG_LPR,
+ "mail": LOG_MAIL,
+ "news": LOG_NEWS,
+ "security": LOG_AUTH, # DEPRECATED
+ "syslog": LOG_SYSLOG,
+ "user": LOG_USER,
+ "uucp": LOG_UUCP,
+ "local0": LOG_LOCAL0,
+ "local1": LOG_LOCAL1,
+ "local2": LOG_LOCAL2,
+ "local3": LOG_LOCAL3,
+ "local4": LOG_LOCAL4,
+ "local5": LOG_LOCAL5,
+ "local6": LOG_LOCAL6,
+ "local7": LOG_LOCAL7,
+ }
+
+ def __init__(self, address=('localhost', SYSLOG_UDP_PORT), facility=LOG_USER):
+ """
+ If address is specified as a string, UNIX socket is used.
+ If facility is not specified, LOG_USER is used.
+ """
+ Handler.__init__(self)
+
+ self.address = address
+ self.facility = facility
+ if type(address) == types.StringType:
+ self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ self.socket.connect(address)
+ self.unixsocket = 1
+ else:
+ self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ self.unixsocket = 0
+
+ self.formatter = None
+
+ # curious: when talking to the unix-domain '/dev/log' socket, a
+ # zero-terminator seems to be required. this string is placed
+ # into a class variable so that it can be overridden if
+ # necessary.
+ log_format_string = '<%d>%s\000'
+
+ def encodePriority (self, facility, priority):
+ """
+ Encode the facility and priority. You can pass in strings or
+ integers - if strings are passed, the facility_names and
+ priority_names mapping dictionaries are used to convert them to
+ integers.
+ """
+ if type(facility) == types.StringType:
+ facility = self.facility_names[facility]
+ if type(priority) == types.StringType:
+ priority = self.priority_names[priority]
+ return (facility << 3) | priority
+
+ def close (self):
+ """
+ Closes the socket.
+ """
+ if self.unixsocket:
+ self.socket.close()
+
+ def emit(self, record):
+ """
+ The record is formatted, and then sent to the syslog server. If
+ exception information is present, it is NOT sent to the server.
+ """
+ msg = self.format(record)
+ """
+ We need to convert record level to lowercase, maybe this will
+ change in the future.
+ """
+ msg = self.log_format_string % (
+ self.encodePriority(self.facility,
+ string.lower(record.levelname)),
+ msg)
+ try:
+ if self.unixsocket:
+ self.socket.send(msg)
+ else:
+ self.socket.sendto(msg, self.address)
+ except:
+ self.handleError()
+
+class SMTPHandler(Handler):
+ """
+ A handler class which sends an SMTP email for each logging event.
+ """
+ def __init__(self, mailhost, fromaddr, toaddrs, subject):
+ """
+ Initialize the instance with the from and to addresses and subject
+ line of the email. To specify a non-standard SMTP port, use the
+ (host, port) tuple format for the mailhost argument.
+ """
+ Handler.__init__(self)
+ if type(mailhost) == types.TupleType:
+ host, port = mailhost
+ self.mailhost = host
+ self.mailport = port
+ else:
+ self.mailhost = mailhost
+ self.mailport = None
+ self.fromaddr = fromaddr
+ self.toaddrs = toaddrs
+ self.subject = subject
+
+ def getSubject(self, record):
+ """
+ If you want to specify a subject line which is record-dependent,
+ override this method.
+ """
+ return self.subject
+
+ def emit(self, record):
+ """
+ Format the record and send it to the specified addressees.
+ """
+ try:
+ import smtplib
+ port = self.mailport
+ if not port:
+ port = smtplib.SMTP_PORT
+ smtp = smtplib.SMTP(self.mailhost, port)
+ msg = self.format(record)
+ msg = "From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n%s" % (
+ self.fromaddr,
+ string.join(self.toaddrs, ","),
+ self.getSubject(record), msg
+ )
+ smtp.sendmail(self.fromaddr, self.toaddrs, msg)
+ smtp.quit()
+ except:
+ self.handleError()
+
+class BufferingHandler(Handler):
+ """
+ A handler class which buffers logging records in memory. Whenever each
+ record is added to the buffer, a check is made to see if the buffer should
+ be flushed. If it should, then flush() is expected to do the needful.
+ """
+ def __init__(self, capacity):
+ """
+ Initialize the handler with the buffer size.
+ """
+ Handler.__init__(self)
+ self.capacity = capacity
+ self.buffer = []
+
+ def shouldFlush(self, record):
+ """
+ Returns true if the buffer is up to capacity. This method can be
+ overridden to implement custom flushing strategies.
+ """
+ return (len(self.buffer) >= self.capacity)
+
+ def emit(self, record):
+ """
+ Append the record. If shouldFlush() tells us to, call flush() to process
+ the buffer.
+ """
+ self.buffer.append(record)
+ if self.shouldFlush(record):
+ self.flush()
+
+ def flush(self):
+ """
+ Override to implement custom flushing behaviour. This version just zaps
+ the buffer to empty.
+ """
+ self.buffer = []
+
+class MemoryHandler(BufferingHandler):
+ """
+ A handler class which buffers logging records in memory, periodically
+ flushing them to a target handler. Flushing occurs whenever the buffer
+ is full, or when an event of a certain severity or greater is seen.
+ """
+ def __init__(self, capacity, flushLevel=ERROR, target=None):
+ """
+ Initialize the handler with the buffer size, the level at which
+ flushing should occur and an optional target. Note that without a
+ target being set either here or via setTarget(), a MemoryHandler
+ is no use to anyone!
+ """
+ BufferingHandler.__init__(self, capacity)
+ self.flushLevel = flushLevel
+ self.target = target
+
+ def shouldFlush(self, record):
+ """
+ Check for buffer full or a record at the flushLevel or higher.
+ """
+ return (len(self.buffer) >= self.capacity) or \
+ (record.levelno >= self.flushLevel)
+
+ def setTarget(self, target):
+ """
+ Set the target handler for this handler.
+ """
+ self.target = target
+
+ def flush(self):
+ """
+ For a MemoryHandler, flushing means just sending the buffered
+ records to the target, if there is one. Override if you want
+ different behaviour.
+ """
+ if self.target:
+ for record in self.buffer:
+ self.target.handle(record)
+ self.buffer = []
+
+ def close(self):
+ """
+ Flush, set the target to None and lose the buffer.
+ """
+ self.flush()
+ self.target = None
+ self.buffer = []
+
+class NTEventLogHandler(Handler):
+ """
+ A handler class which sends events to the NT Event Log. Adds a
+ registry entry for the specified application name. If no dllname is
+ provided, win32service.pyd (which contains some basic message
+ placeholders) is used. Note that use of these placeholders will make
+ your event logs big, as the entire message source is held in the log.
+ If you want slimmer logs, you have to pass in the name of your own DLL
+ which contains the message definitions you want to use in the event log.
+ """
+ def __init__(self, appname, dllname=None, logtype="Application"):
+ Handler.__init__(self)
+ try:
+ import win32evtlogutil, win32evtlog
+ self.appname = appname
+ self._welu = win32evtlogutil
+ if not dllname:
+ dllname = os.path.split(self._welu.__file__)
+ dllname = os.path.split(dllname[0])
+ dllname = os.path.join(dllname[0], r'win32service.pyd')
+ self.dllname = dllname
+ self.logtype = logtype
+ self._welu.AddSourceToRegistry(appname, dllname, logtype)
+ self.deftype = win32evtlog.EVENTLOG_ERROR_TYPE
+ self.typemap = {
+ DEBUG : win32evtlog.EVENTLOG_INFORMATION_TYPE,
+ INFO : win32evtlog.EVENTLOG_INFORMATION_TYPE,
+ WARN : win32evtlog.EVENTLOG_WARNING_TYPE,
+ ERROR : win32evtlog.EVENTLOG_ERROR_TYPE,
+ CRITICAL: win32evtlog.EVENTLOG_ERROR_TYPE,
+ }
+ except ImportError:
+ print "The Python Win32 extensions for NT (service, event "\
+ "logging) appear not to be available."
+ self._welu = None
+
+ def getMessageID(self, record):
+ """
+ Return the message ID for the event record. If you are using your
+ own messages, you could do this by having the msg passed to the
+ logger being an ID rather than a formatting string. Then, in here,
+ you could use a dictionary lookup to get the message ID. This
+ version returns 1, which is the base message ID in win32service.pyd.
+ """
+ return 1
+
+ def getEventCategory(self, record):
+ """
+ Return the event category for the record. Override this if you
+ want to specify your own categories. This version returns 0.
+ """
+ return 0
+
+ def getEventType(self, record):
+ """
+ Return the event type for the record. Override this if you want
+ to specify your own types. This version does a mapping using the
+ handler's typemap attribute, which is set up in __init__() to a
+ dictionary which contains mappings for DEBUG, INFO, WARN, ERROR
+ and CRITICAL. If you are using your own levels you will either need
+ to override this method or place a suitable dictionary in the
+ handler's typemap attribute.
+ """
+ return self.typemap.get(record.levelno, self.deftype)
+
+ def emit(self, record):
+ """
+ Determine the message ID, event category and event type. Then
+ log the message in the NT event log.
+ """
+ if self._welu:
+ try:
+ id = self.getMessageID(record)
+ cat = self.getEventCategory(record)
+ type = self.getEventType(record)
+ msg = self.format(record)
+ self._welu.ReportEvent(self.appname, id, cat, type, [msg])
+ except:
+ self.handleError()
+
+ def close(self):
+ """
+ You can remove the application name from the registry as a
+ source of event log entries. However, if you do this, you will
+ not be able to see the events as you intended in the Event Log
+ Viewer - it needs to be able to access the registry to get the
+ DLL name.
+ """
+ #self._welu.RemoveSourceFromRegistry(self.appname, self.logtype)
+ pass
+
+class HTTPHandler(Handler):
+ """
+ A class which sends records to a Web server, using either GET or
+ POST semantics.
+ """
+ def __init__(self, host, url, method="GET"):
+ """
+ Initialize the instance with the host, the request URL, and the method
+ ("GET" or "POST")
+ """
+ Handler.__init__(self)
+ method = string.upper(method)
+ if method not in ["GET", "POST"]:
+ raise ValueError, "method must be GET or POST"
+ self.host = host
+ self.url = url
+ self.method = method
+
+ def emit(self, record):
+ """
+ Send the record to the Web server as an URL-encoded dictionary
+ """
+ try:
+ import httplib, urllib
+ h = httplib.HTTP(self.host)
+ url = self.url
+ data = urllib.urlencode(record.__dict__)
+ if self.method == "GET":
+ if (string.find(url, '?') >= 0):
+ sep = '&'
+ else:
+ sep = '?'
+ url = url + "%c%s" % (sep, data)
+ h.putrequest(self.method, url)
+ if self.method == "POST":
+ h.putheader("Content-length", str(len(data)))
+ h.endheaders()
+ if self.method == "POST":
+ h.send(data)
+ h.getreply() #can't do anything with the result
+ except:
+ self.handleError()
+
+#---------------------------------------------------------------------------
+# Manager classes and functions
+#---------------------------------------------------------------------------
+
+class PlaceHolder:
+ """
+ PlaceHolder instances are used in the Manager logger hierarchy to take
+ the place of nodes for which no loggers have been defined [FIXME add
+ example].
+ """
+ def __init__(self, alogger):
+ """
+ Initialize with the specified logger being a child of this placeholder.
+ """
+ self.loggers = [alogger]
+
+ def append(self, alogger):
+ """
+ Add the specified logger as a child of this placeholder.
+ """
+ if alogger not in self.loggers:
+ self.loggers.append(alogger)
+
+#
+# Determine which class to use when instantiating loggers.
+#
+_loggerClass = None
+
+def setLoggerClass(klass):
+ """
+ Set the class to be used when instantiating a logger. The class should
+ define __init__() such that only a name argument is required, and the
+ __init__() should call Logger.__init__()
+ """
+ if klass != Logger:
+ if type(klass) != types.ClassType:
+ raise TypeError, "setLoggerClass is expecting a class"
+ if not issubclass(klass, Logger):
+ raise TypeError, "logger not derived from logging.Logger: " + \
+ klass.__name__
+ global _loggerClass
+ _loggerClass = klass
+
+class Manager:
+ """
+ There is [under normal circumstances] just one Manager instance, which
+ holds the hierarchy of loggers.
+ """
+ def __init__(self, root):
+ """
+ Initialize the manager with the root node of the logger hierarchy.
+ """
+ self.root = root
+ self.disable = 0
+ self.emittedNoHandlerWarning = 0
+ self.loggerDict = {}
+
+ def getLogger(self, name):
+ """
+ Get a logger with the specified name, creating it if it doesn't
+ yet exist. If a PlaceHolder existed for the specified name [i.e.
+ the logger didn't exist but a child of it did], replace it with
+ the created logger and fix up the parent/child references which
+ pointed to the placeholder to now point to the logger.
+ """
+ rv = None
+ _acquireLock()
+ try:
+ if self.loggerDict.has_key(name):
+ rv = self.loggerDict[name]
+ if isinstance(rv, PlaceHolder):
+ ph = rv
+ rv = _loggerClass(name)
+ rv.manager = self
+ self.loggerDict[name] = rv
+ self._fixupChildren(ph, rv)
+ self._fixupParents(rv)
+ else:
+ rv = _loggerClass(name)
+ rv.manager = self
+ self.loggerDict[name] = rv
+ self._fixupParents(rv)
+ finally:
+ _releaseLock()
+ return rv
+
+ def _fixupParents(self, alogger):
+ """
+ Ensure that there are either loggers or placeholders all the way
+ from the specified logger to the root of the logger hierarchy.
+ """
+ name = alogger.name
+ i = string.rfind(name, ".")
+ rv = None
+ while (i > 0) and not rv:
+ substr = name[:i]
+ if not self.loggerDict.has_key(substr):
+ self.loggerDict[substr] = PlaceHolder(alogger)
+ else:
+ obj = self.loggerDict[substr]
+ if isinstance(obj, Logger):
+ rv = obj
+ else:
+ assert isinstance(obj, PlaceHolder)
+ obj.append(alogger)
+ i = string.rfind(name, ".", 0, i - 1)
+ if not rv:
+ rv = self.root
+ alogger.parent = rv
+
+ def _fixupChildren(self, ph, alogger):
+ """
+ Ensure that children of the placeholder ph are connected to the
+ specified logger.
+ """
+ for c in ph.loggers:
+ if string.find(c.parent.name, alogger.name) <> 0:
+ alogger.parent = c.parent
+ c.parent = alogger
+
+#---------------------------------------------------------------------------
+# Logger classes and functions
+#---------------------------------------------------------------------------
+
+class Logger(Filterer):
+ """
+ Instances of the Logger class represent a single logging channel. A
+ "logging channel" indicates an area of an application. Exactly how an
+ "area" is defined is up to the application developer. Since an
+ application can have any number of areas, logging channels are identified
+ by a unique string. Application areas can be nested (e.g. an area
+ of "input processing" might include sub-areas "read CSV files", "read
+ XLS files" and "read Gnumeric files"). To cater for this natural nesting,
+ channel names are organized into a namespace hierarchy where levels are
+ separated by periods, much like the Java or Python package namespace. So
+ in the instance given above, channel names might be "input" for the upper
+ level, and "input.csv", "input.xls" and "input.gnu" for the sub-levels.
+ There is no arbitrary limit to the depth of nesting.
+ """
+ def __init__(self, name, level=ALL):
+ """
+ Initialize the logger with a name and an optional level.
+ """
+ Filterer.__init__(self)
+ self.name = name
+ self.level = level
+ self.parent = None
+ self.propagate = 1
+ self.handlers = []
+ self.disabled = 0
+
+ def setLevel(self, lvl):
+ """
+ Set the logging level of this logger.
+ """
+ self.level = lvl
+
+# def getRoot(self):
+# """
+# Get the root of the logger hierarchy.
+# """
+# return Logger.root
+
+ def debug(self, msg, *args, **kwargs):
+ """
+ Log 'msg % args' with severity 'DEBUG'. To pass exception information,
+ use the keyword argument exc_info with a true value, e.g.
+
+ logger.debug("Houston, we have a %s", "thorny problem", exc_info=1)
+ """
+ if self.manager.disable >= DEBUG:
+ return
+ if DEBUG >= self.getEffectiveLevel():
+ apply(self._log, (DEBUG, msg, args), kwargs)
+
+ def info(self, msg, *args, **kwargs):
+ """
+ Log 'msg % args' with severity 'INFO'. To pass exception information,
+ use the keyword argument exc_info with a true value, e.g.
+
+ logger.info("Houston, we have a %s", "interesting problem", exc_info=1)
+ """
+ if self.manager.disable >= INFO:
+ return
+ if INFO >= self.getEffectiveLevel():
+ apply(self._log, (INFO, msg, args), kwargs)
+
+ def warn(self, msg, *args, **kwargs):
+ """
+ Log 'msg % args' with severity 'WARN'. To pass exception information,
+ use the keyword argument exc_info with a true value, e.g.
+
+ logger.warn("Houston, we have a %s", "bit of a problem", exc_info=1)
+ """
+ if self.manager.disable >= WARN:
+ return
+ if self.isEnabledFor(WARN):
+ apply(self._log, (WARN, msg, args), kwargs)
+
+ def error(self, msg, *args, **kwargs):
+ """
+ Log 'msg % args' with severity 'ERROR'. To pass exception information,
+ use the keyword argument exc_info with a true value, e.g.
+
+ logger.error("Houston, we have a %s", "major problem", exc_info=1)
+ """
+ if self.manager.disable >= ERROR:
+ return
+ if self.isEnabledFor(ERROR):
+ apply(self._log, (ERROR, msg, args), kwargs)
+
+ def exception(self, msg, *args):
+ """
+ Convenience method for logging an ERROR with exception information
+ """
+ apply(self.error, (msg,) + args, {'exc_info': 1})
+
+ def critical(self, msg, *args, **kwargs):
+ """
+ Log 'msg % args' with severity 'CRITICAL'. To pass exception
+ information, use the keyword argument exc_info with a true value, e.g.
+
+ logger.critical("Houston, we have a %s", "major disaster", exc_info=1)
+ """
+ if self.manager.disable >= CRITICAL:
+ return
+ if CRITICAL >= self.getEffectiveLevel():
+ apply(self._log, (CRITICAL, msg, args), kwargs)
+
+ fatal = critical
+
+ def log(self, lvl, msg, *args, **kwargs):
+ """
+ Log 'msg % args' with the severity 'lvl'. To pass exception
+ information, use the keyword argument exc_info with a true value, e.g.
+ logger.log(lvl, "We have a %s", "mysterious problem", exc_info=1)
+ """
+ if self.manager.disable >= lvl:
+ return
+ if self.isEnabledFor(lvl):
+ apply(self._log, (lvl, msg, args), kwargs)
+
+ def findCaller(self):
+ """
+ Find the stack frame of the caller so that we can note the source
+ file name and line number.
+ """
+ rv = (None, None)
+ frame = inspect.currentframe().f_back
+ while frame:
+ sfn = inspect.getsourcefile(frame)
+ if sfn:
+ sfn = os.path.normcase(sfn)
+ if sfn != _srcfile:
+ #print frame.f_code.co_code
+ lineno = inspect.getlineno(frame)
+ rv = (sfn, lineno)
+ break
+ frame = frame.f_back
+ return rv
+
+ def makeRecord(self, name, lvl, fn, lno, msg, args, exc_info):
+ """
+ A factory method which can be overridden in subclasses to create
+ specialized LogRecords.
+ """
+ return LogRecord(name, lvl, fn, lno, msg, args, exc_info)
+
+ def _log(self, lvl, msg, args, exc_info=None):
+ """
+ Low-level logging routine which creates a LogRecord and then calls
+ all the handlers of this logger to handle the record.
+ """
+ if inspect and _srcfile:
+ _acquireLock()
+ try:
+ fn, lno = self.findCaller()
+ finally:
+ _releaseLock()
+ else:
+ fn, lno = "<unknown file>", 0
+ if exc_info:
+ exc_info = sys.exc_info()
+ record = self.makeRecord(self.name, lvl, fn, lno, msg, args, exc_info)
+ self.handle(record)
+
+ def handle(self, record):
+ """
+ Call the handlers for the specified record. This method is used for
+ unpickled records received from a socket, as well as those created
+ locally. Logger-level filtering is applied.
+ """
+ if (not self.disabled) and self.filter(record):
+ self.callHandlers(record)
+
+ def addHandler(self, hdlr):
+ """
+ Add the specified handler to this logger.
+ """
+ if not (hdlr in self.handlers):
+ self.handlers.append(hdlr)
+
+ def removeHandler(self, hdlr):
+ """
+ Remove the specified handler from this logger.
+ """
+ if hdlr in self.handlers:
+ hdlr.close()
+ self.handlers.remove(hdlr)
+
+ def callHandlers(self, record):
+ """
+ Loop through all handlers for this logger and its parents in the
+ logger hierarchy. If no handler was found, output a one-off error
+ message to sys.stderr. Stop searching up the hierarchy whenever a
+ logger with the "propagate" attribute set to zero is found - that
+ will be the last logger whose handlers are called.
+ """
+ c = self
+ found = 0
+ while c:
+ for hdlr in c.handlers:
+ found = found + 1
+ if record.levelno >= hdlr.level:
+ hdlr.handle(record)
+ if not c.propagate:
+ c = None #break out
+ else:
+ c = c.parent
+ if (found == 0) and not self.manager.emittedNoHandlerWarning:
+ sys.stderr.write("No handlers could be found for logger"
+ " \"%s\"\n" % self.name)
+ self.manager.emittedNoHandlerWarning = 1
+
+ def getEffectiveLevel(self):
+ """
+ Loop through this logger and its parents in the logger hierarchy,
+ looking for a non-zero logging level. Return the first one found.
+ """
+ logger = self
+ while logger:
+ if logger.level:
+ return logger.level
+ logger = logger.parent
+ return ALL
+
+ def isEnabledFor(self, lvl):
+ """
+ Is this logger enabled for level lvl?
+ """
+ if self.manager.disable >= lvl:
+ return 0
+ return lvl >= self.getEffectiveLevel()
+
+class RootLogger(Logger):
+ """
+ A root logger is not that different to any other logger, except that
+ it must have a logging level and there is only one instance of it in
+ the hierarchy.
+ """
+ def __init__(self, lvl):
+ """
+ Initialize the logger with the name "root".
+ """
+ Logger.__init__(self, "root", lvl)
+
+_loggerClass = Logger
+
+root = RootLogger(DEBUG)
+Logger.root = root
+Logger.manager = Manager(Logger.root)
+
+#---------------------------------------------------------------------------
+# Configuration classes and functions
+#---------------------------------------------------------------------------
+
+BASIC_FORMAT = "%(levelname)s:%(name)s:%(message)s"
+
+def basicConfig():
+ """
+ Do basic configuration for the logging system by creating a
+ StreamHandler with a default Formatter and adding it to the
+ root logger.
+ """
+ if len(root.handlers) == 0:
+ hdlr = StreamHandler()
+ fmt = Formatter(BASIC_FORMAT)
+ hdlr.setFormatter(fmt)
+ root.addHandler(hdlr)
+
+def fileConfig(fname):
+ """
+ Read the logging configuration from a ConfigParser-format file. This can
+ be called several times from an application, allowing an end user the
+ ability to select from various pre-canned configurations (if the
+ developer provides a mechanism to present the choices and load the chosen
+ configuration).
+ """
+ import ConfigParser
+
+ cp = ConfigParser.ConfigParser()
+ cp.read(fname)
+ #first, do the formatters...
+ flist = cp.get("formatters", "keys")
+ if len(flist):
+ flist = string.split(flist, ",")
+ formatters = {}
+ for form in flist:
+ sectname = "formatter_%s" % form
+ opts = cp.options(sectname)
+ if "format" in opts:
+ fs = cp.get(sectname, "format", 1)
+ else:
+ fs = None
+ if "datefmt" in opts:
+ dfs = cp.get(sectname, "datefmt", 1)
+ else:
+ dfs = None
+ f = Formatter(fs, dfs)
+ formatters[form] = f
+ #next, do the handlers...
+ #critical section...
+ _acquireLock()
+ try:
+ try:
+ #first, lose the existing handlers...
+ _handlers.clear()
+ #now set up the new ones...
+ hlist = cp.get("handlers", "keys")
+ if len(hlist):
+ hlist = string.split(hlist, ",")
+ handlers = {}
+ fixups = [] #for inter-handler references
+ for hand in hlist:
+ sectname = "handler_%s" % hand
+ klass = cp.get(sectname, "class")
+ opts = cp.options(sectname)
+ if "formatter" in opts:
+ fmt = cp.get(sectname, "formatter")
+ else:
+ fmt = ""
+ klass = eval(klass)
+ args = cp.get(sectname, "args")
+ args = eval(args)
+ h = apply(klass, args)
+ if "level" in opts:
+ lvl = cp.get(sectname, "level")
+ h.setLevel(_levelNames[lvl])
+ if len(fmt):
+ h.setFormatter(formatters[fmt])
+ #temporary hack for FileHandler and MemoryHandler.
+ if klass == FileHandler:
+ maxsize = 0
+ if "maxsize" in opts:
+ ms = cp.getint(sectname, "maxsize")
+ if ms > 0:
+ maxsize = ms
+ if maxsize:
+ backcount = 0
+ if "backcount" in opts:
+ bc = cp.getint(sectname, "backcount")
+ if bc > 0:
+ backcount = bc
+ h.setRollover(maxsize, backcount)
+ elif klass == MemoryHandler:
+ if "target" in opts:
+ target = cp.get(sectname,"target")
+ else:
+ target = ""
+ if len(target): #the target handler may not be loaded yet, so keep for later...
+ fixups.append((h, target))
+ handlers[hand] = h
+ #now all handlers are loaded, fixup inter-handler references...
+ for fixup in fixups:
+ h = fixup[0]
+ t = fixup[1]
+ h.setTarget(handlers[t])
+ #at last, the loggers...first the root...
+ llist = cp.get("loggers", "keys")
+ llist = string.split(llist, ",")
+ llist.remove("root")
+ sectname = "logger_root"
+ log = root
+ opts = cp.options(sectname)
+ if "level" in opts:
+ lvl = cp.get(sectname, "level")
+ log.setLevel(_levelNames[lvl])
+ for h in root.handlers:
+ root.removeHandler(h)
+ hlist = cp.get(sectname, "handlers")
+ if len(hlist):
+ hlist = string.split(hlist, ",")
+ for hand in hlist:
+ log.addHandler(handlers[hand])
+ #and now the others...
+ #we don't want to lose the existing loggers,
+ #since other threads may have pointers to them.
+ #existing is set to contain all existing loggers,
+ #and as we go through the new configuration we
+ #remove any which are configured. At the end,
+ #what's left in existing is the set of loggers
+ #which were in the previous configuration but
+ #which are not in the new configuration.
+ existing = root.manager.loggerDict.keys()
+ #now set up the new ones...
+ for log in llist:
+ sectname = "logger_%s" % log
+ qn = cp.get(sectname, "qualname")
+ opts = cp.options(sectname)
+ if "propagate" in opts:
+ propagate = cp.getint(sectname, "propagate")
+ else:
+ propagate = 1
+ logger = getLogger(qn)
+ if qn in existing:
+ existing.remove(qn)
+ if "level" in opts:
+ lvl = cp.get(sectname, "level")
+ logger.setLevel(_levelNames[lvl])
+ for h in logger.handlers:
+ logger.removeHandler(h)
+ logger.propagate = propagate
+ logger.disabled = 0
+ hlist = cp.get(sectname, "handlers")
+ if len(hlist):
+ hlist = string.split(hlist, ",")
+ for hand in hlist:
+ logger.addHandler(handlers[hand])
+ #Disable any old loggers. There's no point deleting
+ #them as other threads may continue to hold references
+ #and by disabling them, you stop them doing any logging.
+ for log in existing:
+ root.manager.loggerDict[log].disabled = 1
+ except:
+ import traceback
+ ei = sys.exc_info()
+ traceback.print_exception(ei[0], ei[1], ei[2], None, sys.stderr)
+ del ei
+ finally:
+ _releaseLock()
+
+#---------------------------------------------------------------------------
+# Utility functions at module level.
+# Basically delegate everything to the root logger.
+#---------------------------------------------------------------------------
+
+def getLogger(name=None):
+ """
+ Return a logger with the specified name, creating it if necessary.
+ If no name is specified, return the root logger.
+ """
+ if name:
+ return Logger.manager.getLogger(name)
+ else:
+ return root
+
+def getRootLogger():
+ """
+ Return the root logger. Note that getLogger('') now does the same thing,
+ so this function is deprecated and may disappear in the future.
+ """
+ return root
+
+def critical(msg, *args, **kwargs):
+ """
+ Log a message with severity 'CRITICAL' on the root logger.
+ """
+ if len(root.handlers) == 0:
+ basicConfig()
+ apply(root.critical, (msg,)+args, kwargs)
+
+fatal = critical
+
+def error(msg, *args, **kwargs):
+ """
+ Log a message with severity 'ERROR' on the root logger.
+ """
+ if len(root.handlers) == 0:
+ basicConfig()
+ apply(root.error, (msg,)+args, kwargs)
+
+def exception(msg, *args):
+ """
+ Log a message with severity 'ERROR' on the root logger,
+ with exception information.
+ """
+ apply(error, (msg,)+args, {'exc_info': 1})
+
+def warn(msg, *args, **kwargs):
+ """
+ Log a message with severity 'WARN' on the root logger.
+ """
+ if len(root.handlers) == 0:
+ basicConfig()
+ apply(root.warn, (msg,)+args, kwargs)
+
+def info(msg, *args, **kwargs):
+ """
+ Log a message with severity 'INFO' on the root logger.
+ """
+ if len(root.handlers) == 0:
+ basicConfig()
+ apply(root.info, (msg,)+args, kwargs)
+
+def debug(msg, *args, **kwargs):
+ """
+ Log a message with severity 'DEBUG' on the root logger.
+ """
+ if len(root.handlers) == 0:
+ basicConfig()
+ apply(root.debug, (msg,)+args, kwargs)
+
+def disable(level):
+ """
+ Disable all logging calls less severe than 'level'.
+ """
+ root.manager.disable = level
+
+def shutdown():
+ """
+ Perform any cleanup actions in the logging system (e.g. flushing
+ buffers). Should be called at application exit.
+ """
+ for h in _handlers.keys():
+ h.flush()
+ h.close()
+
+#
+# The following code implements a socket listener for on-the-fly
+# reconfiguration of logging.
+#
+# _listener holds the server object doing the listening
+_listener = None
+
+def listen(port=DEFAULT_LOGGING_CONFIG_PORT):
+ """
+ Start up a socket server on the specified port, and listen for new
+ configurations. These will be sent as a file suitable for processing
+ by fileConfig(). Returns a Thread object on which you can call start()
+ to start the server, and which you can join() when appropriate.
+ To stop the server, call stopListening().
+ """
+ if not thread:
+ raise NotImplementedError, "listen() needs threading to work"
+
+ import threading
+
+ class ConfigStreamHandler(StreamRequestHandler):
+ """
+ Handler for a logging configuration request. It expects a
+ completely new logging configuration and uses fileConfig to
+ install it.
+ """
+ def handle(self):
+ """
+ Each request is expected to be a 2-byte length,
+ followed by the config file. Uses fileConfig() to do the
+ needful.
+ """
+ import tempfile
+ try:
+ conn = self.connection
+ chunk = conn.recv(2)
+ if len(chunk) == 2:
+ slen = (ord(chunk[0]) << 8) | ord(chunk[1])
+ chunk = self.connection.recv(slen)
+ while len(chunk) < slen:
+ chunk = chunk + conn.recv(slen - len(chunk))
+ #Apply new configuration. We'd like to be able to
+ #create a StringIO and pass that in, but unfortunately
+ #1.5.2 ConfigParser does not support reading file
+ #objects, only actual files. So we create a temporary
+ #file and remove it later.
+ file = tempfile.mktemp(".ini")
+ f = open(file, "w")
+ f.write(chunk)
+ f.close()
+ fileConfig(file)
+ os.remove(file)
+ except socket.error, e:
+ if type(e.args) != types.TupleType:
+ raise
+ else:
+ errcode = e.args[0]
+ if errcode != RESET_ERROR:
+ raise
+
+ class ConfigSocketReceiver(ThreadingTCPServer):
+ """
+ A simple TCP socket-based logging config receiver.
+ """
+
+ allow_reuse_address = 1
+
+ def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT,
+ handler=None):
+ ThreadingTCPServer.__init__(self, (host, port), handler)
+ _acquireLock()
+ self.abort = 0
+ _releaseLock()
+ self.timeout = 1
+
+ def serve_until_stopped(self):
+ import select
+ abort = 0
+ while not abort:
+ rd, wr, ex = select.select([self.socket.fileno()],
+ [], [],
+ self.timeout)
+ if rd:
+ self.handle_request()
+ _acquireLock()
+ abort = self.abort
+ _releaseLock()
+
+ def serve(rcvr, hdlr):
+ server = rcvr(handler=hdlr)
+ global _listener
+ _acquireLock()
+ _listener = server
+ _releaseLock()
+ server.serve_until_stopped()
+
+ return threading.Thread(target=serve, args=(ConfigSocketReceiver, ConfigStreamHandler))
+
+def stopListening():
+ """
+ Stop the listening server which was created with a call to listen().
+ """
+ if _listener:
+ _acquireLock()
+ _listener.abort = 1
+ _listener = None
+ _releaseLock()
+
+
+# bicycle repair man stuff
+
+bike_logger_initialised = 0
+
+def init():
+ global bike_logger_initialised
+ if not bike_logger_initialised:
+ log = getLogger("bike")
+ h = StreamHandler()
+ h.setFormatter(Formatter(
+ fmt="%(pathname)s:%(lineno)s:%(levelname)s %(message)s"))
+ log.addHandler(h)
+ bike_logger_initialised = 1
+
+if __name__ == "__main__":
+ print __doc__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/mock.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,143 @@
+#
+# (c) Dave Kirby 2001
+# dkirby@bigfoot.com
+#
+# Call interceptor code by Phil Dawes (pdawes@users.sourceforge.net)
+
+'''
+The Mock class emulates any other class for testing purposes.
+All method calls are stored for later examination.
+The class constructor takes a dictionary of method names and the values
+they return. Methods that are not in the dictionary will return None.
+'''
+import inspect
+
+
+class Mock:
+ def __init__(self, returnValues=None ):
+ self.mockCalledMethods = {}
+ self.mockAllCalledMethods = []
+ self.mockReturnValues = returnValues or {}
+ self.setupMethodInterceptors()
+
+ def setupMethodInterceptors(self):
+ if self.__class__ != Mock: # check we've been subclassed
+ methods = inspect.getmembers(self.__class__,inspect.ismethod)
+ for m in methods:
+ name = m[0]
+ self.__dict__[name] = MethodCallInterceptor(name,self)
+
+ def __getattr__( self, name ):
+ return MockCaller( name, self )
+
+ def getAllCalls(self):
+ '''return a list of MockCall objects,
+ representing all the methods in the order they were called'''
+ return self.mockAllCalledMethods
+
+ def getNamedCalls(self, methodName ):
+ '''return a list of MockCall objects,
+ representing all the calls to the named method in the order they were called'''
+ return self.mockCalledMethods.get(methodName, [] )
+
+ def assertNamedCall(self,methodName,*args):
+ # assert call was made once
+ assert(len(self.getNamedCalls(methodName)) == 1)
+ # assert args are correct
+ argsdict = inspect.getargvalues(inspect.currentframe())[3]
+ i=0
+ for arg in argsdict['args']:
+ assert(self.getNamedCalls(methodName)[0].getParam(i) == arg)
+ i += 1
+
+ def assertCallInOrder(self,methodName,*args):
+ ''' Convenience method to allow client to check that calls were
+ made in the right order. Call once for each methodcall'''
+
+ # initialise callIndex. (n.b. __getattr__ method complicates
+ # this since attrs are MockCaller instances by default)
+ if type(self.callIndex) != type(1):
+ self.callIndex=0
+
+ call = self.getAllCalls()[self.callIndex]
+ # assert method name is correct
+ assert(call.getName() == methodName)
+ # assert args are correct
+ argsdict = inspect.getargvalues(inspect.currentframe())[3]
+ i=0
+ for arg in argsdict['args']:
+ assert(call.getParam(i) == arg)
+ i += 1
+ # assert num args are correct
+ assert(call.getNumParams() == i)
+ self.callIndex += 1
+
+ def assertNoMoreCalls(self):
+ assert(len(self.getAllCalls()) == self.callIndex)
+
+class MockCall:
+ def __init__(self, name, params, kwparams ):
+ self.name = name
+ self.params = params
+ self.kwparams = kwparams
+ def getParam( self, n ):
+ if type(n) == type(1):
+ return self.params[n]
+ elif type(n) == type(''):
+ return self.kwparams[n]
+ else:
+ raise IndexError, 'illegal index type for getParam'
+
+ def getNumParams(self):
+ return len(self.params)
+
+
+ def getName(self):
+ return self.name
+
+ #pretty-print the method call
+ def __str__(self):
+ s = self.name + "("
+ sep = ''
+ for p in self.params:
+ s = s + sep + repr(p)
+ sep = ', '
+ for k,v in self.kwparams.items():
+ s = s + sep + k+ '='+repr(v)
+ sep = ', '
+ s = s + ')'
+ return s
+ def __repr__(self):
+ return self.__str__()
+
+class MockCaller:
+ def __init__( self, name, mock):
+ self.name = name
+ self.mock = mock
+ def __call__(self, *params, **kwparams ):
+ self.recordCall(params,kwparams)
+ return self.mock.mockReturnValues.get(self.name)
+
+ def recordCall(self,params,kwparams):
+ thisCall = MockCall( self.name, params, kwparams )
+ calls = self.mock.mockCalledMethods.get(self.name, [] )
+ if calls == []:
+ self.mock.mockCalledMethods[self.name] = calls
+ calls.append(thisCall)
+ self.mock.mockAllCalledMethods.append(thisCall)
+
+
+# intercepts the call and records it, then delegates to the real call
+class MethodCallInterceptor(MockCaller):
+
+ def __call__(self, *params, **kwparams ):
+ self.recordCall(params,kwparams)
+ return self.makeCall(params)
+
+ def makeCall(self,params):
+ argsstr="(self.mock"
+ for i in range(len(params)):
+ argsstr += ",params["+`i`+"]"
+ argsstr+=")"
+ return eval("self.mock.__class__."+self.name+argsstr)
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/__init__.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,3 @@
+#from addtypeinfo import addtypeinfo
+#from load import load
+#from brmtransformer import parse
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/constants.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,3 @@
+messages={"PARSING":"Parsing","ADDTYPEINFO":"Deducing Type Information"}
+
+MAXCALLDEPTH = 10
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/fastparser.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,70 @@
+#!/usr/bin/env python
+from bike.parsing.fastparserast import *
+from bike.parsing.parserutils import *
+from parser import ParserError
+#import exceptions
+
+indentRE = re.compile("^\s*(\w+)")
+
+# returns a tree of objects representing nested classes and functions
+# in the source
+def fastparser(src,modulename="",filename=""):
+ try:
+ return fastparser_impl(src,modulename,filename)
+ except RuntimeError, ex: # if recursive call exceeds maximum depth
+ if str(ex) == "maximum recursion limit exceeded":
+ raise ParserError,"maximum recursion depth exceeded when fast-parsing src "+filename
+ else:
+ raise
+
+def fastparser_impl(src,modulename,filename):
+ lines = src.splitlines(1)
+ maskedSrc = maskPythonKeywordsInStringsAndComments(src)
+ maskedLines = maskedSrc.splitlines(1)
+ root = Module(filename,modulename,lines,maskedSrc)
+ parentnode = root
+ lineno = 0
+ for line in maskedLines:
+ lineno+=1
+ #print "line",lineno,":",line
+ m = indentRE.match(line)
+ if m:
+ indent = m.start(1)
+ tokenstr = m.group(1)
+ if tokenstr == "import" or tokenstr == "from":
+ while indent <= parentnode.indent: # root indent is -TABWIDTH
+ parentnode = parentnode.getParent()
+ try:
+ parentnode.importlines.append(lineno)
+ except AttributeError:
+ parentnode.importlines = [lineno]
+ elif tokenstr == "class":
+ m2 = classNameRE.match(line)
+ if m2:
+ n = Class(m2.group(1), filename, root, lineno, indent, lines, maskedSrc)
+ root.flattenedNodes.append(n)
+
+ while indent <= parentnode.indent:
+ parentnode = parentnode.getParent()
+ parentnode.addChild(n)
+ parentnode = n
+
+ elif tokenstr == "def":
+ m2 = fnNameRE.match(line)
+ if m2:
+ n = Function(m2.group(1), filename, root, lineno, indent, lines, maskedSrc)
+ root.flattenedNodes.append(n)
+
+ while indent <= parentnode.indent:
+ parentnode = parentnode.getParent()
+ parentnode.addChild(n)
+ parentnode = n
+
+ elif indent <= parentnode.indent and \
+ tokenstr in ['if','for','while','try']:
+ parentnode = parentnode.getParent()
+ while indent <= parentnode.indent:
+ parentnode = parentnode.getParent()
+
+ return root
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/fastparserast.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,327 @@
+from __future__ import generators
+from parserutils import generateLogicalLines, maskStringsAndComments, maskStringsAndRemoveComments
+import re
+import os
+import compiler
+from bike.transformer.save import resetOutputQueue
+
+TABWIDTH = 4
+
+classNameRE = re.compile("^\s*class\s+(\w+)")
+fnNameRE = re.compile("^\s*def\s+(\w+)")
+
+_root = None
+
+def getRoot():
+ global _root
+ if _root is None:
+ resetRoot()
+ return _root
+
+def resetRoot(root = None):
+ global _root
+ _root = root or Root()
+ _root.unittestmode = False
+ resetOutputQueue()
+
+
+def getModule(filename_path):
+ from bike.parsing.load import CantLocateSourceNodeException, getSourceNode
+ try:
+ sourcenode = getSourceNode(filename_path)
+ return sourcenode.fastparseroot
+ except CantLocateSourceNodeException:
+ return None
+
+def getPackage(directory_path):
+ from bike.parsing.pathutils import getRootDirectory
+ rootdir = getRootDirectory(directory_path)
+ if rootdir == directory_path:
+ return getRoot()
+ else:
+ return Package(directory_path,
+ os.path.basename(directory_path))
+
+
+
+
+
+class Root:
+ def __init__(self, pythonpath = None):
+ # singleton hack to allow functions in query package to appear
+ # 'stateless'
+ resetRoot(self)
+
+ # this is to get round a python optimisation which reuses an
+ # empty list as a default arg. unfortunately the client of
+ # this method may fill that list, so it's not empty
+ if not pythonpath:
+ pythonpath = []
+ self.pythonpath = pythonpath
+
+ def __repr__(self):
+ return "Root()"
+ #return "Root(%s)"%(self.getChildNodes())
+
+
+ # dummy method
+ def getChild(self,name):
+ return None
+
+class Package:
+ def __init__(self, path, name):
+ self.path = path
+ self.name = name
+
+ def getChild(self,name):
+ from bike.parsing.newstuff import getModule
+ return getModule(os.path.join(self.path,name+".py"))
+
+ def __repr__(self):
+ return "Package(%s,%s)"%(self.path, self.name)
+
+# used so that linenum can be an attribute
+class Line(str):
+ pass
+
+class StructuralNode:
+ def __init__(self, filename, srclines, modulesrc):
+ self.childNodes = []
+ self.filename = filename
+ self._parent = None
+ self._modulesrc = modulesrc
+ self._srclines = srclines
+ self._maskedLines = None
+
+ def addChild(self, node):
+ self.childNodes.append(node)
+ node.setParent(self)
+
+ def setParent(self, parent):
+ self._parent = parent
+
+ def getParent(self):
+ return self._parent
+
+ def getChildNodes(self):
+ return self.childNodes
+
+ def getChild(self,name):
+ matches = [c for c in self.getChildNodes() if c.name == name]
+ if matches != []:
+ return matches[0]
+
+ def getLogicalLine(self,physicalLineno):
+ return generateLogicalLines(self._srclines[physicalLineno-1:]).next()
+
+ # badly named: actually returns line numbers of import statements
+ def getImportLineNumbers(self):
+ try:
+ return self.importlines
+ except AttributeError:
+ return[]
+
+ def getLinesNotIncludingThoseBelongingToChildScopes(self):
+ srclines = self.getMaskedModuleLines()
+ lines = []
+ lineno = self.getStartLine()
+ for child in self.getChildNodes():
+ lines+=srclines[lineno-1: child.getStartLine()-1]
+ lineno = child.getEndLine()
+ lines+=srclines[lineno-1: self.getEndLine()-1]
+ return lines
+
+
+ def generateLinesNotIncludingThoseBelongingToChildScopes(self):
+ srclines = self.getMaskedModuleLines()
+ lines = []
+ lineno = self.getStartLine()
+ for child in self.getChildNodes():
+ for line in srclines[lineno-1: child.getStartLine()-1]:
+ yield self.attachLinenum(line,lineno)
+ lineno +=1
+ lineno = child.getEndLine()
+ for line in srclines[lineno-1: self.getEndLine()-1]:
+ yield self.attachLinenum(line,lineno)
+ lineno +=1
+
+ def generateLinesWithLineNumbers(self,startline=1):
+ srclines = self.getMaskedModuleLines()
+ for lineno in range(startline,len(srclines)+1):
+ yield self.attachLinenum(srclines[lineno-1],lineno)
+
+ def attachLinenum(self,line,lineno):
+ line = Line(line)
+ line.linenum = lineno
+ return line
+
+ def getMaskedModuleLines(self):
+ from bike.parsing.load import Cache
+ try:
+ maskedlines = Cache.instance.maskedlinescache[self.filename]
+ except:
+ # make sure src is actually masked
+ # (could just have keywords masked)
+ maskedsrc = maskStringsAndComments(self._modulesrc)
+ maskedlines = maskedsrc.splitlines(1)
+ Cache.instance.maskedlinescache[self.filename] = maskedlines
+ return maskedlines
+
+
+class Module(StructuralNode):
+ def __init__(self, filename, name, srclines, maskedsrc):
+ StructuralNode.__init__(self, filename, srclines, maskedsrc)
+ self.name = name
+ self.indent = -TABWIDTH
+ self.flattenedNodes = []
+ self.module = self
+
+ def getMaskedLines(self):
+ return self.getMaskedModuleLines()
+
+ def getFlattenedListOfChildNodes(self):
+ return self.flattenedNodes
+
+ def getStartLine(self):
+ return 1
+
+ def getEndLine(self):
+ return len(self.getMaskedModuleLines())+1
+
+ def getSourceNode(self):
+ return self.sourcenode
+
+ def setSourceNode(self, sourcenode):
+ self.sourcenode = sourcenode
+
+ def matchesCompilerNode(self,node):
+ return isinstance(node,compiler.ast.Module) and \
+ node.name == self.name
+
+ def getParent(self):
+ if self._parent is not None:
+ return self._parent
+ else:
+ from newstuff import getPackage
+ return getPackage(os.path.dirname(self.filename))
+
+
+ def __str__(self):
+ return "bike:Module:"+self.filename
+
+indentRE = re.compile("^(\s*)\S")
+class Node:
+ # module = the module node
+ # linenum = starting line number
+ def __init__(self, name, module, linenum, indent):
+ self.name = name
+ self.module = module
+ self.linenum = linenum
+ self.endline = None
+ self.indent = indent
+
+ def getMaskedLines(self):
+ return self.getMaskedModuleLines()[self.getStartLine()-1:self.getEndLine()-1]
+
+ def getStartLine(self):
+ return self.linenum
+
+ def getEndLine(self):
+ if self.endline is None:
+ physicallines = self.getMaskedModuleLines()
+ lineno = self.linenum
+ logicallines = generateLogicalLines(physicallines[lineno-1:])
+
+ # skip the first line, because it's the declaration
+ line = logicallines.next()
+ lineno+=line.count("\n")
+
+ # scan to the end of the fn
+ for line in logicallines:
+ #print lineno,":",line,
+ match = indentRE.match(line)
+ if match and match.end()-1 <= self.indent:
+ break
+ lineno+=line.count("\n")
+ self.endline = lineno
+ return self.endline
+
+ # linenum starts at 0
+ def getLine(self, linenum):
+ return self._srclines[(self.getStartLine()-1) + linenum]
+
+
+baseClassesRE = re.compile("class\s+[^(]+\(([^)]+)\):")
+
+class Class(StructuralNode, Node):
+ def __init__(self, name, filename, module, linenum, indent, srclines, maskedmodulesrc):
+ StructuralNode.__init__(self, filename, srclines, maskedmodulesrc)
+ Node.__init__(self, name, module, linenum, indent)
+ self.type = "Class"
+
+
+ def getBaseClassNames(self):
+ #line = self.getLine(0)
+ line = self.getLogicalLine(self.getStartLine())
+ match = baseClassesRE.search(line)
+ if match:
+ return [s.strip()for s in match.group(1).split(",")]
+ else:
+ return []
+
+ def getColumnOfName(self):
+ match = classNameRE.match(self.getLine(0))
+ return match.start(1)
+
+ def __repr__(self):
+ return "<bike:Class:%s>" % self.name
+
+ def __str__(self):
+ return "bike:Class:"+self.filename+":"+\
+ str(self.getStartLine())+":"+self.name
+
+ def matchesCompilerNode(self,node):
+ return isinstance(node,compiler.ast.Class) and \
+ node.name == self.name
+
+ def __eq__(self,other):
+ return isinstance(other,Class) and \
+ self.filename == other.filename and \
+ self.getStartLine() == other.getStartLine()
+
+# describes an instance of a class
+class Instance:
+ def __init__(self, type):
+ assert type is not None
+ self._type = type
+
+ def getType(self):
+ return self._type
+
+ def __str__(self):
+ return "Instance(%s)"%(self.getType())
+
+
+class Function(StructuralNode, Node):
+ def __init__(self, name, filename, module, linenum, indent,
+ srclines, maskedsrc):
+ StructuralNode.__init__(self, filename, srclines, maskedsrc)
+ Node.__init__(self, name, module, linenum, indent)
+ self.type = "Function"
+
+ def getColumnOfName(self):
+ match = fnNameRE.match(self.getLine(0))
+ return match.start(1)
+
+ def __repr__(self):
+ return "<bike:Function:%s>" % self.name
+
+ def __str__(self):
+ return "bike:Function:"+self.filename+":"+\
+ str(self.getStartLine())+":"+self.name
+
+ def matchesCompilerNode(self,node):
+ return isinstance(node,compiler.ast.Function) and \
+ node.name == self.name
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/load.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,94 @@
+from bike.globals import *
+import os
+from bike.parsing.fastparser import fastparser
+
+class Cache:
+ def __init__(self):
+ self.reset()
+ def reset(self):
+ self.srcnodecache = {}
+ self.typecache = {}
+ self.maskedlinescache = {}
+
+ instance = None
+
+Cache.instance = Cache()
+
+class CantLocateSourceNodeException(Exception): pass
+
+def getSourceNode(filename_path):
+ #print "getSourceNode:",filename_path
+ sourcenode = None
+
+ try:
+ sourcenode = Cache.instance.srcnodecache[filename_path]
+ except KeyError:
+ pass
+
+ if sourcenode is None:
+ from bike.parsing.newstuff import translateFnameToModuleName
+ sourcenode = SourceFile.createFromFile(filename_path,
+ translateFnameToModuleName(filename_path))
+ if sourcenode is None:
+ raise CantLocateSourceNodeException(filename_path)
+
+ Cache.instance.srcnodecache[filename_path]=sourcenode
+ return sourcenode
+
+class SourceFile:
+
+ def createFromString(filename, modulename, src):
+ return SourceFile(filename,modulename,src)
+ createFromString = staticmethod(createFromString)
+
+ def createFromFile(filename,modulename):
+ try:
+ f = file(filename)
+ src = f.read()
+ f.close()
+ except IOError:
+ return None
+ else:
+ return SourceFile(filename,modulename,src)
+
+ createFromFile = staticmethod(createFromFile)
+
+ def __init__(self, filename, modulename, src):
+
+ if os.path.isabs(filename):
+ self.filename = filename
+ else:
+ self.filename = os.path.abspath(filename)
+ self.modulename = modulename
+
+ self.resetWithSource(src)
+
+ def resetWithSource(self, source):
+ # fastparser ast
+ self.fastparseroot = fastparser(source,self.modulename,self.filename)
+ self.fastparseroot.setSourceNode(self)
+ self._lines = source.splitlines(1)
+ self.sourcenode = self
+
+ def __repr__(self):
+ return "Source(%s,%s)"%('source', self.filename)
+
+ def getChildNodes(self):
+ return self.fastparseroot.getChildNodes()
+
+ def getSource(self):
+ return "".join(self.getLines())
+
+ def getLine(self,linenum):
+ return self.getLines()[linenum-1]
+
+ # TODO: rename me!
+ def getFlattenedListOfFastParserASTNodes(self):
+ return self.fastparseroot.getFlattenedListOfChildNodes()
+
+ def getLines(self):
+ return self._lines
+
+
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/newstuff.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,114 @@
+from __future__ import generators
+# Holding module for scaffolding needed to transition parsing package
+# into stateless design
+import os
+import re
+from bike.parsing.pathutils import getRootDirectory, getPackageBaseDirectory, \
+ filenameToModulePath, getPathOfModuleOrPackage, getFilesForName
+from bike.parsing.fastparserast import Module, Package, getRoot, getPackage, getModule
+import sys
+from bike.parsing.load import getSourceNode, CantLocateSourceNodeException
+
+
+def translateFnameToModuleName(filename_path):
+ return filenameToModulePath(filename_path)
+
+
+
+# scope is the scope to search from
+def getModuleOrPackageUsingFQN(fqn, dirpath=None):
+ pythonpath = getPythonPath()
+ #print "getModuleOrPackageUsingFQN",pythonpath,fqn
+ if dirpath is not None:
+ assert os.path.isdir(dirpath)
+ pythonpath = [dirpath] + pythonpath
+ filename = getPathOfModuleOrPackage(fqn,pythonpath)
+ #print "getModuleOrPackageUsingFQN - filename",filename
+ if filename is not None:
+ if os.path.isdir(filename):
+ return getPackage(filename)
+ else:
+ return getModule(filename)
+ else:
+ return None
+
+def getPythonPath():
+ return getRoot().pythonpath
+
+
+def generateModuleFilenamesInPythonPath(contextFilename):
+ files = []
+ rootdir = getRootDirectory(contextFilename)
+ if rootdir in getPythonPath():
+ # just search the pythonpath
+ for path in getPythonPath():
+ for file in getFilesForName(path):
+ if file not in files: # check for duplicates
+ files.append(file)
+ yield file
+ else:
+ # search the package hierarchy containing contextFilename
+ # in addition to pythonpath
+ basedir = getPackageBaseDirectory(contextFilename)
+ for path in [basedir] + getPythonPath():
+ for file in getFilesForName(path):
+ if file not in files: # check for duplicates
+ files.append(file)
+ yield file
+
+ # and search the files immediately above the package hierarchy
+ for file in getFilesForName(os.path.join(rootdir,"*.py")):
+ if file not in files: # check for duplicates
+ files.append(file)
+ yield file
+
+def generateModuleFilenamesInPackage(filenameInPackage):
+ basedir = getPackageBaseDirectory(filenameInPackage)
+ for file in getFilesForName(basedir):
+ yield file
+
+
+
+# search all sourcenodes globally from the perspective of file 'contextFilename'
+def getSourceNodesContainingRegex(regexstr,contextFilename):
+ regex = re.compile(regexstr)
+ for fname in generateModuleFilenamesInPythonPath(contextFilename):
+ try:
+ f = file(fname)
+ src = f.read()
+ finally:
+ f.close()
+ if regex.search(src) is not None:
+ yield getSourceNode(fname)
+
+
+
+
+fromRegex = re.compile("^\s*from\s+(\w+)\s+import")
+importregex = re.compile("^\s*import\s+(\w+)")
+
+# fileInPackage is the filename of a file in the package hierarchy
+# generates file and directory paths
+def generatePackageDependencies(fileInPackage):
+ rejectPackagePaths = [getPackageBaseDirectory(fileInPackage)]
+ for fname in generateModuleFilenamesInPackage(fileInPackage):
+
+ try:
+ f = file(fname)
+ src = f.read()
+ finally:
+ f.close()
+
+ packagepath = None
+
+ for line in src.splitlines():
+ match = fromRegex.search(line) or importregex.search(line)
+ if match is not None:
+ modulepath = match.group(1)
+ packagename = modulepath.split('.')[0]
+ packagepath = getPathOfModuleOrPackage(packagename,
+ getPythonPath())
+ if packagepath is not None and \
+ packagepath not in rejectPackagePaths:
+ rejectPackagePaths.append(packagepath) # avoid duplicates
+ yield packagepath
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/parserutils.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,194 @@
+from __future__ import generators
+import re
+
+escapedQuotesRE = re.compile(r"(\\\\|\\\"|\\\')")
+
+# changess \" \' and \\ into ** so that text searches
+# for " and ' won't hit escaped ones
+def maskEscapedQuotes(src):
+ return escapedQuotesRE.sub("**", src)
+
+stringsAndCommentsRE = \
+ re.compile("(\"\"\".*?\"\"\"|'''.*?'''|\"[^\"]*\"|\'[^\']*\'|#.*?\n)", re.DOTALL)
+
+import string
+#transtable = string.maketrans('classdefifforwhiletry', "*********************")
+
+# performs a transformation on all of the comments and strings so that
+# text searches for python keywords won't accidently find a keyword in
+# a string or comment
+def maskPythonKeywordsInStringsAndComments(src):
+ src = escapedQuotesRE.sub("**", src)
+ allstrings = stringsAndCommentsRE.split(src)
+ # every odd element is a string or comment
+ for i in xrange(1, len(allstrings), 2):
+ allstrings[i] = allstrings[i].upper()
+ #allstrings[i] = allstrings[i].translate(transtable)
+ return "".join(allstrings)
+
+
+allchars = string.maketrans("", "")
+allcharsExceptNewline = allchars[: allchars.index('\n')]+allchars[allchars.index('\n')+1:]
+allcharsExceptNewlineTranstable = string.maketrans(allcharsExceptNewline, '*'*len(allcharsExceptNewline))
+
+
+# replaces all chars in a string or a comment with * (except newlines).
+# this ensures that text searches don't mistake comments for keywords, and that all
+# matches are in the same line/comment as the original
+def maskStringsAndComments(src):
+ src = escapedQuotesRE.sub("**", src)
+ allstrings = stringsAndCommentsRE.split(src)
+ # every odd element is a string or comment
+ for i in xrange(1, len(allstrings), 2):
+ if allstrings[i].startswith("'''")or allstrings[i].startswith('"""'):
+ allstrings[i] = allstrings[i][:3]+ \
+ allstrings[i][3:-3].translate(allcharsExceptNewlineTranstable)+ \
+ allstrings[i][-3:]
+ else:
+ allstrings[i] = allstrings[i][0]+ \
+ allstrings[i][1:-1].translate(allcharsExceptNewlineTranstable)+ \
+ allstrings[i][-1]
+
+ return "".join(allstrings)
+
+
+# replaces all chars in a string or a comment with * (except newlines).
+# this ensures that text searches don't mistake comments for keywords, and that all
+# matches are in the same line/comment as the original
+def maskStringsAndRemoveComments(src):
+ src = escapedQuotesRE.sub("**", src)
+ allstrings = stringsAndCommentsRE.split(src)
+ # every odd element is a string or comment
+ for i in xrange(1, len(allstrings), 2):
+ if allstrings[i].startswith("'''")or allstrings[i].startswith('"""'):
+ allstrings[i] = allstrings[i][:3]+ \
+ allstrings[i][3:-3].translate(allcharsExceptNewlineTranstable)+ \
+ allstrings[i][-3:]
+ elif allstrings[i].startswith("#"):
+ allstrings[i] = '\n'
+ else:
+ allstrings[i] = allstrings[i][0]+ \
+ allstrings[i][1:-1].translate(allcharsExceptNewlineTranstable)+ \
+ allstrings[i][-1]
+ return "".join(allstrings)
+
+
+implicitContinuationChars = (('(', ')'), ('[', ']'), ('{', '}'))
+emptyHangingBraces = [0,0,0,0,0]
+linecontinueRE = re.compile(r"\\\s*(#.*)?$")
+multiLineStringsRE = \
+ re.compile("(^.*?\"\"\".*?\"\"\".*?$|^.*?'''.*?'''.*?$)", re.DOTALL)
+
+#def splitLogicalLines(src):
+# src = multiLineStringsRE.split(src)
+
+# splits the string into logical lines. This requires the comments to
+# be removed, and strings masked (see other fns in this module)
+def splitLogicalLines(src):
+ physicallines = src.splitlines(1)
+ return [x for x in generateLogicalLines(physicallines)]
+
+
+class UnbalancedBracesException: pass
+
+# splits the string into logical lines. This requires the strings
+# masked (see other fns in this module)
+# Physical Lines *Must* start on a non-continued non-in-a-comment line
+# (although detects unbalanced braces)
+def generateLogicalLines(physicallines):
+ tmp = []
+ hangingBraces = list(emptyHangingBraces)
+ hangingComments = 0
+ for line in physicallines:
+ # update hanging braces
+ for i in range(len(implicitContinuationChars)):
+ contchar = implicitContinuationChars[i]
+ numHanging = hangingBraces[i]
+ hangingBraces[i] = numHanging+line.count(contchar[0]) - \
+ line.count(contchar[1])
+
+ hangingComments ^= line.count('"""') % 2
+ hangingComments ^= line.count("'''") % 2
+
+ if hangingBraces[0] < 0 or \
+ hangingBraces[1] < 0 or \
+ hangingBraces[2] < 0:
+ raise UnbalancedBracesException()
+
+ if linecontinueRE.search(line):
+ tmp.append(line)
+ elif hangingBraces != emptyHangingBraces:
+ tmp.append(line)
+ elif hangingComments:
+ tmp.append(line)
+ else:
+ tmp.append(line)
+ yield "".join(tmp)
+ tmp = []
+
+
+# see above but yields (line,linenum)
+# needs physicallines to have linenum attribute
+# TODO: refactor with previous function
+def generateLogicalLinesAndLineNumbers(physicallines):
+ tmp = []
+ hangingBraces = list(emptyHangingBraces)
+ hangingComments = 0
+ linenum = None
+ for line in physicallines:
+ if tmp == []:
+ linenum = line.linenum
+
+ # update hanging braces
+ for i in range(len(implicitContinuationChars)):
+ contchar = implicitContinuationChars[i]
+ numHanging = hangingBraces[i]
+ hangingBraces[i] = numHanging+line.count(contchar[0]) - \
+ line.count(contchar[1])
+
+ hangingComments ^= line.count('"""') % 2
+ hangingComments ^= line.count("'''") % 2
+
+ if linecontinueRE.search(line):
+ tmp.append(line)
+ elif hangingBraces != emptyHangingBraces:
+ tmp.append(line)
+ elif hangingComments:
+ tmp.append(line)
+ else:
+ tmp.append(line)
+ yield "".join(tmp),linenum
+ tmp = []
+
+
+
+
+# takes a line of code, and decorates it with noops so that it can be
+# parsed by the python compiler.
+# e.g. "if foo:" -> "if foo: pass"
+# returns the line, and the adjustment made to the column pos of the first char
+# line must have strings and comments masked
+#
+# N.B. it only inserts keywords whitespace and 0's
+notSpaceRE = re.compile("\s*(\S)")
+commentRE = re.compile("#.*$")
+
+def makeLineParseable(line):
+ return makeLineParseableWhenCommentsRemoved(commentRE.sub("",line))
+
+def makeLineParseableWhenCommentsRemoved(line):
+ line = line.strip()
+ if ":" in line:
+ if line.endswith(":"):
+ line += " pass"
+ if line.startswith("try"):
+ line += "\nexcept: pass"
+ elif line.startswith("except") or line.startswith("finally"):
+ line = "try: pass\n" + line
+ return line
+ elif line.startswith("else") or line.startswith("elif"):
+ line = "if 0: pass\n" + line
+ return line
+ elif line.startswith("yield"):
+ return ("return"+line[5:])
+ return line
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/pathutils.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,165 @@
+
+# A some of this code is take from Pythius -
+# Copyright (GPL) 2001 Jurgen Hermann <jh@web.de>
+
+from bike.globals import *
+import os
+
+def containsAny(str, set):
+ """ Check whether 'str' contains ANY of the chars in 'set'
+ """
+ return 1 in [c in str for c in set]
+
+
+
+def getPathOfModuleOrPackage(dotted_name, pathlist = None):
+ """ Get the filesystem path for a module or a package.
+
+ Return the file system path to a file for a module,
+ and to a directory for a package. Return None if
+ the name is not found, or is a builtin or extension module.
+ """
+ from bike.parsing.newstuff import getPythonPath
+ if pathlist is None:
+ pathlist = getPythonPath()
+
+ import imp
+
+ # split off top-most name
+ parts = dotted_name.split('.', 1)
+
+ if len(parts) > 1:
+ # we have a dotted path, import top-level package
+ try:
+ file, pathname, description = imp.find_module(parts[0], pathlist)
+ if file: file.close()
+ except ImportError:
+ return None
+
+ # check if it's indeed a package
+ if description[2] == imp.PKG_DIRECTORY:
+ # recursively handle the remaining name parts
+ pathname = getPathOfModuleOrPackage(parts[1], [pathname])
+ else:
+ pathname = None
+ else:
+ # plain name
+ try:
+ file, pathname, description = imp.find_module(dotted_name, pathlist)
+ if file: file.close()
+ if description[2]not in[imp.PY_SOURCE, imp.PKG_DIRECTORY]:
+ pathname = None
+ except ImportError:
+ pathname = None
+
+ return pathname
+
+
+def getFilesForName(name):
+ """ Get a list of module files for a filename, a module or package name,
+ or a directory.
+ """
+ import imp
+
+ if not os.path.exists(name):
+ # check for glob chars
+ if containsAny(name, "*?[]"):
+ import glob
+ files = glob.glob(name)
+ list = []
+ for file in files:
+ list.extend(getFilesForName(file))
+ return list
+
+ # try to find module or package
+ name = getPathOfModuleOrPackage(name)
+ if not name:
+ return[]
+
+ if os.path.isdir(name):
+ # find all python files in directory
+ list = []
+ os.path.walk(name, _visit_pyfiles, list)
+ return list
+ elif os.path.exists(name) and not name.startswith("."):
+ # a single file
+ return [name]
+
+ return []
+
+def _visit_pyfiles(list, dirname, names):
+ """ Helper for getFilesForName().
+ """
+ # get extension for python source files
+ if not globals().has_key('_py_ext'):
+ import imp
+ global _py_ext
+ _py_ext = [triple[0]for triple in imp.get_suffixes()if triple[2] == imp.PY_SOURCE][0]
+
+ # don't recurse into CVS or Subversion directories
+ if 'CVS'in names:
+ names.remove('CVS')
+ if '.svn'in names:
+ names.remove('.svn')
+
+ names_copy = [] + names
+ for n in names_copy:
+ if os.path.isdir(os.path.join(dirname, n))and \
+ not os.path.exists(os.path.join(dirname, n, "__init__.py")):
+ names.remove(n)
+
+ # add all *.py files to list
+ list.extend(
+ [os.path.join(dirname, file)
+ for file in names
+ if os.path.splitext(file)[1] == _py_ext and not file.startswith(".")])
+
+
+# returns the directory which holds the first package of the package
+# hierarchy under which 'filename' belongs
+def getRootDirectory(filename):
+ if os.path.isdir(filename):
+ dir = filename
+ else:
+ dir = os.path.dirname(filename)
+ while dir != "" and \
+ os.path.exists(os.path.join(dir, "__init__.py")):
+ dir = os.path.dirname(dir)
+ return dir
+
+
+
+# Returns the root package directoryname of the package hierarchy
+# under which 'filename' belongs
+def getPackageBaseDirectory(filename):
+ if os.path.isdir(filename):
+ dir = filename
+ else:
+ dir = os.path.dirname(filename)
+
+ if not os.path.exists(os.path.join(dir, "__init__.py")):
+ # parent dir is not a package
+ return dir
+
+ while dir != "" and \
+ os.path.exists(os.path.join(os.path.dirname(dir), "__init__.py")):
+ dir = os.path.dirname(dir)
+ return dir
+
+
+
+def filenameToModulePath(fname):
+ directoriesPreceedingRoot = getRootDirectory(fname)
+ import os
+ # strip off directories preceeding root package directory
+ if directoriesPreceedingRoot != "":
+ mpath = fname.replace(directoriesPreceedingRoot, "")
+ else:
+ mpath = fname
+
+ if(mpath[0] == os.path.normpath("/")):
+ mpath = mpath[1:]
+ mpath, ext = os.path.splitext(mpath)
+ mpath = mpath.replace(os.path.normpath("/"), ".")
+ return mpath
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/setpath.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,5 @@
+import sys,os
+if not os.path.abspath("../..") in sys.path:
+ from bike import log
+ print >> log.warning, "Appending to the system path. This should only happen in unit tests"
+ sys.path.append(os.path.abspath("../.."))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/test_fastparser.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,160 @@
+#!/usr/bin/env python
+import unittest
+from fastparser import*
+from bike.parsing.load import*
+from bike.parsing.fastparserast import*
+from bike.testutils import *
+
+class TestFastParser(BRMTestCase):
+ def test_doesntGetClassDeclsInMLStrings(self):
+ src = trimLines('''
+ """
+ class foo bah
+ """
+ ''')
+ root = fastparser(src)
+ assert root.getChildNodes() == []
+
+ def test_evaluatesMLStringWithQuoteInIt(self):
+ src = trimLines('''
+ """some ml comment inclosing a " """
+ def foo:
+ pass
+ " hello "
+ ''')
+ root = fastparser(src)
+ assert root.getChildNodes() != []
+
+ def test_handlesClassDefsWithTwoSpacesInDecl(self):
+ src = trimLines('''
+ class foo: pass
+ ''')
+ root = fastparser(src)
+ assert root.getChildNodes() != []
+
+ def test_handlesFnDefsWithTwoSpacesInDecl(self):
+ src = trimLines('''
+ def foo: pass
+ ''')
+ root = fastparser(src)
+ assert root.getChildNodes() != []
+
+
+MLStringWithQuoteInIt = """
+\"\"\"some ml comment inclosing a \" \"\"\"
+def foo:
+ pass
+\" hello \"
+"""
+
+def load(path):
+ files = getFilesForName(path)
+ for fname in files:
+ src = file(fname).read()
+ fastparser(src)
+ #print fname
+ #myroot = parseFile(fname)
+
+
+
+def fastparsetreeToString(root):
+ class stringholder: pass
+ s = stringholder()
+ s.mystr = ""
+ s.tabstr = ""
+ def t2s(node):
+ if isinstance(node, Class):
+ s.mystr+=s.tabstr+"class "+node.name+"\n"
+ s.tabstr+="\t"
+ for n in node.getChildNodes():
+ t2s(n)
+ s.tabstr = s.tabstr[:-1]
+
+ elif isinstance(node, Function):
+ s.mystr+=s.tabstr+"function "+node.name+"\n"
+ s.tabstr+="\t"
+ for n in node.getChildNodes():
+ t2s(n)
+ s.tabstr = s.tabstr[:-1]
+
+
+ for n in root.getChildNodes():
+ t2s(n)
+ return s.mystr
+
+
+def compilerParseTreeToString(root):
+ try:
+ class TreeVisitor:
+ def __init__(self):
+ self.mystr = ""
+ self.tabstr = ""
+
+ def visitClass(self, node):
+ self.mystr+=self.tabstr+"class "+node.name+"\n"
+ self.tabstr+="\t"
+ for child in node.getChildNodes():
+ self.visit(child)
+ self.tabstr = self.tabstr[:-1]
+
+ def visitFunction(self, node):
+ self.mystr+=self.tabstr+"function "+node.name+"\n"
+ self.tabstr+="\t"
+ for child in node.getChildNodes():
+ self.visit(child)
+ self.tabstr = self.tabstr[:-1]
+
+ return compiler.walk(root, TreeVisitor()).mystr
+
+ except:
+ log.exception("ex")
+ import sys
+ sys.exit(0)
+
+
+def compareCompilerWithFastparserOverPath(path):
+ from bike.parsing.load import getFilesForName
+ files = getFilesForName(path)
+ for fname in files:
+ if fname.endswith("bdist_wininst.py"): continue
+ log.info(fname)
+ src = file(fname).read()
+ try:
+ compiler_root = compiler.parse(src)
+ except SyntaxError:
+ continue
+ fastparse_root = fastparser(src)
+ str1 = fastparsetreeToString(fastparse_root)
+ str2 = compilerParseTreeToString(compiler_root)
+ assert str1 == str2, "\n"+"-"*70+"\n"+str1+"-"*70+"\n"+str2
+
+
+def timeParseOfPythonLibrary(path):
+ import time
+ t1 = time.time()
+ files = getFilesForName(path)
+ import sys
+ for fname in files:
+ if fname.endswith("bdist_wininst.py"): continue
+ src = file(fname).read()
+ fastparser(src)
+ print "\n", time.time()-t1
+
+
+if __name__ == "__main__":
+ from bike import logging
+ logging.init()
+ log = logging.getLogger("bike")
+ log.setLevel(logging.INFO)
+ # add soak tests to end of test
+ class Z_SoakTestFastparser(BRMTestCase):
+
+ def test_A_timeParseOfPythonLibrary(self):
+ timeParseOfPythonLibrary("/usr/local/lib/python2.2")
+
+ def test_parsesPythonLibraryCorrectly(self):
+ print ""
+ compareCompilerWithFastparserOverPath("/usr/local/lib/python2.2")
+
+ unittest.main()
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/test_fastparserast.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,151 @@
+#!/usr/bin/env python
+import unittest
+from fastparserast import *
+from fastparser import fastparser
+from bike.query.getTypeOf import getTypeOf
+from bike.testutils import *
+
+class TestGetModule(BRMTestCase):
+
+ def test_getRootWorksAfterDefinedByCreateSourceNodeAt(self):
+ src=trimLines("""
+ class TheClass:
+ pass
+ a = TheClass()
+ """)
+ root = createSourceNodeAt(src,"mymodule")
+ assert root == getRoot()
+
+ def test_returnsNoneIfModuleDoesntExist(self):
+ assert getModule(tmpfile) == None
+
+
+class TestGetEndLine(BRMTestCase):
+ def test_returnsEndLineWithSimpleFunction(self):
+ src = trimLines("""
+ class TheClass:
+ def theMethod():
+ pass
+ def foo():
+ b = TheClass()
+ return b
+ a = foo()
+ a.theMethod()
+ """)
+ root = fastparser(src)
+ fn = getTypeOf(root,"foo")
+ self.assertEqual(fn.getEndLine(),7)
+
+ def test_worksWithFunctionsThatHaveEmptyLinesInThem(self):
+ src = fnWithEmptyLineInIt
+ root = fastparser(src)
+ fn = getTypeOf(root,"TheClass.theFunction")
+ self.assertEqual(fn.getEndLine(),8)
+
+class TestGetBaseClassNames(BRMTestCase):
+ def test_worksForClassHierarchy(self):
+ src = trimLines("""
+ class root:
+ def theMethod():
+ pass
+
+ class a(root):
+ def theMethod():
+ pass
+
+ class b(root):
+ pass
+
+ class TheClass(a,b):
+ def theMethod():
+ pass
+
+ rootinstance = root()
+ rootinstance.theMethod()
+ """)
+ #classes = getASTNodeFromSrc(src,"Source").fastparseroot.getChildNodes()
+ classes = createAST(src).fastparseroot.getChildNodes()
+ self.assertEqual(classes[3].getBaseClassNames(),['a','b'])
+
+ def test_returnsEmptyListForClassWithNoBases(self):
+ src = trimLines("""
+ class root:
+ pass
+ """)
+ #classes = getASTNodeFromSrc(src,"Source").fastparseroot.getChildNodes()
+ classes = createAST(src).fastparseroot.getChildNodes()
+ self.assertEqual(classes[0].getBaseClassNames(),[])
+
+
+class TestGetMaskedLines(BRMTestCase):
+ def test_doit(self):
+ src =trimLines("""
+ class foo: #bah
+ pass
+ """)
+ mod = createAST(src).fastparseroot
+ lines = mod.getMaskedModuleLines()
+ assert lines[0] == "class foo: #***\n"
+
+
+class TestGetLinesNotIncludingThoseBelongingToChildScopes(BRMTestCase):
+ def test_worksForModule(self):
+ src =trimLines("""
+ class TheClass:
+ def theMethod():
+ pass
+ def foo():
+ b = TheClass()
+ return b
+ a = foo()
+ a.theMethod()
+ """)
+ mod = createAST(src).fastparseroot
+ self.assertEqual(''.join(mod.getLinesNotIncludingThoseBelongingToChildScopes()),
+ trimLines("""
+ a = foo()
+ a.theMethod()
+ """))
+
+ def test_worksForModuleWithSingleLineFunctions(self):
+ src=trimLines("""
+ a = blah()
+ def foo(): pass
+ b = 1
+ """)
+ mod = createAST(src).fastparseroot
+ lines = mod.getLinesNotIncludingThoseBelongingToChildScopes()
+ self.assertEqual(''.join(lines),
+ trimLines("""
+ a = blah()
+ b = 1
+ """))
+
+
+ def test_worksForSingleLineFunction(self):
+ src=trimLines("""
+ a = blah()
+ def foo(): pass
+ b = 1
+ """)
+ fn = createAST(src).fastparseroot.getChildNodes()[0]
+ lines = fn.getLinesNotIncludingThoseBelongingToChildScopes()
+ self.assertEqual(''.join(lines),
+ trimLines("""
+ def foo(): pass
+ """))
+
+
+fnWithEmptyLineInIt = """
+class TheClass:
+ def theFunction():
+ a = foo()
+
+ print 'a'
+
+ # end of function
+"""
+
+if __name__ == "__main__":
+ unittest.main()
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/test_load.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,18 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+import compiler
+import os
+
+from bike import testdata
+from bike.testutils import *
+from bike.mock import Mock
+
+from pathutils import getPathOfModuleOrPackage
+from load import *
+import load as loadmodule
+
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/test_newstuff.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,284 @@
+#!/usr/bin/env python
+import setpath
+import os
+import unittest
+from bike.testutils import *
+from bike.parsing.fastparserast import getRoot
+from bike.parsing.newstuff import getModuleOrPackageUsingFQN,\
+ generateModuleFilenamesInPythonPath, getSourceNodesContainingRegex,\
+ generatePackageDependencies
+
+
+class TestGetModuleOrPackageUsingFQN(BRMTestCase):
+ def test_worksForFullPath(self):
+ try:
+ createPackageStructure("pass","pass")
+ self.assertEqual(getModuleOrPackageUsingFQN("a.b.bah").filename,
+ pkgstructureFile2)
+ finally:
+ removePackageStructure()
+
+ def test_worksForPackage(self):
+ try:
+ createPackageStructure("pass","pass")
+ self.assertEqual(getModuleOrPackageUsingFQN("a.b").path,
+ pkgstructureChilddir)
+ finally:
+ removePackageStructure()
+
+
+class TestGenerateModuleFilenamesInPythonPath(BRMTestCase):
+ def test_works(self):
+ try:
+ createPackageStructure("pass","pass")
+ fnames = [f for f in \
+ generateModuleFilenamesInPythonPath(pkgstructureFile2)]
+
+
+ assert os.path.join(pkgstructureBasedir,"__init__.py") in fnames
+ assert pkgstructureFile1 in fnames
+ assert os.path.join(pkgstructureChilddir,"__init__.py") in fnames
+ assert pkgstructureFile2 in fnames
+ assert len(fnames) == 5
+ finally:
+ removePackageStructure()
+
+
+
+ def test_doesntTraverseIntoNonPackages(self):
+ try:
+ createPackageStructure("pass","pass")
+ nonPkgDir = os.path.join(pkgstructureChilddir,"c")
+ newfile = os.path.join(nonPkgDir,"baz.py")
+ # N.B. don't put an __init__.py in it, so isnt a package
+ os.makedirs(nonPkgDir)
+ writeFile(newfile,"pass")
+ fnames = [f for f in \
+ generateModuleFilenamesInPythonPath(pkgstructureFile2)]
+ assert newfile not in fnames
+ finally:
+ #os.remove(initfile)
+ os.remove(newfile)
+ os.removedirs(nonPkgDir)
+ removePackageStructure()
+
+
+ def test_doesScanFilesInTheRootDirectory(self):
+ try:
+ createPackageStructure("pass","pass","pass")
+ fnames = [f for f in \
+ generateModuleFilenamesInPythonPath(pkgstructureFile2)]
+ assert pkgstructureFile0 in fnames
+ finally:
+ #os.remove(initfile)
+ removePackageStructure()
+
+ def test_returnsOtherFilesInSameNonPackageDirectory(self):
+ try:
+ oldpath = getRoot().pythonpath
+ getRoot().pythonpath = [] # clear the python path
+ writeTmpTestFile("")
+ newtmpfile = os.path.join(tmproot,"baz.py")
+ writeFile(newtmpfile, "")
+ fnames = [f for f in \
+ generateModuleFilenamesInPythonPath(tmpfile)]
+ assert newtmpfile in fnames
+ finally:
+ os.remove(newtmpfile)
+ deleteTmpTestFile()
+ getRoot().pythonpath = oldpath
+
+
+
+ def test_doesntTraverseIntoNonPackagesUnderRoot(self):
+ try:
+ os.makedirs(pkgstructureBasedir)
+ writeFile(pkgstructureFile1,"pass")
+ fnames = [f for f in \
+ generateModuleFilenamesInPythonPath(pkgstructureFile2)]
+ assert pkgstructureFile1 not in fnames
+ finally:
+ os.remove(pkgstructureFile1)
+ os.removedirs(pkgstructureBasedir)
+
+
+ def test_doesntGenerateFilenamesMoreThanOnce(self):
+ try:
+ createPackageStructure("pass","pass")
+ newfile = os.path.join(pkgstructureChilddir,"baz.py")
+ writeFile(newfile,"pass")
+ fnames = [f for f in \
+ generateModuleFilenamesInPythonPath(pkgstructureFile2)]
+ matched = [f for f in fnames if f == newfile]
+ self.assertEqual(1, len(matched))
+ finally:
+ os.remove(newfile)
+ removePackageStructure()
+
+class TestGetSourceNodesContainingRegex(BRMTestCase):
+ def test_works(self):
+ try:
+ createPackageStructure("# testregexfoobah","pass")
+ srcfiles = [s for s in
+ getSourceNodesContainingRegex("testregexfoobah",
+ pkgstructureFile2)]
+ self.assertEqual(pkgstructureFile1,srcfiles[0].filename)
+ finally:
+ removePackageStructure()
+
+class TestGenerateModuleFilenamesInPythonPath2(BRMTestCase):
+ def test_getsAllFilenamesInSameHierarchyAsContextFile(self):
+ try:
+ oldpath = getRoot().pythonpath
+ getRoot().pythonpath = [] # clear the python path
+ createPackageStructure("","")
+ fnames = [f for f in
+ generateModuleFilenamesInPythonPath(pkgstructureFile1)]
+ self.assert_(pkgstructureFile0 in fnames)
+ self.assert_(pkgstructureFile1 in fnames)
+ self.assert_(pkgstructureFile2 in fnames)
+ finally:
+ getRoot().pythonpath = oldpath
+ removePackageStructure()
+
+ def test_getsFilenamesInSubPackagesIfCtxFilenameIsInTheRoot(self):
+ try:
+ oldpath = getRoot().pythonpath
+ getRoot().pythonpath = [] # clear the python path
+ createPackageStructure("","")
+ fnames = [f for f in
+ generateModuleFilenamesInPythonPath(pkgstructureFile0)]
+ self.assert_(pkgstructureFile1 in fnames)
+ self.assert_(pkgstructureFile2 in fnames)
+ finally:
+ getRoot().pythonpath = oldpath
+ removePackageStructure()
+
+ def test_doesntTraverseOtherPackagesOffOfTheRoot(self):
+ try:
+ oldpath = getRoot().pythonpath
+ getRoot().pythonpath = [] # clear the python path
+ createPackageStructure("","")
+ os.makedirs(os.path.join(pkgstructureRootDir, "c"))
+ writeFile(os.path.join(pkgstructureRootDir, "c", "__init__.py"), "# ")
+ bazfile = os.path.join(pkgstructureRootDir, "c", "baz.py")
+ writeFile(bazfile, "pass")
+ fnames = [f for f in
+ generateModuleFilenamesInPythonPath(pkgstructureFile1)]
+ self.assert_(pkgstructureFile0 in fnames)
+ self.assert_(pkgstructureFile1 in fnames)
+ self.assert_(pkgstructureFile2 in fnames)
+ self.assert_(bazfile not in fnames)
+ finally:
+ getRoot().pythonpath = oldpath
+ os.remove(os.path.join(pkgstructureRootDir, "c", "baz.py"))
+ os.remove(os.path.join(pkgstructureRootDir, "c", "__init__.py"))
+ os.removedirs(os.path.join(pkgstructureRootDir, "c"))
+ removePackageStructure()
+
+
+class TestGetPackageDependencies(BRMTestCase):
+
+ def test_followsImportModule(self):
+ try:
+ createPackageStructure("","import c.bing")
+ createSecondPackageStructure("")
+ dependencies = [d for d in
+ generatePackageDependencies(pkgstructureFile2)]
+ self.assertEqual([pkgstructureBasedir2],dependencies)
+ finally:
+ removeSecondPackageStructure()
+ removePackageStructure()
+
+
+ def test_followsFromImportPackage(self):
+ try:
+ createPackageStructure("","import c")
+ createSecondPackageStructure("")
+ dependencies = [d for d in
+ generatePackageDependencies(pkgstructureFile2)]
+ self.assertEqual([pkgstructureBasedir2],dependencies)
+ finally:
+ removeSecondPackageStructure()
+ removePackageStructure()
+
+
+
+ def test_followsFromImportStar(self):
+ try:
+ createPackageStructure("","from c import *")
+ createSecondPackageStructure("")
+ dependencies = [d for d in
+ generatePackageDependencies(pkgstructureFile2)]
+ self.assertEqual([pkgstructureBasedir2],dependencies)
+ finally:
+ removeSecondPackageStructure()
+ removePackageStructure()
+
+ def test_followsFromImportModule(self):
+ try:
+ createPackageStructure("","from c import bing")
+ createSecondPackageStructure("")
+ dependencies = [d for d in
+ generatePackageDependencies(pkgstructureFile2)]
+ self.assertEqual([pkgstructureBasedir2],dependencies)
+ finally:
+ removeSecondPackageStructure()
+ removePackageStructure()
+
+
+ def test_doesntBreakIfImportIsInAMultilineString(self):
+ try:
+ createPackageStructure("",trimLines("""
+ '''
+ from aoeuaoeu import aocxaoieicxoe
+ '''
+ """))
+ createSecondPackageStructure("")
+ dependencies = [d for d in
+ generatePackageDependencies(pkgstructureFile2)]
+ self.assertEqual([],dependencies)
+ finally:
+ removeSecondPackageStructure()
+ removePackageStructure()
+
+ def test_doesntBreakIfImportIsCommented(self):
+ try:
+ createPackageStructure("","#from aoeuaoeu import aocxaoieicxoe")
+ createSecondPackageStructure("")
+ dependencies = [d for d in
+ generatePackageDependencies(pkgstructureFile2)]
+ self.assertEqual([],dependencies)
+ finally:
+ removeSecondPackageStructure()
+ removePackageStructure()
+
+
+ def test_doesntBreakIfCantFindImport(self):
+ try:
+ createPackageStructure("","from aoeuaoeu import aocxaoieicxoe")
+ createSecondPackageStructure("")
+ dependencies = [d for d in
+ generatePackageDependencies(pkgstructureFile2)]
+ self.assertEqual([],dependencies)
+ finally:
+ removeSecondPackageStructure()
+ removePackageStructure()
+
+
+ def test_doesntIncludeCurrentPackage(self):
+ try:
+ createPackageStructure("","import a.foo")
+ createSecondPackageStructure("")
+ dependencies = [d for d in
+ generatePackageDependencies(pkgstructureFile2)]
+ self.assertEqual([],dependencies)
+ finally:
+ removeSecondPackageStructure()
+ removePackageStructure()
+
+
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/test_parserutils.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,135 @@
+#!/usr/bin/env python
+import unittest
+from parserutils import *
+from bike.testutils import *
+
+class TestRemoveEscapedQuotes(BRMTestCase):
+
+ def testMaskEscapedQuotes_MasksEscapedQuotes(self):
+ src = '\" \\\\\\\" \' \\\\\\\\\" \' \''
+ self.assertEqual(maskEscapedQuotes(src),'" **** \' ****" \' \'')
+
+class TestMungePythonKeywordsInStrings(BRMTestCase):
+ def test_mungesKeywords(self):
+ src = '\"\"\"class try while\"\"\" class2 try2 while2 \'\'\' def if for \'\'\' def2 if2 for2'
+ self.assertEqual(maskPythonKeywordsInStringsAndComments(src),
+ '"""CLASS TRY WHILE""" class2 try2 while2 \'\'\' DEF IF FOR \'\'\' def2 if2 for2')
+
+
+class TestSplitLines(BRMTestCase):
+ def test_handlesExplicitlyContinuedLineWithComment(self):
+ self.assertEqual(splitLogicalLines(explicitlyContinuedLineWithComment),
+ ['\n', 'z = a + b + \\ # comment\n c + d\n', 'pass\n'])
+
+ def test_handlesImplicitlyContinuedLine(self):
+ self.assertEqual(splitLogicalLines(implicitlyContinuedLine),
+ ['\n', 'z = a + b + (\n c + d)\n', 'pass\n'])
+
+ def test_handlesNestedImplicitlyContinuedLine(self):
+ self.assertEqual(splitLogicalLines(implicitlyContinuedLine2),
+ ['\n', 'z = a + b + ( c + [d\n + e]\n + f) # comment\n', 'pass\n'])
+
+
+ def test_handlesMultiLineStrings(self):
+ self.assertEqual(splitLogicalLines(multilineComment),
+ ['\n', "''' this is an mlc\nso is this\n'''\n", 'pass\n'])
+
+
+class TestMakeLineParseable(BRMTestCase):
+ def test_worksWithIfStatement(self):
+ src = "if foo:"
+ self.assertEqual(makeLineParseable(src),("if foo: pass"))
+
+ def test_worksWithTryStatement(self):
+ src = "try :"
+ self.assertEqual(makeLineParseable(src),("try : pass\nexcept: pass"))
+
+ def test_worksOnTryStatementWithCodeInlined(self):
+ src = "try : a = 1"
+ self.assertEqual(makeLineParseable(src),("try : a = 1\nexcept: pass"))
+
+ def test_worksWithExceptStatement(self):
+ src = "except :"
+ self.assertEqual(makeLineParseable(src),("try: pass\nexcept : pass"))
+
+ def test_worksWithFinallyStatement(self):
+ src = "finally:"
+ self.assertEqual(makeLineParseable(src),("try: pass\nfinally: pass"))
+
+ def test_worksWithIfStatement(self):
+ src = "if foo:"
+ self.assertEqual(makeLineParseable(src),("if foo: pass"))
+
+ def test_worksWithElseStatement(self):
+ src = "else :"
+ self.assertEqual(makeLineParseable(src),("if 0: pass\nelse : pass"))
+
+ def test_worksWithElifStatement(self):
+ src = "elif foo:"
+ self.assertEqual(makeLineParseable(src),("if 0: pass\nelif foo: pass"))
+
+
+def runOverPath(path):
+ import compiler
+ from parser import ParserError
+ from bike.parsing.load import getFilesForName
+ files = getFilesForName(path)
+ for fname in files:
+ print fname
+ src = file(fname).read()
+ #print src
+ src = maskStringsAndRemoveComments(src)
+
+ for logicalline in splitLogicalLines(src):
+ #print "logicalline=",logicalline
+ logicalline = logicalline.strip()
+ logicalline = makeLineParseable(logicalline)
+ try:
+ compiler.parse(logicalline)
+ except ParserError:
+ print "ParserError on logicalline:",logicalline
+ except:
+ log.exception("caught exception")
+
+
+explicitlyContinuedLineWithComment = """
+z = a + b + \ # comment
+ c + d
+pass
+"""
+
+implicitlyContinuedLine = """
+z = a + b + (
+ c + d)
+pass
+"""
+
+
+implicitlyContinuedLine2 = """
+z = a + b + ( c + [d
+ + e]
+ + f) # comment
+pass
+"""
+
+multilineComment = """
+''' this is an mlc
+so is this
+'''
+pass
+"""
+
+if __name__ == "__main__":
+ from bike import logging
+ logging.init()
+ log = logging.getLogger("bike")
+ log.setLevel(logging.INFO)
+
+ # add soak tests to end of test
+ class Z_SoakTest(BRMTestCase):
+ def test_linesRunThroughPythonParser(self):
+ print ""
+ #print splitLogicalLines(file('/usr/local/lib/python2.2/aifc.py').read())
+ #runOverPath('/usr/local/lib/python2.2/test/badsyntax_nocaret.py')
+ runOverPath('/usr/local/lib/python2.2/')
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/test_pathutils.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,128 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+import compiler
+import os
+
+from bike import testdata
+from bike.testutils import *
+from bike.mock import Mock
+
+from pathutils import getPathOfModuleOrPackage
+from pathutils import *
+import pathutils as loadmodule
+
+class TestGetFilesForName(BRMTestCase):
+ def testGetFilesForName_recursivelyReturnsFilesInBreadthFirstOrder(self):
+ createPackageStructure("pass", "pass")
+
+ files = getFilesForName(pkgstructureBasedir)
+ for f in files:
+ assert f in \
+ [os.path.join(pkgstructureBasedir, '__init__.py'),
+ os.path.join(pkgstructureBasedir, 'foo.py'),
+ os.path.join(pkgstructureChilddir, '__init__.py'),
+ os.path.join(pkgstructureChilddir, 'bah.py')]
+
+ def testGetFilesForName_globsStars(self):
+ createPackageStructure("pass", "pass")
+ assert getFilesForName(os.path.join(pkgstructureBasedir, "fo*")) == [os.path.join(pkgstructureBasedir, 'foo.py')]
+ removePackageStructure()
+
+ def testGetFilesForName_doesntListFilesWithDotAtFront(self):
+ writeFile(os.path.join(".foobah.py"),"")
+ files = getFilesForName("a")
+ self.assertEqual([],files)
+
+
+
+
+class TestGetRootDirectory(BRMTestCase):
+ def test_returnsParentDirectoryIfFileNotInPackage(self):
+ try:
+ # this doesnt have __init__.py file, so
+ # isnt package
+ os.makedirs("a")
+ writeFile(os.path.join("a", "foo.py"), "pass")
+ dir = loadmodule.getRootDirectory(os.path.join("a", "foo.py"))
+ assert dir == "a"
+ finally:
+ os.remove(os.path.join("a", "foo.py"))
+ os.removedirs(os.path.join("a"))
+
+ def test_returnsFirstNonPackageParentDirectoryIfFileInPackage(self):
+ try:
+ os.makedirs(os.path.join("root", "a", "b"))
+ writeFile(os.path.join("root", "a", "__init__.py"), "# ")
+ writeFile(os.path.join("root", "a", "b", "__init__.py"), "# ")
+ writeFile(os.path.join("root", "a", "b", "foo.py"), "pass")
+ dir = loadmodule.getRootDirectory(os.path.join("root", "a", "b", "foo.py"))
+ assert dir == "root"
+ finally:
+ os.remove(os.path.join("root", "a", "__init__.py"))
+ os.remove(os.path.join("root", "a", "b", "__init__.py"))
+ os.remove(os.path.join("root", "a", "b", "foo.py"))
+ os.removedirs(os.path.join("root", "a", "b"))
+
+ def test_returnsFirstNonPackageParentDirectoryIfPathIsAPackage(self):
+ try:
+ os.makedirs(os.path.join("root", "a", "b"))
+ writeFile(os.path.join("root", "a", "__init__.py"), "# ")
+ writeFile(os.path.join("root", "a", "b", "__init__.py"), "# ")
+ writeFile(os.path.join("root", "a", "b", "foo.py"), "pass")
+ dir = loadmodule.getRootDirectory(os.path.join("root", "a", "b"))
+ assert dir == "root"
+ finally:
+ os.remove(os.path.join("root", "a", "__init__.py"))
+ os.remove(os.path.join("root", "a", "b", "__init__.py"))
+ os.remove(os.path.join("root", "a", "b", "foo.py"))
+ os.removedirs(os.path.join("root", "a", "b"))
+
+ def test_returnsDirIfDirIsTheRootDirectory(self):
+ try:
+ os.makedirs(os.path.join("root", "a", "b"))
+ writeFile(os.path.join("root", "a", "__init__.py"), "# ")
+ writeFile(os.path.join("root", "a", "b", "__init__.py"), "# ")
+ writeFile(os.path.join("root", "a", "b", "foo.py"), "pass")
+ dir = loadmodule.getRootDirectory("root")
+ assert dir == "root"
+ finally:
+ os.remove(os.path.join("root", "a", "__init__.py"))
+ os.remove(os.path.join("root", "a", "b", "__init__.py"))
+ os.remove(os.path.join("root", "a", "b", "foo.py"))
+ os.removedirs(os.path.join("root", "a", "b"))
+
+
+class getPackageBaseDirectory(BRMTestCase):
+ def test_returnsBasePackageIfFileInPackageHierarchy(self):
+ try:
+ createPackageStructure("","")
+ dir = loadmodule.getPackageBaseDirectory(pkgstructureFile2)
+ self.assertEqual(pkgstructureBasedir, dir)
+ finally:
+ removePackageStructure()
+
+ def test_returnsFileDirectoryIfFileNotInPackage(self):
+ try:
+ createPackageStructure("","")
+ dir = loadmodule.getPackageBaseDirectory(pkgstructureFile0)
+ self.assertEqual(pkgstructureRootDir, dir)
+ finally:
+ removePackageStructure()
+
+
+class TestGetPathOfModuleOrPackage(BRMTestCase):
+ def test_worksForFullPath(self):
+ try:
+ createPackageStructure("pass","pass")
+ import sys
+ self.assertEqual(getPathOfModuleOrPackage("a.b.bah",
+ [pkgstructureRootDir]),
+ pkgstructureFile2)
+ finally:
+ removePackageStructure()
+
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/test_utils.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,41 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+from utils import *
+
+class Test_CarCdrEtc(unittest.TestCase):
+ def test_carReturnsTheFirstElementOfTheFqn(self):
+ fqn = "apple.pear.foo"
+ assert fqn_car(fqn) == "apple"
+
+ def test_carReturnsElementInOneElementFqn(self):
+ fqn = "apple"
+ assert fqn_car(fqn) == "apple"
+
+ def test_cdrReturnsTheAllElementsOfTheFqnExceptFirst(self):
+ fqn = "apple.pear.foo"
+ assert fqn_cdr(fqn) == "pear.foo"
+
+ def test_cdrReturnsEmptyStringForOneElementFqn(self):
+ fqn = "apple"
+ assert fqn_cdr(fqn) == ""
+
+ def test_rcarReturnsTheLastElementOfTheFqn(self):
+ fqn = "apple.pear.foo"
+ assert fqn_rcar(fqn) == "foo"
+
+ def test_rcarReturnsElementInOneElementFqn(self):
+ fqn = "apple"
+ assert fqn_rcar(fqn) == "apple"
+
+ def test_rdrReturnsTheAllElementsOfTheFqnExceptLast(self):
+ fqn = "apple.pear.foo"
+ assert fqn_rcdr(fqn) == "apple.pear"
+
+ def test_rdrReturnsEmptyStringForOneElementFqn(self):
+ fqn = "apple"
+ assert fqn_rcdr(fqn) == ""
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/test_visitor.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,114 @@
+#!/usr/bin/env python
+from __future__ import generators
+import setpath
+import unittest
+import visitor
+from bike.testutils import *
+
+class TestVisitor(BRMTestCase):
+ def test_callsVistorFunctions(self):
+ tree = createTree()
+
+ class TreeVisitor:
+ def __init__(self):
+ self.txt = []
+
+ def visitAClass(self,node):
+ self.txt.append("visitAClass")
+ self.txt.append(node.txt)
+ return self.visitChildren(node)
+
+ def visitCClass(self,node):
+ self.txt.append("visitCClass")
+ self.txt.append(node.txt)
+
+ def getTxt(self):
+ return ",".join(self.txt)
+
+ self.assertEqual(visitor.walk(tree,TreeVisitor()).getTxt(),
+ "visitAClass,aclass,visitCClass,cclass0,visitCClass,cclass1,visitCClass,cclass2")
+
+
+ def test_callsVisitorFunctionsWithYield(self):
+ tree = createTree()
+
+ class TreeVisitor:
+ def __init__(self):
+ self.txt = []
+
+ def visitAClass(self,node):
+ self.txt.append("visitAClass")
+ self.txt.append(node.txt)
+ yield node
+ for i in self.visitChildren(node):
+ yield i
+
+ def visitCClass(self,node):
+ self.txt.append("visitCClass")
+ self.txt.append(node.txt)
+ if 0: yield 1
+
+ def getTxt(self):
+ return ",".join(self.txt)
+
+ for node in visitor.walkAndGenerate(tree,TreeVisitor()):
+ assert node.txt == "aclass"
+
+
+def createTree():
+ n = AClass("aclass")
+ for i in xrange(3):
+ b = n.addChildNode(BClass("bclass%d" % i))
+ for j in xrange(20):
+ b = b.addChildNode(BClass("bclass%d" % i))
+ b.addChildNode(CClass("cclass%d" % i))
+ return n
+
+class node(object):
+ def __init__(self,txt):
+ self._childNodes=[]
+ self.txt = txt
+
+ def addChildNode(self,node):
+ self._childNodes.append(node)
+ return node
+
+ def getChildNodes(self):
+ return [x for x in self._childNodes]
+
+
+class AClass(node):
+ pass
+
+class BClass(node):
+ pass
+
+class CClass(node):
+ pass
+
+if __name__ == "__main__":
+
+ # add perf test at end of tests
+ class Z_SoakTestFastparser(BRMTestCase):
+ def test_parsesPythonLibraryCorrectly(self):
+
+ class TreeVisitor:
+ pass
+
+ import time
+
+ tree = createTree()
+
+ t1 = time.time()
+ for i in xrange(1000):
+ visitor.walk(tree,TreeVisitor())
+ print "tree without yield",time.time()-t1
+
+ t1 = time.time()
+ for i in xrange(1000):
+ for node in visitor.walkAndGenerate(tree,TreeVisitor()):
+ pass
+ print "tree with yield",time.time()-t1
+
+
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/testall.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,18 @@
+#!/usr/bin/env python
+import setpath
+
+import unittest
+
+#import all the tests
+from test_load import *
+from test_newstuff import *
+from test_parserutils import *
+from test_fastparser import *
+from test_fastparserast import *
+
+if __name__ == "__main__":
+ from bike import logging
+ logging.init()
+ log = logging.getLogger("bike")
+ log.setLevel(logging.WARN)
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/utils.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,30 @@
+# get the first element of a fully qualified python path
+#(e.g. _car('a.b.c.d') = 'a')
+def fqn_car(fqn):
+ try:
+ return fqn[:fqn.index(".")]
+ except ValueError: # i.e. no dots in fqn
+ return fqn
+
+# get the other elements of a fully qualified python path
+#(e.g. _cdr('a.b.c.d') = 'b.c.d')
+def fqn_cdr(fqn):
+ try:
+ return fqn[fqn.index(".")+1:]
+ except ValueError: # i.e. no dots in fqn
+ return ""
+
+# reverse of above _rcar("a.b.c.d") = "d"
+def fqn_rcar(fqn):
+ try:
+ return fqn[fqn.rindex(".")+1:]
+ except ValueError: # i.e. no dots in fqn
+ return fqn
+
+
+# reverse of above _rcdr("a.b.c.d") = "a.b.c"
+def fqn_rcdr(fqn):
+ try:
+ return fqn[:fqn.rindex(".")]
+ except ValueError: # i.e. no dots in fqn
+ return ""
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/visitor.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,49 @@
+from __future__ import generators
+
+class TreeWalker(object):
+ VERBOSE = 0
+
+ def __init__(self):
+ self.node = None
+ self._cache = {}
+
+ def default(self, node, *args):
+ for child in node.getChildNodes():
+ self.dispatch(child, *args)
+
+ def dispatch(self, node, *args):
+ self.node = node
+ klass = node.__class__
+ meth = self._cache.get(klass, None)
+ if meth is None:
+ className = klass.__name__
+ meth = getattr(self.visitor, 'visit' + className, self.default)
+ self._cache[klass] = meth
+ return meth(node, *args)
+
+ def preorder(self, tree, visitor, *args):
+ """Do preorder walk of tree using visitor"""
+ self.visitor = visitor
+ visitor.visit = self.dispatch
+ visitor.visitChildren = self.default
+ return self.dispatch(tree, *args)
+
+class GeneratingTreeWalker(TreeWalker):
+
+ def default(self, node, *args):
+ for child in node.getChildNodes():
+ for i in self.dispatch(child, *args):
+ yield i
+
+
+def walk(tree, visitor):
+ walker = TreeWalker()
+ walker.preorder(tree, visitor)
+ return walker.visitor
+
+
+def walkAndGenerate(tree,visitor):
+ walker = GeneratingTreeWalker()
+ return walker.preorder(tree, visitor)
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/__init__.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,1 @@
+#
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/common.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,333 @@
+from __future__ import generators
+from bike.globals import *
+from bike.parsing.fastparserast import getRoot, Function, Class, Module, getModule
+from bike.parsing.parserutils import generateLogicalLines, makeLineParseable, UnbalancedBracesException, generateLogicalLinesAndLineNumbers
+from bike.parsing.newstuff import getSourceNodesContainingRegex
+from bike.parsing import visitor
+from bike import log
+import compiler
+from compiler.ast import Getattr, Name
+import re
+
+class Match:
+ def __repr__(self):
+ return ",".join([self.filename, str(self.lineno), str(self.colno),
+ str(self.confidence)])
+ def __eq__(self,other):
+ if self is None or other is None:
+ return False
+ return self.filename == other.filename and \
+ self.lineno == other.lineno and \
+ self.colno == other.colno
+
+def getScopeForLine(sourceNode, lineno):
+ scope = None
+ childnodes = sourceNode.getFlattenedListOfFastParserASTNodes()
+ if childnodes == []:
+ return sourceNode.fastparseroot #module node
+
+ scope = sourceNode.fastparseroot
+
+ for node in childnodes:
+ if node.linenum > lineno: break
+ scope = node
+
+ if scope.getStartLine() != scope.getEndLine(): # is inline
+ while scope.getEndLine() <= lineno:
+ scope = scope.getParent()
+ return scope
+
+
+
+# global from the perspective of 'contextFilename'
+def globalScanForMatches(contextFilename, matchFinder, targetname):
+ for sourcenode in getSourceNodesContainingRegex(targetname, contextFilename):
+ print >> log.progress, "Scanning", sourcenode.filename
+ searchscope = sourcenode.fastparseroot
+ for match in scanScopeForMatches(sourcenode,searchscope,
+ matchFinder,targetname):
+ yield match
+
+
+def scanScopeForMatches(sourcenode,scope,matchFinder,targetname):
+ lineno = scope.getStartLine()
+ for line in generateLogicalLines(scope.getMaskedLines()):
+ if line.find(targetname) != -1:
+ doctoredline = makeLineParseable(line)
+ ast = compiler.parse(doctoredline)
+ scope = getScopeForLine(sourcenode, lineno)
+ matchFinder.reset(line)
+ matchFinder.setScope(scope)
+ matches = visitor.walk(ast, matchFinder).getMatches()
+ for index, confidence in matches:
+ match = Match()
+ match.filename = sourcenode.filename
+ match.sourcenode = sourcenode
+ x, y = indexToCoordinates(line, index)
+ match.lineno = lineno+y
+ match.colno = x
+ match.colend = match.colno+len(targetname)
+ match.confidence = confidence
+ yield match
+ lineno+=line.count("\n")
+
+
+def walkLinesContainingStrings(scope,astWalker,targetnames):
+ lineno = scope.getStartLine()
+ for line in generateLogicalLines(scope.getMaskedLines()):
+ if lineContainsOneOf(line,targetnames):
+ doctoredline = makeLineParseable(line)
+ ast = compiler.parse(doctoredline)
+ astWalker.lineno = lineno
+ matches = visitor.walk(ast, astWalker)
+ lineno+=line.count("\n")
+
+
+def lineContainsOneOf(line,targetnames):
+ for name in targetnames:
+ if line.find(name) != -1:
+ return True
+ return False
+
+
+# translates an idx in a logical line into physical line coordinates
+# returns x and y coords
+def indexToCoordinates(src, index):
+ y = src[: index].count("\n")
+ startOfLineIdx = src.rfind("\n", 0, index)+1
+ x = index-startOfLineIdx
+ return x, y
+
+
+
+# interface for MatchFinder classes
+# implement the visit methods
+class MatchFinder:
+ def setScope(self, scope):
+ self.scope = scope
+
+ def reset(self, line):
+ self.matches = []
+ self.words = re.split("(\w+)", line) # every other one is a non word
+ self.positions = []
+ i = 0
+ for word in self.words:
+ self.positions.append(i)
+ #if '\n' in word: # handle newlines
+ # i = len(word[word.index('\n')+1:])
+ #else:
+ i+=len(word)
+ self.index = 0
+
+ def getMatches(self):
+ return self.matches
+
+ # need to visit childnodes in same order as they appear
+ def visitPrintnl(self,node):
+ if node.dest:
+ self.visit(node.dest)
+ for n in node.nodes:
+ self.visit(n)
+
+ def visitName(self, node):
+ self.popWordsUpTo(node.name)
+
+ def visitClass(self, node):
+ self.popWordsUpTo(node.name)
+ for base in node.bases:
+ self.visit(base)
+
+ def zipArgs(self, argnames, defaults):
+ """Takes a list of argument names and (possibly a shorter) list of
+ default values and zips them into a list of pairs (argname, default).
+ Defaults are aligned so that the last len(defaults) arguments have
+ them, and the first len(argnames) - len(defaults) pairs have None as a
+ default.
+ """
+ fixed_args = len(argnames) - len(defaults)
+ defaults = [None] * fixed_args + list(defaults)
+ return zip(argnames, defaults)
+
+ def visitFunction(self, node):
+ self.popWordsUpTo(node.name)
+ for arg, default in self.zipArgs(node.argnames, node.defaults):
+ self.popWordsUpTo(arg)
+ if default is not None:
+ self.visit(default)
+ self.visit(node.code)
+
+ def visitGetattr(self,node):
+ self.visit(node.expr)
+ self.popWordsUpTo(node.attrname)
+
+ def visitAssName(self, node):
+ self.popWordsUpTo(node.name)
+
+ def visitAssAttr(self, node):
+ self.visit(node.expr)
+ self.popWordsUpTo(node.attrname)
+
+ def visitImport(self, node):
+ for name, alias in node.names:
+ for nameelem in name.split("."):
+ self.popWordsUpTo(nameelem)
+ if alias is not None:
+ self.popWordsUpTo(alias)
+
+ def visitFrom(self, node):
+ for elem in node.modname.split("."):
+ self.popWordsUpTo(elem)
+ for name, alias in node.names:
+ self.popWordsUpTo(name)
+ if alias is not None:
+ self.popWordsUpTo(alias)
+
+ def visitLambda(self, node):
+ for arg, default in self.zipArgs(node.argnames, node.defaults):
+ self.popWordsUpTo(arg)
+ if default is not None:
+ self.visit(default)
+ self.visit(node.code)
+
+ def visitGlobal(self, node):
+ for name in node.names:
+ self.popWordsUpTo(name)
+
+ def popWordsUpTo(self, word):
+ if word == "*":
+ return # won't be able to find this
+ posInWords = self.words.index(word)
+ idx = self.positions[posInWords]
+ self.words = self.words[posInWords+1:]
+ self.positions = self.positions[posInWords+1:]
+
+ def appendMatch(self,name,confidence=100):
+ idx = self.getNextIndexOfWord(name)
+ self.matches.append((idx, confidence))
+
+ def getNextIndexOfWord(self,name):
+ return self.positions[self.words.index(name)]
+
+class CouldNotLocateNodeException(Exception): pass
+
+def translateSourceCoordsIntoASTNode(filename,lineno,col):
+ module = getModule(filename)
+ maskedlines = module.getMaskedModuleLines()
+ lline,backtrackchars = getLogicalLine(module, lineno)
+ doctoredline = makeLineParseable(lline)
+ ast = compiler.parse(doctoredline)
+ idx = backtrackchars+col
+ nodefinder = ASTNodeFinder(lline,idx)
+ node = compiler.walk(ast, nodefinder).node
+ if node is None:
+ raise CouldNotLocateNodeException("Could not translate editor coordinates into source node")
+ return node
+
+def getLogicalLine(module, lineno):
+ # we know that the scope is the start of a logical line, so
+ # we search from there
+ scope = getScopeForLine(module.getSourceNode(), lineno)
+ linegenerator = \
+ module.generateLinesWithLineNumbers(scope.getStartLine())
+ for lline,llinenum in \
+ generateLogicalLinesAndLineNumbers(linegenerator):
+ if llinenum > lineno:
+ break
+ prevline = lline
+ prevlinenum = llinenum
+
+ backtrackchars = 0
+ for i in range(prevlinenum,lineno):
+ backtrackchars += len(module.getSourceNode().getLines()[i-1])
+ return prevline, backtrackchars
+
+
+
+class ASTNodeFinder(MatchFinder):
+ # line is a masked line of text
+ # lineno and col are coords
+ def __init__(self,line,col):
+ self.line = line
+ self.col = col
+ self.reset(line)
+ self.node = None
+
+ def visitName(self,node):
+ if self.checkIfNameMatchesColumn(node.name):
+ self.node = node
+ self.popWordsUpTo(node.name)
+
+ def visitGetattr(self,node):
+ self.visit(node.expr)
+ if self.checkIfNameMatchesColumn(node.attrname):
+ self.node = node
+ self.popWordsUpTo(node.attrname)
+
+ def visitFunction(self, node):
+ if self.checkIfNameMatchesColumn(node.name):
+ self.node = node
+ self.popWordsUpTo(node.name)
+
+ for arg, default in self.zipArgs(node.argnames, node.defaults):
+ if self.checkIfNameMatchesColumn(arg):
+ self.node = Name(arg)
+ self.popWordsUpTo(arg)
+ if default is not None:
+ self.visit(default)
+ self.visit(node.code)
+
+
+ visitAssName = visitName
+ visitAssAttr = visitGetattr
+
+ def visitClass(self, node):
+ if self.checkIfNameMatchesColumn(node.name):
+ self.node = node
+ self.popWordsUpTo(node.name)
+ for base in node.bases:
+ self.visit(base)
+
+
+ def checkIfNameMatchesColumn(self,name):
+ idx = self.getNextIndexOfWord(name)
+ #print "name",name,"idx",idx,"self.col",self.col
+ if idx <= self.col and idx+len(name) > self.col:
+ return 1
+ return 0
+
+ def visitFrom(self, node):
+ for elem in node.modname.split("."):
+ self.popWordsUpTo(elem)
+ for name, alias in node.names:
+ if self.checkIfNameMatchesColumn(name):
+ self.node = self._manufactureASTNodeFromFQN(name)
+ return
+ self.popWordsUpTo(name)
+ if alias is not None:
+ self.popWordsUpTo(alias)
+
+ # gets round the fact that imports etc dont contain nested getattr
+ # nodes for fqns (e.g. import a.b.bah) by converting the fqn
+ # string into a getattr instance
+ def _manufactureASTNodeFromFQN(self,fqn):
+ if "." in fqn:
+ assert 0, "getattr not supported yet"
+ else:
+ return Name(fqn)
+
+def isAMethod(scope,node):
+ return isinstance(node,compiler.ast.Function) and \
+ isinstance(scope,Class)
+
+def convertNodeToMatchObject(node,confidence=100):
+ m = Match()
+ m.sourcenode = node.module.getSourceNode()
+ m.filename = node.filename
+ if isinstance(node,Module):
+ m.lineno = 1
+ m.colno = 0
+ elif isinstance(node,Class) or isinstance(node,Function):
+ m.lineno = node.getStartLine()
+ m.colno = node.getColumnOfName()
+ m.confidence = confidence
+ return m
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/findDefinition.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,277 @@
+from __future__ import generators
+from bike.query.common import Match, MatchFinder, \
+ getScopeForLine, indexToCoordinates, \
+ translateSourceCoordsIntoASTNode, scanScopeForMatches, \
+ isAMethod, convertNodeToMatchObject, walkLinesContainingStrings
+from bike.parsing.parserutils import generateLogicalLines,\
+ generateLogicalLinesAndLineNumbers, \
+ splitLogicalLines, makeLineParseable
+import compiler
+from compiler.ast import Getattr, Name, AssName, AssAttr
+from bike.parsing.fastparserast import getRoot, Package, Class, \
+ Module, Function, Instance
+import re
+from bike.query.getTypeOf import getTypeOfExpr, UnfoundType, \
+ isWordInLine, resolveImportedModuleOrPackage
+from bike.parsing import visitor
+from bike.parsing.visitor import walkAndGenerate
+
+from bike.parsing.parserutils import makeLineParseable,splitLogicalLines
+from bike.parsing.newstuff import getSourceNodesContainingRegex
+from bike.parsing.load import getSourceNode
+from bike import log
+
+
+class CantFindDefinitionException:
+ pass
+
+
+def findAllPossibleDefinitionsByCoords(filepath,lineno,col):
+
+ #try:
+ node = translateSourceCoordsIntoASTNode(filepath,lineno,col)
+ #except:
+ # import traceback
+ # traceback.print_exc()
+
+ if node is None:
+ raise "selected node type not supported"
+ scope = getScopeForLine(getSourceNode(filepath),lineno)
+ match = findDefinitionFromASTNode(scope,node)
+ if match is not None:
+ yield match
+ if isinstance(node,Getattr) and (match is None or match.confidence != 100):
+ root = getRoot()
+ name = node.attrname
+ for match in scanPythonPathForMatchingMethodNames(name,filepath):
+ yield match
+ print >>log.progress,"done"
+
+
+def findDefinitionFromASTNode(scope,node):
+ assert node is not None
+ if isinstance(node,Name) or isinstance(node,AssName):
+ while 1:
+ # try scope children
+ childscope = scope.getChild(node.name)
+ if childscope is not None:
+ return convertNodeToMatchObject(childscope,100)
+
+ if isinstance(scope,Package):
+ scope = scope.getChild("__init__")
+
+ # try arguments and assignments
+ match = scanScopeAST(scope,node.name,
+ AssignmentAndFnArgsSearcher(node.name))
+ if match is not None:
+ return match
+
+ # try imports
+ match = searchImportedModulesForDefinition(scope,node)
+ if match is not None:
+ return match
+
+
+ if not isinstance(scope,Module):
+ # try parent scope
+ scope = scope.getParent()
+ else:
+ break
+ assert isinstance(scope,Module)
+
+ elif isinstance(node,Getattr) or isinstance(node,AssAttr):
+ exprtype = getTypeOfExpr(scope,node.expr)
+ if not (exprtype is None or isinstance(exprtype,UnfoundType)):
+ if isinstance(exprtype,Instance):
+ exprtype = exprtype.getType()
+ match = findDefinitionOfAttributeFromASTNode(exprtype,
+ node.attrname)
+ else:
+ match = findDefinitionFromASTNode(exprtype,
+ Name(node.attrname))
+ if match is not None:
+ return match
+
+ elif isinstance(node,compiler.ast.Function) or \
+ isinstance(node,compiler.ast.Class):
+ if isAMethod(scope,node):
+ match = findDefinitionOfAttributeFromASTNode(scope,
+ node.name)
+ else:
+ match = findDefinitionFromASTNode(scope,Name(node.name))
+ if match is not None:
+ return match
+
+
+ type = getTypeOfExpr(scope,node)
+ if type is not None and (not isinstance(type,UnfoundType)) and \
+ (not isinstance(type,Instance)):
+ return convertNodeToMatchObject(type,100)
+ else:
+ return None
+
+
+def findDefinitionOfAttributeFromASTNode(type,name):
+ assert isinstance(type,Class)
+ attrfinder = AttrbuteDefnFinder([type],name)
+
+ # first scan the method names:
+ for child in type.getChildNodes():
+ if child.name == name:
+ return convertNodeToMatchObject(child,100)
+ # then scan the method source for attribues
+ for child in type.getChildNodes():
+ if isinstance(child,Function):
+ try:
+ return scanScopeForMatches(child.module.getSourceNode(),
+ child, attrfinder,
+ name).next()
+ except StopIteration:
+ continue
+
+
+class AttrbuteDefnFinder(MatchFinder):
+ def __init__(self,targetClasses,targetAttribute):
+ self.targetClasses = targetClasses
+ self.targetAttributeName = targetAttribute
+
+ def visitAssAttr(self, node):
+ for c in node.getChildNodes():
+ self.visit(c)
+
+ if node.attrname == self.targetAttributeName:
+ exprtype = getTypeOfExpr(self.scope,node.expr)
+ if isinstance(exprtype,Instance) and \
+ exprtype.getType() in self.targetClasses:
+ self.appendMatch(self.targetAttributeName)
+ #else:
+ # self.appendMatch(self.targetAttributeName,50)
+ self.popWordsUpTo(node.attrname)
+
+
+
+
+def searchImportedModulesForDefinition(scope,node):
+ lines = scope.module.getSourceNode().getLines()
+ for lineno in scope.getImportLineNumbers():
+ logicalline = getLogicalLine(lines,lineno)
+ logicalline = makeLineParseable(logicalline)
+ ast = compiler.parse(logicalline)
+ class ImportVisitor:
+ def __init__(self,node):
+ self.target = node
+ self.match = None
+ assert isinstance(self.target,Name), \
+ "Getattr not supported"
+
+ def visitFrom(self, node):
+ module = resolveImportedModuleOrPackage(scope,node.modname)
+ if module is None: # couldn't find module
+ return
+
+ if node.names[0][0] == '*': # e.g. from foo import *
+ match = findDefinitionFromASTNode(module,self.target)
+ if match is not None:
+ self.match = match
+ return
+
+ for name, alias in node.names:
+ if alias is None and name == self.target.name:
+ match = findDefinitionFromASTNode(module,self.target)
+ if match is not None:
+ self.match = match
+ return
+
+
+ match = visitor.walk(ast, ImportVisitor(node)).match
+ if match:
+ return match
+ # loop
+
+
+def getLogicalLine(lines,lineno):
+ return generateLogicalLines(lines[lineno-1:]).next()
+
+class AssignmentAndFnArgsSearcher(MatchFinder):
+ def __init__(self,name):
+ self.targetname = name
+ self.match = None
+
+ def visitAssName(self, node):
+ if node.name == self.targetname:
+ idx = self.getNextIndexOfWord(self.targetname)
+ self.match = idx
+ return
+
+ def visitFunction(self, node):
+ self.popWordsUpTo(node.name)
+ for arg, default in self.zipArgs(node.argnames, node.defaults):
+ if arg == self.targetname:
+ idx = self.getNextIndexOfWord(self.targetname)
+ self.match = idx
+ return
+ self.popWordsUpTo(arg)
+ if default is not None:
+ self.visit(default)
+ self.visit(node.code)
+
+ def getMatch(self):
+ return self.match
+
+
+
+# scans for lines containing keyword, and then runs the visitor over
+# the parsed AST for that line
+def scanScopeAST(scope,keyword,matchfinder):
+ lines = scope.generateLinesNotIncludingThoseBelongingToChildScopes()
+ match = None
+ for line,linenum in generateLogicalLinesAndLineNumbers(lines):
+ if isWordInLine(keyword, line):
+ doctoredline = makeLineParseable(line)
+ ast = compiler.parse(doctoredline)
+ matchfinder.reset(line)
+ match = visitor.walk(ast,matchfinder).getMatch()
+ if match is not None:
+ column,yoffset = indexToCoordinates(line,match)
+ m = createMatch(scope,linenum + yoffset,column)
+ return m
+ return None
+
+def createMatch(scope,lineno,x):
+ m = Match()
+ m.sourcenode = scope.module.getSourceNode()
+ m.filename = m.sourcenode.filename
+ m.lineno = lineno
+ m.colno = x
+ m.confidence = 100
+ return m
+
+# scan for methods globally (from perspective of 'perspectiveFilename')
+def scanPythonPathForMatchingMethodNames(name, contextFilename):
+ class MethodFinder:
+ def __init__(self,srcnode):
+ self.matches = []
+ self.srcnode = srcnode
+ def visitFunction(self,node):
+ node = getScopeForLine(self.srcnode, self.lineno)
+ if isinstance(node.getParent(),Class):
+ if node.name == name:
+ self.matches.append(convertNodeToMatchObject(node,50))
+
+ for srcnode in getSourceNodesContainingRegex(name,contextFilename):
+ m = MethodFinder(srcnode)
+ walkLinesContainingStrings(srcnode.fastparseroot,m,[name])
+ for match in m.matches:
+ yield match
+
+
+def getIndexOfWord(line,targetword):
+ words = re.split("(\w+)", line)
+ idx = 0
+ for word in words:
+ if word == targetword:
+ break
+ idx += len(word)
+ return idx
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/findReferences.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,224 @@
+from __future__ import generators
+from bike.globals import *
+from bike.parsing.fastparserast import Module, Class, Function, getRoot, Instance
+from bike.query.common import Match, MatchFinder,\
+ getScopeForLine, indexToCoordinates, \
+ translateSourceCoordsIntoASTNode, scanScopeForMatches,\
+ globalScanForMatches, isAMethod, convertNodeToMatchObject
+from compiler.ast import AssName,Name,Getattr,AssAttr
+import compiler
+from findDefinition import findDefinitionFromASTNode
+from bike.query.getTypeOf import getTypeOfExpr, UnfoundType
+from bike.query.relationships import getRootClassesOfHierarchy
+from bike import log
+from bike.parsing.load import getSourceNode
+
+
+class CouldntFindDefinitionException(Exception):
+ pass
+
+def findReferencesIncludingDefn(filename,lineno,col):
+ return findReferences(filename,lineno,col,1)
+
+
+def findReferences(filename,lineno,col,includeDefn=0):
+ sourcenode = getSourceNode(filename)
+ node = translateSourceCoordsIntoASTNode(filename,lineno,col)
+ assert node is not None
+ scope,defnmatch = getDefinitionAndScope(sourcenode,lineno,node)
+
+ try:
+ for match in findReferencesIncludingDefn_impl(sourcenode,node,
+ scope,defnmatch):
+ if not includeDefn and match == defnmatch:
+ continue # don't return definition
+ else:
+ yield match
+ except CouldntFindDefinitionException:
+ raise CouldntFindDefinitionException("Could not find definition. Please locate manually (maybe using find definition) and find references from that")
+
+def findReferencesIncludingDefn_impl(sourcenode,node,scope,defnmatch):
+ if isinstance(node,Name) or isinstance(node,AssName):
+ return generateRefsToName(node.name,scope,sourcenode,defnmatch)
+ elif isinstance(node,Getattr) or isinstance(node,AssAttr):
+ exprtype = getTypeOfExpr(scope,node.expr)
+ if exprtype is None or isinstance(exprtype,UnfoundType):
+ raise CouldntFindDefinitionException()
+
+ if isinstance(exprtype,Instance):
+ exprtype = exprtype.getType()
+ return generateRefsToAttribute(exprtype,node.attrname)
+
+ else:
+ targetname = node.attrname
+ return globalScanForMatches(sourcenode.filename,
+ NameRefFinder(targetname, defnmatch),
+ targetname, )
+ if match is not None:
+ return match
+ elif isinstance(node,compiler.ast.Function) or \
+ isinstance(node,compiler.ast.Class):
+ return handleClassOrFunctionRefs(scope, node, defnmatch)
+ else:
+ assert 0,"Seed to references must be Name,Getattr,Function or Class"
+
+def handleClassOrFunctionRefs(scope, node, defnmatch):
+ if isAMethod(scope,node):
+ for ref in generateRefsToAttribute(scope,node.name):
+ yield ref
+ else:
+ #yield convertNodeToMatchObject(node,100)
+ yield defnmatch
+ for ref in generateRefsToName(node.name,scope,
+ scope.module.getSourceNode(),
+ defnmatch):
+ yield ref
+
+def getDefinitionAndScope(sourcenode,lineno,node):
+ scope = getScopeForLine(sourcenode,lineno)
+ if scope.getStartLine() == lineno and \
+ scope.matchesCompilerNode(node): # scope is the node
+ return scope.getParent(), convertNodeToMatchObject(scope,100)
+ defnmatch = findDefinitionFromASTNode(scope,node)
+ if defnmatch is None:
+ raise CouldntFindDefinitionException()
+ scope = getScopeForLine(sourcenode,defnmatch.lineno)
+ return scope,defnmatch
+
+def generateRefsToName(name,scope,sourcenode,defnmatch):
+ assert scope is not None
+ if isinstance(scope,Function):
+ # search can be limited to scope
+ return scanScopeForMatches(sourcenode,scope,
+ NameRefFinder(name,defnmatch),
+ name)
+ else:
+ return globalScanForMatches(sourcenode.filename,
+ NameRefFinder(name,defnmatch),
+ name)
+
+
+class NameRefFinder(MatchFinder):
+ def __init__(self, targetstr,targetMatch):
+ self.targetstr = targetstr
+ self.targetMatch = targetMatch
+
+ def visitName(self, node):
+ if node.name == self.targetstr:
+ potentualMatch = findDefinitionFromASTNode(self.scope, node)
+ if potentualMatch is not None and \
+ potentualMatch == self.targetMatch:
+ self.appendMatch(node.name)
+ self.popWordsUpTo(node.name)
+
+ visitAssName = visitName
+
+ def visitFunction(self, node):
+ self.popWordsUpTo(node.name)
+ for arg, default in self.zipArgs(node.argnames, node.defaults):
+ if arg == self.targetstr:
+ self.appendMatch(arg)
+ self.popWordsUpTo(arg)
+ if default is not None:
+ self.visit(default)
+ self.visit(node.code)
+
+
+ def visitFrom(self, node):
+ for elem in node.modname.split("."):
+ self.popWordsUpTo(elem)
+
+ for name, alias in node.names:
+ if name == self.targetstr:
+ if alias is not None:
+ pretendNode = Name(alias)
+ else:
+ pretendNode = Name(name)
+ if findDefinitionFromASTNode(self.scope, pretendNode) \
+ == self.targetMatch:
+ self.appendMatch(name)
+ self.popWordsUpTo(name)
+ if alias is not None:
+ self.popWordsUpTo(alias)
+
+
+ def visitGetattr(self, node):
+ for c in node.getChildNodes():
+ self.visit(c)
+ if node.attrname == self.targetstr:
+ defn = findDefinitionFromASTNode(self.scope, node)
+ if defn is not None and defn == self.targetMatch:
+ self.appendMatch(node.attrname)
+ self.popWordsUpTo(node.attrname)
+
+
+ def visitImport(self, node):
+ for name, alias in node.names:
+ if name.split(".")[-1] == self.targetstr:
+ getattr = self.createGetattr(name)
+ if findDefinitionFromASTNode(self.scope, getattr) == self.targetMatch:
+ self.appendMatch(self.targetstr)
+ for nameelem in name.split("."):
+ self.popWordsUpTo(nameelem)
+ if alias is not None:
+ self.popWordsUpTo(alias)
+
+
+ def createGetattr(self,fqn):
+ node = Name(fqn[0])
+ for name in fqn.split(".")[1:]:
+ node = Getattr(node,name)
+ return node
+
+def generateRefsToAttribute(classobj,attrname):
+ rootClasses = getRootClassesOfHierarchy(classobj)
+ attrRefFinder = AttrbuteRefFinder(rootClasses,attrname)
+ for ref in globalScanForMatches(classobj.filename, attrRefFinder, attrname):
+ yield ref
+ print >>log.progress,"Done"
+
+
+class AttrbuteRefFinder(MatchFinder):
+ def __init__(self,rootClasses,targetAttribute):
+ self.rootClasses = rootClasses
+ self.targetAttributeName = targetAttribute
+
+
+ def visitGetattr(self, node):
+ for c in node.getChildNodes():
+ self.visit(c)
+
+ if node.attrname == self.targetAttributeName:
+ exprtype = getTypeOfExpr(self.scope,node.expr)
+
+ if isinstance(exprtype,Instance) and \
+ self._isAClassInTheSameHierarchy(exprtype.getType()):
+ self.appendMatch(self.targetAttributeName)
+ elif isinstance(exprtype,UnfoundType) or \
+ exprtype is None: # couldn't find type, so not sure
+ self.appendMatch(self.targetAttributeName,50)
+ else:
+ pass # definately not a match
+ self.popWordsUpTo(node.attrname)
+
+ visitAssAttr = visitGetattr
+
+ def visitFunction(self,node): # visit methods
+ if node.name == self.targetAttributeName:
+ parentScope = self.scope.getParent()
+ #print parentScope
+ #print self.targetClasses
+ if isinstance(parentScope,Class) and \
+ self._isAClassInTheSameHierarchy(parentScope):
+ self.appendMatch(node.name)
+
+ for c in node.getChildNodes():
+ self.visit(c)
+
+ def _isAClassInTheSameHierarchy(self,classobj):
+ #return classobj in self.targetClasses
+ targetRootClasses = getRootClassesOfHierarchy(classobj)
+ for rootclass in self.rootClasses:
+ if rootclass in targetRootClasses:
+ return True
+ return False
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/getAllRelatedClasses.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,47 @@
+
+
+"""
+
+def getAllRelatedClasses(root,classfqn):
+ classobj = getTypeOf(root,classfqn)
+ rootClasses = _getRootClasses(classobj)
+ #print rootClasses
+ relatedClasses = [] + rootClasses
+ for rootClass in rootClasses:
+ relatedClasses += _getAllSubClasses(rootClass,root)
+ return relatedClasses
+
+def _getRootClasses(klass):
+ if klass is None: # i.e. dont have base class in our ast
+ return None
+ if klass.getBaseClassNames() == []: # i.e. is a root class
+ return[klass]
+ else:
+ rootclasses = []
+ for base in klass.getBaseClassNames():
+ baseclass = getTypeOf(klass,base)
+ rootclass = _getRootClasses(baseclass)
+ if rootclass is None: # base class not in our ast
+ rootclass = [klass]
+ rootclasses+=rootclass
+ return rootclasses
+
+
+def _getAllSubClasses(baseclass, root, subclasses = []):
+ class ClassVisitor:
+ def visitSource(self,node):
+ self.visit(node.fastparseroot)
+
+ def visitClass(self, node):
+ for basename in node.getBaseClassNames():
+ if basename.find(baseclass.name) != -1 and \
+ getTypeOf(node,basename) == baseclass:
+ subclasses.append(node)
+ _getAllSubClasses(node,root,subclasses)
+ for child in node.getChildNodes():
+ self.visit(child)
+
+ walk(root, ClassVisitor())
+ return subclasses
+
+"""
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/getPackageDependencies.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,6 @@
+#!/usr/bin/env python
+
+# fileInPackage is the filename of a file in the package hierarchy
+def getPackageDependencies(fileInPackage):
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/getReferencesToModule.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,62 @@
+from __future__ import generators
+from bike.query.common import Match, globalScanForMatches, getScopeForLine, MatchFinder
+from getTypeOf import getTypeOf, getTypeOfExpr
+import compiler
+import re
+
+def getReferencesToModule(root, fqn):
+
+ modulename = fqn.split(".")[-1]
+ moduleobj = getTypeOf(root, fqn)
+ moduleRefFinder = ModuleRefFinder(moduleobj)
+
+ for ref in globalScanForMatches(moduleRefFinder, modulename):
+ yield ref
+
+
+class ModuleRefFinder(MatchFinder):
+ def __init__(self, targetmodule):
+ self.targetmodule = targetmodule
+
+ def visitName(self, node):
+ if node.name == self.targetmodule.name:
+ if getTypeOfExpr(self.scope, node) == self.targetmodule:
+ self.appendMatch(node.name)
+ self.popWordsUpTo(node.name)
+
+ def visitImport(self, node):
+ for name, alias in node.names:
+ if name.split(".")[-1] == self.targetmodule.name:
+ if getTypeOf(self.scope, name) == self.targetmodule:
+ self.appendMatch(self.targetmodule.name)
+ for nameelem in name.split("."):
+ self.popWordsUpTo(nameelem)
+ if alias is not None:
+ self.popWordsUpTo(alias)
+
+ def visitGetattr(self, node):
+ for c in node.getChildNodes():
+ self.visit(c)
+ if node.attrname == self.targetmodule.name:
+ if getTypeOfExpr(self.scope, node) == self.targetmodule:
+ self.appendMatch(self.targetmodule.name)
+ self.popWordsUpTo(node.attrname)
+
+ def visitFrom(self, node):
+ for elem in node.modname.split("."):
+ if elem == self.targetmodule.name:
+ getTypeOf(self.scope, elem) == self.targetmodule
+ self.appendMatch(self.targetmodule.name)
+ self.popWordsUpTo(elem)
+
+ for name, alias in node.names:
+ if name == self.targetmodule.name:
+ if alias and \
+ getTypeOf(self.scope, alias) == self.targetmodule:
+ self.appendMatch(self.targetmodule.name)
+ elif getTypeOf(self.scope, name) == self.targetmodule:
+ self.appendMatch(self.targetmodule.name)
+ if name != "*":
+ self.popWordsUpTo(name)
+ if alias is not None:
+ self.popWordsUpTo(alias)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/getTypeOf.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,377 @@
+# getTypeOf(scope,fqn) and getTypeOfExpr(scope,ast)
+
+from bike.parsing.fastparserast import Class, Function, Module, Root, getRoot, Package, Instance, getModule
+from bike.parsing.parserutils import generateLogicalLines, makeLineParseable,splitLogicalLines, makeLineParseable
+from bike.parsing import visitor
+from bike import log
+from bike.parsing.newstuff import getModuleOrPackageUsingFQN
+from bike.parsing.pathutils import getPackageBaseDirectory
+from bike.parsing.load import Cache
+import os
+import re
+import compiler
+
+# used if an assignment exists, but cant find type
+# e.g. a = SomeFunctionNotLoaded()
+# (as opposed to 'None' if cant find an assignment)
+class UnfoundType: pass
+
+
+getTypeOfStack = []
+
+# name is the fqn of the reference, scope is the scope ast object from
+# which the question is being asked.
+# returns an fastparser-ast object representing the type
+# or None if type not found
+def getTypeOf(scope, fqn):
+ if isinstance(scope, Root):
+ assert False, "Can't use getTypeOf to resolve from Root. Use getModuleOrPackageUsingFQN instead"
+
+
+ #print "getTypeOf:"+fqn+" -- "+str(scope)
+ #print
+ #print str(getTypeOfStack)
+ #print
+ if (fqn,scope) in getTypeOfStack: # loop protection
+ return None
+
+ # this is crap!
+ hashcode = str(scope)+fqn
+
+ try:
+ getTypeOfStack.append((fqn,scope))
+
+ try:
+ type = Cache.instance.typecache[hashcode]
+ except KeyError:
+ type = getTypeOf_impl(scope, fqn)
+ Cache.instance.typecache[hashcode] = type
+ return type
+ finally:
+ del getTypeOfStack[-1]
+
+
+
+def getTypeOf_impl(scope, fqn):
+ #print "getTypeOf_impl",scope,fqn
+ if fqn == "None":
+ return None
+
+ if "."in fqn:
+ rcdr = ".".join(fqn.split(".")[:-1])
+ rcar = fqn.split(".")[-1]
+ newscope = getTypeOf(scope,rcdr)
+ if newscope is not None:
+ return getTypeOf(newscope, rcar)
+ else:
+ #print "couldnt find "+rcdr+" in "+str(scope)
+ pass
+
+ assert scope is not None
+ #assert not ("." in fqn)
+
+ if isinstance(scope,UnfoundType):
+ return UnfoundType()
+
+ if isinstance(scope, Package):
+ #assert 0,scope
+ return handlePackageScope(scope, fqn)
+ elif isinstance(scope,Instance):
+ return handleClassInstanceAttribute(scope, fqn)
+ else:
+ return handleModuleClassOrFunctionScope(scope,fqn)
+
+
+
+def handleModuleClassOrFunctionScope(scope,name):
+ if name == "self" and isinstance(scope,Function) and \
+ isinstance(scope.getParent(),Class):
+ return Instance(scope.getParent())
+
+ matches = [c for c in scope.getChildNodes()if c.name == name]
+ if matches != []:
+ return matches[0]
+
+ type = scanScopeSourceForType(scope, name)
+ if type != None:
+ return type
+
+ #print "name = ",name,"scope = ",scope
+ type = getImportedType(scope, name) # try imported types
+ #print "type=",type
+ if type != None:
+ return type
+ parentScope = scope.getParent()
+ while isinstance(parentScope,Class):
+ # don't search class scope, since this is not accessible except
+ # through self (is this true?)
+ parentScope = parentScope.getParent()
+
+ if not (isinstance(parentScope,Package) or isinstance(parentScope,Root)):
+ return getTypeOf(parentScope, name)
+
+
+def handleClassInstanceAttribute(instance, attrname):
+ theClass = instance.getType()
+
+ # search methods and inner classes
+ match = theClass.getChild(attrname)
+ if match:
+ return match
+
+ #search methods for assignments with self.foo getattrs
+ for child in theClass.getChildNodes():
+ if not isinstance(child,Function):
+ continue
+ res = scanScopeAST(child,attrname,
+ SelfAttributeAssignmentVisitor(child,attrname))
+ if res is not None:
+ return res
+
+def handlePackageScope(package, fqn):
+ #print "handlePackageScope",package,fqn
+ child = package.getChild(fqn)
+ if child:
+ return child
+
+ if isinstance(package,Root):
+ return getModuleOrPackageUsingFQN(fqn)
+
+ # try searching the fs
+ node = getModuleOrPackageUsingFQN(fqn,package.path)
+ if node:
+ return node
+
+
+
+
+ # try the package init module
+ initmod = package.getChild("__init__")
+ if initmod is not None:
+ type = getImportedType(initmod, fqn)
+ if type:
+ return type
+ # maybe fqn is absolute
+ return getTypeOf(getRoot(), fqn)
+
+
+wordRE = re.compile("\w+")
+def isWordInLine(word, line):
+ if line.find(word) != -1:
+ words = wordRE.findall(line)
+ if word in words:
+ return 1
+ return 0
+
+def getImportedType(scope, fqn):
+ lines = scope.module.getSourceNode().getLines()
+ for lineno in scope.getImportLineNumbers():
+ logicalline = generateLogicalLines(lines[lineno-1:]).next()
+ logicalline = makeLineParseable(logicalline)
+ ast = compiler.parse(logicalline)
+ match = visitor.walk(ast, ImportVisitor(scope,fqn)).match
+ if match:
+ return match
+ #else loop
+
+class ImportVisitor:
+ def __init__(self,scope,fqn):
+ self.match = None
+ self.targetfqn = fqn
+ self.scope = scope
+
+ def visitImport(self, node):
+ # if target fqn is an import, then it must be a module or package
+ for name, alias in node.names:
+ if name == self.targetfqn:
+ self.match = resolveImportedModuleOrPackage(self.scope,name)
+ elif alias is not None and alias == self.targetfqn:
+ self.match = resolveImportedModuleOrPackage(self.scope,name)
+
+ def visitFrom(self, node):
+ if node.names[0][0] == '*': # e.g. from foo import *
+ if not "."in self.targetfqn:
+ module = resolveImportedModuleOrPackage(self.scope,
+ node.modname)
+ if module:
+ self.match = getTypeOf(module, self.targetfqn)
+ else:
+ for name, alias in node.names:
+ if alias == self.targetfqn or \
+ (alias is None and name == self.targetfqn):
+ scope = resolveImportedModuleOrPackage(self.scope,
+ node.modname)
+ if scope is not None:
+ if isinstance(scope,Package):
+ self.match = getModuleOrPackageUsingFQN(name,scope.path)
+ else:
+ assert isinstance(scope,Module)
+ self.match = getTypeOf(scope, name)
+
+
+
+
+class TypeNotSupportedException:
+ def __init__(self,msg):
+ self.msg = msg
+
+ def __str__(self):
+ return self.msg
+
+# attempts to evaluate the type of the expression
+def getTypeOfExpr(scope, ast):
+ if isinstance(ast, compiler.ast.Name):
+ return getTypeOf(scope, ast.name)
+
+ elif isinstance(ast, compiler.ast.Getattr) or \
+ isinstance(ast, compiler.ast.AssAttr):
+
+ # need to do this in order to match foo.bah.baz as
+ # a string in import statements
+ fqn = attemptToConvertGetattrToFqn(ast)
+ if fqn is not None:
+ return getTypeOf(scope,fqn)
+
+ expr = getTypeOfExpr(scope, ast.expr)
+ if expr is not None:
+ attrnametype = getTypeOf(expr, ast.attrname)
+ return attrnametype
+ return None
+
+ elif isinstance(ast, compiler.ast.CallFunc):
+ node = getTypeOfExpr(scope,ast.node)
+ if isinstance(node,Class):
+ return Instance(node)
+ elif isinstance(node,Function):
+ return getReturnTypeOfFunction(node)
+ else:
+ #raise TypeNotSupportedException, \
+ # "Evaluation of "+str(ast)+" not supported. scope="+str(scope)
+ print >> log.warning, "Evaluation of "+str(ast)+" not supported. scope="+str(scope)
+ return None
+
+
+def attemptToConvertGetattrToFqn(ast):
+ fqn = ast.attrname
+ ast = ast.expr
+ while isinstance(ast,compiler.ast.Getattr):
+ fqn = ast.attrname + "." + fqn
+ ast = ast.expr
+ if isinstance(ast,compiler.ast.Name):
+ return ast.name + "." + fqn
+ else:
+ return None
+
+
+getReturnTypeOfFunction_stack = []
+def getReturnTypeOfFunction(function):
+ if function in getReturnTypeOfFunction_stack: # loop protection
+ return None
+ try:
+ getReturnTypeOfFunction_stack.append(function)
+ return getReturnTypeOfFunction_impl(function)
+ finally:
+ del getReturnTypeOfFunction_stack[-1]
+
+def getReturnTypeOfFunction_impl(function):
+ return scanScopeAST(function,"return",ReturnTypeVisitor(function))
+
+
+# does parse of scope sourcecode to deduce type
+def scanScopeSourceForType(scope, name):
+ return scanScopeAST(scope,name,AssignmentVisitor(scope,name))
+
+
+# scans for lines containing keyword, and then runs the visitor over
+# the parsed AST for that line
+def scanScopeAST(scope,keyword,astvisitor):
+ lines = scope.getLinesNotIncludingThoseBelongingToChildScopes()
+ src = ''.join(lines)
+ match = None
+ #print "scanScopeAST:"+str(scope)
+ for line in splitLogicalLines(src):
+ if isWordInLine(keyword, line):
+ #print "scanning for "+keyword+" in line:"+line[:-1]
+ doctoredline = makeLineParseable(line)
+ ast = compiler.parse(doctoredline)
+ match = visitor.walk(ast,astvisitor).getMatch()
+ if match:
+ return match
+ return match
+
+
+class AssignmentVisitor:
+ def __init__(self,scope,targetName):
+ self.match=None
+ self.scope = scope
+ self.targetName = targetName
+
+ def getMatch(self):
+ return self.match
+
+ def visitAssign(self,node):
+ if isinstance(node.expr,compiler.ast.CallFunc):
+ for assnode in node.nodes:
+ if isinstance(assnode,compiler.ast.AssName) and \
+ assnode.name == self.targetName:
+ self.match = getTypeOfExpr(self.scope,node.expr)
+ if self.match is None:
+ self.match = UnfoundType()
+
+
+
+class SelfAttributeAssignmentVisitor:
+ def __init__(self,scope,targetName):
+ self.match=None
+ self.scope = scope
+ self.targetName = targetName
+
+ def getMatch(self):
+ return self.match
+
+ def visitAssign(self,node):
+ if isinstance(node.expr,compiler.ast.CallFunc):
+ for assnode in node.nodes:
+ if isinstance(assnode,compiler.ast.AssAttr) and \
+ isinstance(assnode.expr,compiler.ast.Name) and \
+ assnode.expr.name == "self" and \
+ assnode.attrname == self.targetName:
+ self.match = getTypeOfExpr(self.scope,node.expr)
+ #print "here!",self.match.getType().fqn
+
+
+class ReturnTypeVisitor:
+ def __init__(self,fn):
+ self.match=None
+ self.fn = fn
+
+ def getMatch(self):
+ return self.match
+
+ def visitReturn(self,node):
+ try:
+ self.match = getTypeOfExpr(self.fn,node.value)
+ except TypeNotSupportedException, ex:
+ pass
+
+
+def resolveImportedModuleOrPackage(scope,fqn):
+ # try searching from directory containing scope module
+ path = os.path.dirname(scope.module.filename)
+ node = getModuleOrPackageUsingFQN(fqn,path)
+ if node is not None:
+ return node
+
+ # try searching in same package hierarchy
+ basedir = getPackageBaseDirectory(scope.module.filename)
+ if fqn.split('.')[0] == os.path.split(basedir)[-1]:
+ # base package in fqn matches base directory
+ restOfFqn = ".".join(fqn.split('.')[1:])
+ node = getModuleOrPackageUsingFQN(restOfFqn,basedir)
+ if node is not None:
+ return node
+
+ # try searching the python path
+ node = getModuleOrPackageUsingFQN(fqn)
+ if node is not None:
+ return node
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/relationships.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,26 @@
+# queries to do with module/class/function relationships
+from __future__ import generators
+from bike.globals import *
+from getTypeOf import getTypeOf, getTypeOfExpr
+from bike.parsing.newstuff import generateModuleFilenamesInPythonPath, generateModuleFilenamesInPackage, getPythonPath
+from bike.parsing.pathutils import getPackageBaseDirectory
+from bike.query.common import MatchFinder, walkLinesContainingStrings, getScopeForLine
+from bike import log
+from bike.parsing.fastparserast import Module
+import re
+
+def getRootClassesOfHierarchy(klass):
+ if klass is None: # i.e. dont have base class in our ast
+ return None
+ if klass.getBaseClassNames() == []: # i.e. is a root class
+ return [klass]
+ else:
+ rootclasses = []
+ for base in klass.getBaseClassNames():
+ baseclass = getTypeOf(klass,base)
+ rootclass = getRootClassesOfHierarchy(baseclass)
+ if rootclass is None: # base class not in our ast
+ rootclass = [klass]
+ rootclasses+=rootclass
+ return rootclasses
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/setpath.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,5 @@
+import sys,os
+if not os.path.abspath("..") in sys.path:
+ from bike import log
+ print >> log.warning, "Appending to the system path. This should only happen in unit tests"
+ sys.path.append(os.path.abspath(".."))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/test_common.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,122 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+import os
+import compiler
+from bike.testutils import *
+from bike.parsing.load import getSourceNode
+from bike.parsing.fastparser import fastparser
+from bike.parsing.fastparserast import Module, Class
+from common import indexToCoordinates, getScopeForLine, walkLinesContainingStrings, translateSourceCoordsIntoASTNode
+
+
+class TestGetScopeForLine(BRMTestCase):
+
+ def test_worksWithFunctionScope(self):
+ src = trimLines("""
+ class a:
+ def foo():
+ pass
+ """)
+ node = createAST(src)
+ self.assertEqual(getScopeForLine(node,3).name,"foo")
+
+ def test_worksWithModuleScope(self):
+ src = trimLines("""
+ class TheClass:
+ pass
+ a = TheClass()
+ """)
+ node = createAST(src)
+ assert isinstance(getScopeForLine(node,3),Module)
+
+ def test_worksWithInlineClass(self):
+ src = trimLines("""
+ class TheClass: pass""")
+ node = createAST(src)
+ assert isinstance(getScopeForLine(node,1),Class)
+
+
+class TestIndexToCoordinates(BRMTestCase):
+
+ def test_worksOnSingleLineString(self):
+ src = trimLines('''
+ foo bah
+ ''')
+ x,y = indexToCoordinates(src,src.index("bah"))
+ self.assertEqual(x,4)
+ self.assertEqual(y,0)
+ x,y = indexToCoordinates(src,src.index("foo"))
+ self.assertEqual(x,0)
+ self.assertEqual(y,0)
+
+ def test_worksOnMultilLineString(self):
+ src = trimLines('''
+ foo bah
+ baz boh
+ ''')
+ x,y = indexToCoordinates(src,src.index("boh"))
+ self.assertEqual(x,4)
+ self.assertEqual(y,1)
+
+
+class TestTranslateSourceCoordsIntoASTNode(BRMTestCase):
+
+ def test_worksOnImport(self):
+ src = trimLines('''
+ from foo import bar, baz
+ ''')
+ createSourceNodeAt(src,"mymodule")
+ filename = os.path.abspath("mymodule.py")
+ node = translateSourceCoordsIntoASTNode(filename, 1, 17)
+ assert node.name == 'bar'
+
+ def test_worksOnMultiline(self):
+ src = trimLines("""
+ def foo(x,
+ y,
+ z):
+ return x*y*z
+ """)
+ createSourceNodeAt(src,"mymodule")
+ filename = os.path.abspath("mymodule.py")
+ node = translateSourceCoordsIntoASTNode(filename, 3, 8)
+ assert node.name == 'z'
+
+
+
+
+class TestMatchFinder(BRMTestCase):
+
+ def test_visitLambda(self):
+ from common import MatchFinder
+ finder = MatchFinder()
+ src = '''x = lambda a, b, c=None, d=None: (a + b) and c or d'''
+ ast = compiler.parse(src)
+ finder.reset(src)
+ compiler.walk(ast, finder)
+
+
+class TestWalkLinesContainingStrings(BRMTestCase):
+ def test_walksClasses(self):
+ src=trimLines("""
+ class TestClass(a,
+ baseclass):
+ pass
+ """)
+ class MyWalker:
+ def visitClass(self, node):
+ self.basenames = []
+ for name in node.bases:
+ self.basenames.append(name.name)
+
+ writeTmpTestFile(src)
+ srcnode = getSourceNode(tmpfile)
+ walker = MyWalker()
+ walkLinesContainingStrings(srcnode.fastparseroot,walker,
+ "baseclass")
+ self.assertEqual(["a","baseclass"],walker.basenames)
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/test_findDefinition.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,632 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+import os
+
+from bike import testdata
+from bike.query.findDefinition import findAllPossibleDefinitionsByCoords
+from bike.query.getTypeOf import getTypeOf,resolveImportedModuleOrPackage
+from bike.parsing.newstuff import getModuleOrPackageUsingFQN
+from bike.parsing.fastparserast import getRoot
+from bike.testutils import *
+
+class TestFindDefinitionByCoords(BRMTestCase):
+
+ def test_findsClassRef(self):
+ src=trimLines("""
+ class TheClass:
+ pass
+ a = TheClass()
+ """)
+ createSourceNodeAt(src,"mymodule")
+ defn = [x for x in findAllPossibleDefinitionsByCoords(os.path.abspath("mymodule.py"),3,6)]
+ assert defn[0].filename == os.path.abspath("mymodule.py")
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 6
+ assert defn[0].confidence == 100
+
+ def tests_findsMethodRef(self):
+ src=trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ a = TheClass()
+ a.theMethod()
+ """)
+
+ createSourceNodeAt(src,"mymodule")
+ defn = [x for x in findAllPossibleDefinitionsByCoords(os.path.abspath("mymodule.py"),5,3)]
+
+ assert defn[0].filename == os.path.abspath("mymodule.py")
+ assert defn[0].lineno == 2
+ assert defn[0].colno == 8
+ assert defn[0].confidence == 100
+
+
+ def test_returnsOtherMethodsWithSameName(self):
+ src=trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ a = SomeOtherClass()
+ a.theMethod()
+ """)
+
+ createSourceNodeAt(src,"mymodule")
+ defn = [x for x in findAllPossibleDefinitionsByCoords(os.path.abspath("mymodule.py"),5,3)]
+
+ assert defn[0].filename == os.path.abspath("mymodule.py")
+ assert defn[0].lineno == 2
+ assert defn[0].colno == 8
+ assert defn[0].confidence == 50
+
+
+
+
+ def test_findsTemporaryDefinition(self):
+ src=trimLines("""
+ a = 3
+ b = a + 1
+ """)
+ createSourceNodeAt(src,"mymodule")
+ defn = [x for x in findAllPossibleDefinitionsByCoords(os.path.abspath("mymodule.py"),2,4)]
+ assert defn[0].filename == os.path.abspath("mymodule.py")
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 0
+ assert defn[0].confidence == 100
+
+ def test_findsArgumentDefinition(self):
+ src=trimLines("""
+ def someFunction(a):
+ b = a + 1
+ """)
+ createSourceNodeAt(src,"mymodule")
+ defn = [x for x in findAllPossibleDefinitionsByCoords(os.path.abspath("mymodule.py"),2,8)]
+ assert defn[0].filename == os.path.abspath("mymodule.py")
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 17
+ assert defn[0].confidence == 100
+
+ def test_findsClassInstanceDefinition(self):
+ src=trimLines("""
+ class TheClass():
+ pass
+ a = TheClass()
+ print a
+ """)
+ createSourceNodeAt(src,"mymodule")
+ defn = [x for x in findAllPossibleDefinitionsByCoords(os.path.abspath("mymodule.py"),4,6)]
+ assert defn[0].filename == os.path.abspath("mymodule.py")
+ assert defn[0].lineno == 3
+ assert defn[0].colno == 0
+ assert defn[0].confidence == 100
+
+ def test_findsDefinitionInParentScope(self):
+ src=trimLines("""
+ a = 3
+ def foo(self):
+ b = a + 1
+ """)
+ createSourceNodeAt(src,"mymodule")
+ defn = [x for x in findAllPossibleDefinitionsByCoords(os.path.abspath("mymodule.py"),3,8)]
+ assert defn[0].filename == os.path.abspath("mymodule.py")
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 0
+ assert defn[0].confidence == 100
+
+ def test_findsDefinitionWithinFunction(self):
+ src=trimLines("""
+ def foo(yadda):
+ a = someFunction()
+ print a
+ """)
+ createSourceNodeAt(src,"mymodule")
+ defn = [x for x in findAllPossibleDefinitionsByCoords(os.path.abspath("mymodule.py"),3,10)]
+ assert defn[0].filename == os.path.abspath("mymodule.py")
+ assert defn[0].lineno == 2
+ assert defn[0].colno == 4
+ assert defn[0].confidence == 100
+
+
+ def test_findsDefinitionFromSubsequentAssignment(self):
+ src=trimLines("""
+ def foo(yadda):
+ a = 3
+ print a
+ a = 5
+ """)
+ createSourceNodeAt(src,"mymodule")
+ defn = [x for x in findAllPossibleDefinitionsByCoords(os.path.abspath("mymodule.py"),4,4)]
+ assert defn[0].filename == os.path.abspath("mymodule.py")
+ assert defn[0].lineno == 2
+ assert defn[0].colno == 4
+ assert defn[0].confidence == 100
+
+ def test_findsDefinitionFromDefinition(self):
+ src=trimLines("""
+ def foo(yadda):
+ a = 3
+ print a
+ a = 5
+ """)
+ createSourceNodeAt(src,"mymodule")
+ defn = [x for x in findAllPossibleDefinitionsByCoords(os.path.abspath("mymodule.py"),4,4)]
+ assert defn[0].filename == os.path.abspath("mymodule.py")
+ assert defn[0].lineno == 2
+ assert defn[0].colno == 4
+ assert defn[0].confidence == 100
+
+
+ def test_findsClassRefUsingFromImportStatement(self):
+ src=trimLines("""
+ from a.b.bah import TheClass
+ """)
+ classsrc=trimLines("""
+ class TheClass:
+ pass
+ """)
+ root = createSourceNodeAt(src,"a.foo")
+ root = createSourceNodeAt(classsrc, "a.b.bah")
+ module = getModuleOrPackageUsingFQN("a.foo")
+ filename = os.path.abspath(os.path.join("a","foo.py"))
+ defn = [x for x in findAllPossibleDefinitionsByCoords(filename,1,21)]
+ assert defn[0].filename == os.path.abspath(os.path.join("a","b","bah.py"))
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 6
+ assert defn[0].confidence == 100
+
+
+ def test_findsVariableRefUsingFromImportStatement(self):
+ importsrc=trimLines("""
+ from a.b.bah import mytext
+ print mytext
+ """)
+ src=trimLines("""
+ mytext = 'hello'
+ """)
+ root = createSourceNodeAt(importsrc,"a.foo")
+ root = createSourceNodeAt(src, "a.b.bah")
+ filename = os.path.abspath(os.path.join("a","foo.py"))
+ defn = [x for x in findAllPossibleDefinitionsByCoords(filename,2,6)]
+ assert defn[0].filename == os.path.abspath(os.path.join("a","b","bah.py"))
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 0
+ assert defn[0].confidence == 100
+
+
+ def test_findsVariableRefUsingImportStatement(self):
+ importsrc=trimLines("""
+ import a.b.bah
+ print a.b.bah.mytext
+ """)
+ src=trimLines("""
+ mytext = 'hello'
+ """)
+ root = createSourceNodeAt(importsrc,"a.foo")
+ root = createSourceNodeAt(src, "a.b.bah")
+ filename = os.path.abspath(os.path.join("a","foo.py"))
+ defn = [x for x in findAllPossibleDefinitionsByCoords(filename,2,14)]
+ assert defn[0].filename == os.path.abspath(os.path.join("a","b","bah.py"))
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 0
+ assert defn[0].confidence == 100
+
+
+ def test_findsVariableRefUsingFromImportStarStatement(self):
+ importsrc=trimLines("""
+ from a.b.bah import *
+ print mytext
+ """)
+ src=trimLines("""
+ mytext = 'hello'
+ """)
+ createSourceNodeAt(importsrc,"a.foo")
+ createSourceNodeAt(src, "a.b.bah")
+ filename = os.path.abspath(os.path.join("a","foo.py"))
+ defn = [x for x in findAllPossibleDefinitionsByCoords(filename,2,6)]
+ assert defn[0].filename == os.path.abspath(os.path.join("a","b","bah.py"))
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 0
+ assert defn[0].confidence == 100
+
+ def test_findsVariableRefUsingFromPackageImportModuleStatement(self):
+ importsrc=trimLines("""
+ from a.b import bah
+ print bah.mytext
+ """)
+ src=trimLines("""
+ mytext = 'hello'
+ """)
+ root = createSourceNodeAt(importsrc,"a.b.foo")
+ root = createSourceNodeAt(src, "a.b.bah")
+ filename = os.path.abspath(os.path.join("a","b","foo.py"))
+ defn = [x for x in findAllPossibleDefinitionsByCoords(filename,2,10)]
+ assert defn[0].filename == os.path.abspath(os.path.join("a","b","bah.py"))
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 0
+ assert defn[0].confidence == 100
+
+ def test_findsImportedVariableRefInAFunctionArg(self):
+ importsrc=trimLines("""
+ from a.b import bah
+ someFunction(bah.mytext)
+ """)
+ src=trimLines("""
+ mytext = 'hello'
+ """)
+ root = createSourceNodeAt(importsrc,"a.b.foo")
+ root = createSourceNodeAt(src, "a.b.bah")
+ filename = os.path.abspath(os.path.join("a","b","foo.py"))
+ defn = [x for x in findAllPossibleDefinitionsByCoords(filename,2,17)]
+ assert defn[0].filename == os.path.abspath(os.path.join("a","b","bah.py"))
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 0
+ assert defn[0].confidence == 100
+
+
+ def test_findsVariableRefUsingFromImportStatementInFunction(self):
+ importsrc=trimLines("""
+ def foo:
+ from a.b.bah import mytext
+ print mytext
+ """)
+ src=trimLines("""
+ mytext = 'hello'
+ """)
+ root = createSourceNodeAt(importsrc,"a.foo")
+ root = createSourceNodeAt(src, "a.b.bah")
+ filename = os.path.abspath(os.path.join("a","foo.py"))
+ defn = [x for x in findAllPossibleDefinitionsByCoords(filename,3,10)]
+ assert defn[0].filename == os.path.abspath(os.path.join("a","b","bah.py"))
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 0
+ assert defn[0].confidence == 100
+
+ def test_findsVariableRefByImportingModule(self):
+ importsrc=trimLines("""
+ import a.b.bah
+ print a.b.bah.mytext
+ """)
+ src=trimLines("""
+ mytext = 'hello'
+ """)
+ defn = self.helper(importsrc, src, 2, 14)
+ assert defn[0].filename == pkgstructureFile2
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 0
+ assert defn[0].confidence == 100
+
+
+ def test_findsVariableRefByImportingModuleWithFrom(self):
+ importsrc=trimLines("""
+ from a.b import bah
+ someFunction(bah.mytext)
+ """)
+ src=trimLines("""
+ mytext = 'hello'
+ """)
+
+ defn = self.helper(importsrc, src, 2, 17)
+ assert defn[0].filename == pkgstructureFile2
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 0
+ assert defn[0].confidence == 100
+
+
+ def helper(self, src, classsrc, line, col):
+ try:
+ createPackageStructure(src,classsrc)
+ filename = pkgstructureFile1
+ #Root(None,None,[pkgstructureRootDir])
+ defn = [x for x in findAllPossibleDefinitionsByCoords(filename,line,col)]
+ finally:
+ removePackageStructure()
+ return defn
+
+ def test_doesntfindVariableRefOfUnimportedModule(self):
+ importsrc=trimLines("""
+ # a.b.bah not imported
+ print a.b.bah.mytext
+ """)
+ src=trimLines("""
+ mytext = 'hello'
+ """)
+ root = createSourceNodeAt(importsrc,"a.b.foo")
+ root = createSourceNodeAt(src, "a.b.bah")
+ filename = os.path.abspath(os.path.join("a","b","foo.py"))
+ defn = [x for x in findAllPossibleDefinitionsByCoords(filename,2,14)]
+ self.assertEqual(defn,[])
+
+
+
+ def test_findsSelfAttributeDefinition(self):
+ src=trimLines("""
+ class MyClass:
+ def __init__(self):
+ self.a = 'hello'
+ def myMethod(self):
+ print self.a
+ """)
+ root = createSourceNodeAt(src,"mymodule")
+ filename = os.path.abspath("mymodule.py")
+ defn = [x for x in findAllPossibleDefinitionsByCoords(filename,5,18)]
+ assert defn[0].filename == os.path.abspath("mymodule.py")
+ assert defn[0].lineno == 3
+ assert defn[0].colno == 12
+ assert defn[0].confidence == 100
+
+ def test_findsSelfAttributeDefinitionFromSamePlace(self):
+ src=trimLines("""
+ class MyClass:
+ def __init__(self):
+ self.a = 'hello'
+ def myMethod(self):
+ print self.a
+ """)
+ root = createSourceNodeAt(src,"mymodule")
+ filename = os.path.abspath("mymodule.py")
+ defn = [x for x in findAllPossibleDefinitionsByCoords(filename,3,12)]
+ assert defn[0].filename == os.path.abspath("mymodule.py")
+ assert defn[0].lineno == 3
+ assert defn[0].colno == 12
+ assert defn[0].confidence == 100
+
+
+ def test_findsSelfAttributeDefinition(self):
+ src=trimLines("""
+ class MyClass:
+ def someOtherFn(self):
+ pass
+ def load(self, source):
+ # fastparser ast
+ self.fastparseroot = fastparser(source,self.modulename)
+ """)
+ root = createSourceNodeAt(src,"mymodule")
+ filename = os.path.abspath("mymodule.py")
+ defn = [x for x in findAllPossibleDefinitionsByCoords(filename,6,14)]
+ assert defn[0].filename == os.path.abspath("mymodule.py")
+ assert defn[0].lineno == 6
+ assert defn[0].colno == 13
+ assert defn[0].confidence == 100
+
+
+ def test_findsDefnOfInnerClass(self):
+ src = trimLines("""
+ class TheClass:
+ class TheClass:
+ pass
+ a = TheClass.TheClass()
+ """)
+ root = createSourceNodeAt(src,"mymodule")
+ filename = os.path.abspath("mymodule.py")
+ defn = [x for x in findAllPossibleDefinitionsByCoords(filename,4,14)]
+ assert defn[0].filename == os.path.abspath("mymodule.py")
+ assert defn[0].lineno == 2
+ assert defn[0].colno == 10
+ assert defn[0].confidence == 100
+
+ def test_findsDefnOfOuterClass(self):
+ src = trimLines("""
+ class TheClass:
+ class TheClass:
+ pass
+ a = TheClass.TheClass()
+ """)
+ root = createSourceNodeAt(src,"mymodule")
+ filename = os.path.abspath("mymodule.py")
+ defn = [x for x in findAllPossibleDefinitionsByCoords(filename,4,4)]
+ assert defn[0].filename == os.path.abspath("mymodule.py")
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 6
+ assert defn[0].confidence == 100
+
+
+ def test_findsClassDeclaredIn__init__Module(self):
+ importsrc=trimLines("""
+ class TheClass:
+ pass
+ """)
+ src=trimLines("""
+ from a import TheClass
+ c = TheClass()
+ """)
+
+
+
+ root = createSourceNodeAt(importsrc,"a.__init__")
+ root = createSourceNodeAt(src, "mymodule")
+ filename = os.path.abspath("mymodule.py")
+ defn = [x for x in findAllPossibleDefinitionsByCoords(filename,2,6)]
+ assert defn[0].filename == os.path.abspath(os.path.join("a",
+ "__init__.py"))
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 6
+ assert defn[0].confidence == 100
+
+
+class TestFindDefinitionUsingFiles(BRMTestCase):
+ def test_findsASimpleDefinitionUsingFiles(self):
+ src=trimLines("""
+ class TheClass:
+ pass
+ a = TheClass()
+ """)
+ writeTmpTestFile(src)
+ defn = [x for x in findAllPossibleDefinitionsByCoords(tmpfile,3,6)]
+ assert defn[0].filename == tmpfile
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 6
+ assert defn[0].confidence == 100
+
+
+ def test_findsDefinitionInAnotherModuleUsingFiles(self):
+ src=trimLines("""
+ from a.b.bah import TheClass
+ """)
+ classsrc=trimLines("""
+ class TheClass:
+ pass
+ """)
+ defn = self.helper(src, classsrc, 1, 21)
+ assert defn[0].filename == pkgstructureFile2
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 6
+ assert defn[0].confidence == 100
+
+
+
+ def test_findsDefinitionInAnotherRelativeModuleUsingFiles(self):
+ src=trimLines("""
+ from b.bah import TheClass
+ """)
+ classsrc=trimLines("""
+ class TheClass:
+ pass
+ """)
+ defn = self.helper(src, classsrc,1,21)
+ assert defn[0].filename == pkgstructureFile2
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 6
+ assert defn[0].confidence == 100
+
+ def test_findsMethodDefinitionInAnotherModuleUsingFiles(self):
+ src=trimLines("""
+ from b.bah import TheClass
+ a = TheClass()
+ a.theMethod()
+ """)
+ classsrc=trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ """)
+ defn = self.helper(src, classsrc, 3, 2)
+ assert defn[0].filename == pkgstructureFile2
+ assert defn[0].lineno == 2
+ assert defn[0].colno == 8
+ assert defn[0].confidence == 100
+
+ def test_findsDefinitonOfMethodWhenUseIsOnAMultiLine(self):
+ classsrc=trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ """)
+ src=trimLines("""
+ from b.bah import TheClass
+ a = TheClass()
+ i,j = (32,
+ a.theMethod()) # <--- find me!
+ something=somethingelse
+ """)
+ defn = self.helper(src, classsrc, 4, 9)
+ assert defn[0].filename == pkgstructureFile2
+ assert defn[0].lineno == 2
+ assert defn[0].colno == 8
+ assert defn[0].confidence == 100
+
+
+ def test_findsDefinitionWhenUseIsOnAMultilineAndNextLineBalancesBrace(self):
+ classsrc=trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ """)
+ src=trimLines("""
+ from b.bah import TheClass
+ c = TheClass()
+ f1, f2 = (c.func1,
+ c.theMethod)
+ f1, f2 = (c.func1,
+ c.theMethod)
+ """)
+ defn = self.helper(src, classsrc, 4, 10)
+ self.assertEqual(pkgstructureFile2,defn[0].filename)
+ self.assertEqual(2,defn[0].lineno)
+ self.assertEqual(8,defn[0].colno)
+ self.assertEqual(100,defn[0].confidence)
+
+ def test_worksIfFindingDefnOfRefInSlashMultiline(self):
+ classsrc=trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ """)
+ src=trimLines("""
+ from b.bah import TheClass
+ c = TheClass()
+ f1, f2 = c.func1 \\
+ ,c.theMethod
+ """)
+ defn = self.helper(src, classsrc, 4, 10)
+ self.assertEqual(pkgstructureFile2,defn[0].filename)
+ self.assertEqual(2,defn[0].lineno)
+ self.assertEqual(8,defn[0].colno)
+ self.assertEqual(100,defn[0].confidence)
+
+ def test_findsDefnInSameNonPackageDirectory(self):
+ try:
+ getRoot().pythonpath = [] # clear the python path
+ classsrc = trimLines("""
+ def testFunction():
+ print 'hello'
+ """)
+ src = trimLines("""
+ from baz import testFunction
+ """)
+ writeTmpTestFile(src)
+ newtmpfile = os.path.join(tmproot,"baz.py")
+ writeFile(newtmpfile, classsrc)
+ refs = [x for x in findAllPossibleDefinitionsByCoords(tmpfile,1,16)]
+ assert refs[0].filename == newtmpfile
+ assert refs[0].lineno == 1
+ finally:
+ os.remove(newtmpfile)
+ deleteTmpTestFile()
+
+
+ def test_findsDefnInPackageSubDirectoryAndRootNotInPath(self):
+ src=trimLines("""
+ from b.bah import TheClass
+ """)
+ classsrc=trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ """)
+ getRoot().pythonpath = [] # clear the python path
+ defn = self.helper(src, classsrc, 1, 18)
+ assert defn[0].filename == pkgstructureFile2
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 6
+ assert defn[0].confidence == 100
+
+ def test_findsDefnInSamePackageHierarchyAndRootNotInPath(self):
+ src=trimLines("""
+ from a.b.bah import TheClass
+ """)
+ classsrc=trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ """)
+ getRoot().pythonpath = [] # clear the python path
+ defn = self.helper(src, classsrc, 1, 20)
+ assert defn[0].filename == pkgstructureFile2
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 6
+ assert defn[0].confidence == 100
+
+ def helper(self, src, classsrc, line, col):
+ try:
+ createPackageStructure(src,classsrc)
+ filename = pkgstructureFile1
+ #Root(None,None,[pkgstructureRootDir])
+ defn = [x for x in findAllPossibleDefinitionsByCoords(filename,line,col)]
+ finally:
+ removePackageStructure()
+ return defn
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/test_findReferences.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,516 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+import os
+from bike import testdata
+from bike.testutils import *
+#from bike.testutils import trimLines, createSourceNodeAt, \
+# createSourceNodeAt_old, BRMTestCase
+from bike import testdata
+from findReferences import findReferences, findReferencesIncludingDefn
+from bike.query.getTypeOf import getTypeOf
+
+class helpers:
+ def helper(self,src,lineno,colno):
+ writeTmpTestFile(src)
+ refs = [x for x in findReferences(tmpfile,lineno,colno)]
+ return refs
+
+ def helper2(self,src,lineno,colno):
+ writeTmpTestFile(src)
+ refs = [x for x in findReferencesIncludingDefn(tmpfile,lineno,
+ colno)]
+ return refs
+
+ def helper3(self, src, importedsrc, line, col):
+ createPackageStructure(src,importedsrc)
+ filename = pkgstructureFile2
+ refs = [x for x in findReferences(filename,line,col)
+ if x.confidence == 100]
+ return refs
+
+ def helper4(self, src, importedsrc, line, col):
+ createPackageStructure(src,importedsrc)
+ filename = pkgstructureFile1
+ refs = [x for x in findReferences(filename,line,col)
+ if x.confidence == 100]
+ return refs
+
+
+
+class TestFindReferences(BRMTestCase,helpers):
+ def test_findsSimpleReferencesGivenAssignment(self):
+ src=trimLines("""
+ def foo():
+ a = 3
+ print a
+ """)
+ refs = self.helper(src,3,10)
+ assert refs[0].filename == tmpfile
+ assert refs[0].lineno == 3
+ assert refs[0].colno == 10
+ assert refs[0].confidence == 100
+
+
+
+ def test_findsSimpleReferencesGivenReference(self):
+ src=trimLines("""
+ def foo():
+ a = 3
+ print a
+ """)
+
+ refs = self.helper2(src,3,10)
+ assert refs[0].filename == tmpfile
+ assert refs[0].lineno == 2
+ assert refs[0].colno == 4
+ assert refs[0].confidence == 100
+
+
+ def test_findsReferencesToOtherAssignments(self):
+ src=trimLines("""
+ def foo():
+ a = 3
+ a = 4
+ """)
+ refs = self.helper(src,2,4)
+ assert refs[0].filename == tmpfile
+ assert refs[0].lineno == 3
+ assert refs[0].colno == 4
+ assert refs[0].confidence == 100
+
+ def test_findsFunctionArg(self):
+ src=trimLines("""
+ def foo(a):
+ print a
+ """)
+ refs = self.helper2(src,2,10)
+ assert refs[0].filename == tmpfile
+ assert refs[0].lineno == 1
+ assert refs[0].colno == 8
+ assert refs[0].confidence == 100
+
+ def test_findsFunctionArgWithDefault(self):
+ src=trimLines("""
+ def foo(a=None, b=None):
+ print a, b
+ """)
+ refs = self.helper(src,1,4)
+ self.assertEquals(refs, [])
+
+ def test_findsFunctionArgWithDefault2(self):
+ src=trimLines("""
+ def foo(a=None, b=None):
+ print a, b
+ """)
+ refs = self.helper2(src,2,13)
+ assert refs[0].filename == tmpfile
+ assert refs[0].lineno == 1
+ assert refs[0].colno == 16
+ assert refs[0].confidence == 100
+
+
+ def test_findsReferencesGivenFunctionArg(self):
+ src=trimLines("""
+ def foo(a):
+ print a
+ """)
+ refs = self.helper(src,1,8)
+ assert refs[0].filename == tmpfile
+ assert refs[0].lineno == 2
+ assert refs[0].colno == 10
+ assert refs[0].confidence == 100
+
+
+ def test_findsVariableRefInImportStatementUsingFromImportStatement(self):
+ importsrc=trimLines("""
+ from a.b.bah import mytext
+ """)
+ src=trimLines("""
+ mytext = 'hello'
+ """)
+ refs = self.helper3(importsrc,src,1,1)
+ assert refs[0].filename == pkgstructureFile1
+ assert refs[0].lineno == 1
+ assert refs[0].colno == 20
+ assert refs[0].confidence == 100
+
+
+
+ def test_findsVariableRefUsingFromImportStatement(self):
+ importsrc=trimLines("""
+ from a.b.bah import mytext
+ print mytext
+ """)
+ src=trimLines("""
+ mytext = 'hello'
+ """)
+ refs = self.helper3(importsrc,src,1,1)
+ assert refs[0].filename == pkgstructureFile1
+ assert refs[1].lineno == 2
+ assert refs[1].colno == 6
+ assert refs[1].confidence == 100
+
+ def test_findsImportedVariableRefInAFunctionArg(self):
+ importsrc=trimLines("""
+ from a.b import bah
+ someFunction(bah.mytext)
+ """)
+ src=trimLines("""
+ mytext = 'hello'
+ """)
+ refs = self.helper3(importsrc,src,1,1)
+ assert refs[0].filename == pkgstructureFile1
+ assert refs[0].lineno == 2
+ assert refs[0].colno == 17
+ assert refs[0].confidence == 100
+
+ def test_getsReferenceOfSimpleMethodCall(self):
+ src = trimLines("""
+ from b.bah import TheClass
+ a = TheClass()
+ a.theMethod()
+ """)
+
+ refs = self.helper4(src,testdata.TheClass,3,2)
+ assert refs[0].filename == pkgstructureFile1
+ self.assertEqual(refs[0].lineno,3)
+ self.assertEqual(refs[0].colno,2)
+
+
+ def test_findsRefToSelfAttribute(self):
+ src=trimLines("""
+ class MyClass:
+ def __init__(self):
+ self.a = 'hello'
+ def myMethod(self):
+ print self.a
+ """)
+ refs = self.helper(src,3,12)
+ assert refs[0].filename == tmpfile
+ assert refs[0].lineno == 5
+ assert refs[0].colno == 18
+ assert refs[0].confidence == 100
+
+
+class FindReferencesToMethod(BRMTestCase,helpers):
+ def test_findsReferenceOfSimpleMethodCall(self):
+ src = trimLines("""
+ from b.bah import TheClass
+ a = TheClass()
+ a.theMethod()
+ """)
+ refs = self.helper3(src,testClass,2,8)
+ assert refs[0].filename == pkgstructureFile1
+ self.assertEqual(refs[0].lineno,3)
+ self.assertEqual(refs[0].colno,2)
+ self.assertEqual(refs[0].colno,2)
+
+ def test_getsReferenceOfMethodCallFromClassImportedWithAlias(self):
+ src = trimLines("""
+ from b.bah import TheClass as MyTheClass
+
+ def foo():
+ a = MyTheClass()
+ a.theMethod()
+ """)
+ refs = self.helper3(src,testClass,2,8)
+ assert refs[0].filename == pkgstructureFile1
+ self.assertEqual(refs[0].lineno,5)
+ self.assertEqual(refs[0].colno,6)
+
+
+ def test_getsReferenceOfMethodCallWhenInstanceReturnedByFunction(self):
+ src = trimLines("""
+ from b.bah import TheClass
+
+ def foo():
+ return TheClass()
+ a = foo()
+ a.theMethod()
+ """)
+ refs = self.helper3(src,testClass,2,8)
+ assert refs[0].filename == pkgstructureFile1
+ self.assertEqual(refs[0].lineno,6)
+ self.assertEqual(refs[0].colno,2)
+
+ def test_getsReferenceOfMethodCallInSameClass(self):
+ src = trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ def anotherMethod(self):
+ self.theMethod()
+ """)
+ refs = self.helper4(src,testClass,2,8)
+ assert refs[0].filename == pkgstructureFile1
+ self.assertEqual(refs[0].lineno,5)
+ self.assertEqual(refs[0].colno,13)
+
+ def test_getsReferenceOfMethodOnBaseClassInstance(self):
+ src = trimLines("""
+ class root:
+ def theMethod(self):
+ pass
+
+ class a(root):
+ def theMethod(self):
+ pass
+
+ class b(root):
+ pass
+
+ class TheClass(b):
+ def theMethod(self):
+ pass
+
+ rootinstance = root()
+ rootinstance.theMethod()
+ """)
+ refs = self.helper4(src,"pass",2,8)
+ self.assertEqual(refs[2].filename,pkgstructureFile1)
+ self.assertEqual(refs[2].lineno,17)
+ self.assertEqual(refs[2].colno,13)
+
+ def test_doesntGetReferenceToMethodWhenObjectCreatedInChildScopeToMethodReference(self):
+ src = trimLines("""
+ from b.bah import TheClass
+ a = AnotherClass()
+ def foo():
+ a = TheClass()
+ a.theMethod()
+ """)
+ refs = self.helper3(src,testClass,2,8)
+ assert refs == []
+
+ def test_renamesMethodReferenceOfInstanceCreatedInSubsequentFunction(self):
+ src = trimLines("""
+ class TheClass:
+ def theMethod():
+ pass
+ class NotTheClass:
+ def theMethod():
+ pass
+
+ def foo():
+ a = bah()
+ a.theMethod()
+
+ def bah():
+ return TheClass()
+ """)
+ refs = self.helper4(src,"pass",2,8)
+ self.assertEqual(refs[0].filename,pkgstructureFile1)
+ self.assertEqual(refs[0].lineno,10)
+ self.assertEqual(refs[0].colno,6)
+
+
+ def test_getsReferenceInMiddleOfBiggerCompoundCall(self):
+ src = trimLines("""
+ class TheClass:
+ def theMethod(self): return AnotherClass()
+ TheClass().theMethod().anotherMethod()
+ """)
+
+ refs = self.helper4(src,"pass",2,8)
+ self.assertEqual(refs[0].filename,pkgstructureFile1)
+ self.assertEqual(refs[0].lineno,3)
+ self.assertEqual(refs[0].colno,11)
+ self.assertEqual(refs[0].colend,20)
+
+ def test_doesntBarfWhenObjectIsArrayMember(self):
+ src = trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ a[0] = TheClass()
+ a[0].theMethod()
+ """)
+ refs = self.helper4(src,"pass",2,8)
+ # should get to here without exception
+
+class FindReferencesToClass(BRMTestCase, helpers):
+ def test_returnsEmptyListIfNoReferences(self):
+ src = trimLines("""
+ class MyClass:
+ pass
+ a = TheClass()
+ """)
+ refs = self.helper4(src,"pass",1,6)
+ assert refs == []
+
+
+ def test_findsSimpleReferenceInSameModule(self):
+ src = trimLines("""
+ class TheClass:
+ pass
+ a = TheClass()
+ """)
+ refs = self.helper4(src,"pass",1,6)
+ self.assertEqual(refs[0].filename,pkgstructureFile1)
+ self.assertEqual(refs[0].lineno,3)
+ self.assertEqual(refs[0].colno,4)
+ self.assertEqual(refs[0].confidence,100)
+
+ def test_doesntBarfOnSingleLineSourceWithInlineClass(self):
+ src=trimLines("""
+ from b.bah import TheClass
+ a = TheClass()
+ """)
+ refs = self.helper3(src,"class TheClass: pass",1,6)
+ assert refs != []
+
+ def test_findsReferenceToClassImportedInSameClassScope(self):
+ src=trimLines("""
+ class AnotherClass:
+ from b.bah import TheClass
+ TheClass.baz = 0
+ """)
+ refs = self.helper3(src,"class TheClass: pass",1,6)
+ self.assertEqual(refs[0].filename,pkgstructureFile1)
+ self.assertEqual(refs[0].lineno,2)
+ self.assertEqual(refs[0].colno,22)
+
+ self.assertEqual(refs[0].filename,pkgstructureFile1)
+ self.assertEqual(refs[1].lineno,3)
+ self.assertEqual(refs[1].colno,4)
+
+ def testFindsClassReferenceWhenScopeIsSameNameAsClass(self):
+ src = trimLines("""
+ class TheClass:
+ class TheClass:
+ pass
+ a = TheClass.TheClass()
+ """)
+ refs = self.helper4(src,"pass",2,10)
+ self.assertEqual(refs[0].filename,pkgstructureFile1)
+ self.assertEqual(refs[0].lineno,4)
+ self.assertEqual(refs[0].colno,13)
+ self.assertEqual(refs[0].confidence,100)
+
+ def testFindsClassReferenceWhenChildIsSameNameAsClass(self):
+ src = trimLines("""
+ class TheClass:
+ class TheClass:
+ pass
+ a = TheClass.TheClass()
+ """)
+ refs = self.helper4(src,"pass",1,6)
+ self.assertEqual(refs[0].filename,pkgstructureFile1)
+ self.assertEqual(refs[0].lineno,4)
+ self.assertEqual(refs[0].colno,4)
+ self.assertEqual(refs[0].confidence,100)
+
+
+class TestFindReferencesIncludingDefn(BRMTestCase,helpers):
+ def test_findsMethodDecl(self):
+ src=trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ """)
+ refs = self.helper2(src,2,8)
+ self.assertEqual(refs[0].filename,tmpfile)
+ self.assertEqual(refs[0].lineno,2)
+ self.assertEqual(refs[0].colno,8)
+ self.assertEqual(refs[0].confidence,100)
+
+
+
+class TestFindReferencesUsingFiles(BRMTestCase):
+ def test_findsSimpleReferencesUsingFiles(self):
+ src=trimLines("""
+ def foo():
+ a = 3
+ print a
+ """)
+ refs = self.helper("pass",src,2,4)
+ assert refs[0].filename == pkgstructureFile2
+ assert refs[0].lineno == 3
+ assert refs[0].colno == 10
+ assert refs[0].confidence == 100
+
+ def test_findsReferenceInModuleWhichImportsClassWithFromAndAlias(self):
+ src = trimLines("""
+ from b.bah import TheClass as MyTheClass
+ def foo():
+ a = MyTheClass()
+ """)
+ refs = self.helper(src,testClass,1,6)
+ self.assertEqual(refs[0].filename,pkgstructureFile1)
+ self.assertEqual(refs[0].lineno,1)
+ self.assertEqual(refs[0].colno,18)
+ self.assertEqual(refs[0].confidence,100)
+
+
+ def test_doesntBarfWhenCantLocatePackageWhenTryingToFindBaseClass(self):
+ src = trimLines("""
+ from doesntexist import baseclass
+ class foo(baseclass):
+ def myMethod(self):
+ pass
+ """)
+ refs = self.helper("",src,3,8)
+
+ def test_doesntBarfWhenComesAcrossAPrintNl(self):
+ src = trimLines("""
+ class TheClass:
+ pass
+
+ print >>foo, TheClass
+ """)
+ refs = self.helper("",src,1,6)
+
+
+ def test_returnsOtherFilesInSameNonPackageDirectory(self):
+ try:
+ getRoot().pythonpath = [] # clear the python path
+ classsrc = trimLines("""
+ def testFunction():
+ print 'hello'
+ """)
+ src = trimLines("""
+ from baz import testFunction
+ """)
+ writeTmpTestFile(src)
+ newtmpfile = os.path.join(tmproot,"baz.py")
+ writeFile(newtmpfile, classsrc)
+ refs = [x for x in findReferences(newtmpfile,1,4)]
+
+ assert refs[0].filename == tmpfile
+ assert refs[0].lineno == 1
+ finally:
+ os.remove(newtmpfile)
+ deleteTmpTestFile()
+
+
+
+
+ def helper(self, src, classsrc, line, col):
+ try:
+ createPackageStructure(src,classsrc)
+ filename = pkgstructureFile2
+ refs = [x for x in findReferences(filename,line,col)]
+ finally:
+ removePackageStructure()
+ return refs
+
+
+
+
+testClass = trimLines("""
+class TheClass:
+ def theMethod(self):
+ pass
+ def differentMethod(self):
+ pass
+
+class DifferentClass:
+ def theMethod(self):
+ pass
+""")
+
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/test_getPackageDependencies.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+import os
+from bike import testdata
+from bike.testutils import *
+
+class TestGetPackageDependencies(BRMTestCase):
+ def test_foo(self):
+ assert 0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/test_getReferencesToClass.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,280 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+import os
+from bike import testdata
+from bike.testutils import *
+from bike.query.findReferences import findReferences
+from bike.parsing.fastparserast import Module
+
+class TestGetReferencesToClass(BRMTestCase):
+ def test_returnsEmptyListIfNoReferences(self):
+ src = trimLines("""
+ class MyClass:
+ pass
+ a = TheClass()
+ """)
+ root = createSourceNodeAt(src,"mymodule")
+ refs = [x for x in findReferences(os.path.abspath("mymodule.py"),1,6)]
+ self.assertEqual(refs,[])
+
+ def test_findsSimpleReferenceInSameModule(self):
+ src = trimLines("""
+ class TheClass:
+ pass
+ a = TheClass()
+ """)
+ root = createSourceNodeAt(src,"mymodule")
+ refs = [x for x in findReferences(os.path.abspath("mymodule.py"),1,6)]
+ self.assertEqual(refs[0].filename,os.path.abspath("mymodule.py"))
+ self.assertEqual(refs[0].lineno,3)
+ self.assertEqual(refs[0].colno,4)
+ self.assertEqual(refs[0].confidence,100)
+
+ def test_findsReferencesInModuleWhichImportsClass(self):
+ src = trimLines("""
+ import b.bah
+ def foo():
+ a = b.bah.TheClass()
+ a.theMethod()
+ """)
+ root = createSourceNodeAt(src, "a.foo")
+ root = createSourceNodeAt(ClassTestdata, "a.b.bah")
+ refs = [x for x in findReferences(os.path.abspath("a/b/bah.py"),1,6)]
+
+ self.assertEqual(refs[0].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,3)
+ self.assertEqual(refs[0].colno,14)
+ self.assertEqual(refs[0].confidence,100)
+
+
+ def test_findsReferenceInModuleWhichImportsClassWithFrom(self):
+ src = trimLines("""
+ from b.bah import TheClass
+ def foo():
+ a = TheClass()
+ a.theMethod()
+ """)
+
+ root = createSourceNodeAt(src, "a.foo")
+ root = createSourceNodeAt(ClassTestdata, "a.b.bah")
+
+ filename = os.path.abspath("a/b/bah.py")
+ refs = [x for x in findReferences(filename,1,6)]
+
+ self.assertEqual(refs[0].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,1)
+ self.assertEqual(refs[0].colno,18)
+ self.assertEqual(refs[0].confidence,100)
+
+ self.assertEqual(refs[1].filename,os.path.abspath(os.path.join("a/foo.py")))
+ self.assertEqual(refs[1].lineno,3)
+ self.assertEqual(refs[1].colno,8)
+ self.assertEqual(refs[1].confidence,100)
+
+ def test_findsReferenceToClassImportedInSameClassScope(self):
+ src=trimLines("""
+ class AnotherClass:
+ from b.bah import TheClass
+ TheClass.baz = 0
+ """)
+
+ root = createSourceNodeAt(src, "a.foo")
+ root = createSourceNodeAt(ClassTestdata, "a.b.bah")
+
+ filename = os.path.abspath("a/b/bah.py")
+ refs = [x for x in findReferences(filename,1,6)]
+ assert refs != []
+
+ def test_findsReferenceInModuleWhichImportsClassWithFromAndAlias(self):
+ src = trimLines("""
+ from b.bah import TheClass as MyTheClass
+ def foo():
+ a = MyTheClass()
+ a.theMethod()
+ """)
+
+ root = createSourceNodeAt(src, "a.foo")
+ root = createSourceNodeAt(ClassTestdata, "a.b.bah")
+ filename = os.path.abspath("a/b/bah.py")
+ refs = [x for x in findReferences(filename,1,6)]
+
+ self.assertEqual(refs[0].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,1)
+ self.assertEqual(refs[0].colno,18)
+ self.assertEqual(refs[0].confidence,100)
+
+
+ def test_findsReferenceInModuleWhichImportsClassWithImportAs(self):
+ src = trimLines("""
+ from b.bah import TheClass as MyTheClass
+ def foo():
+ a = MyTheClass()
+ a.theMethod()
+ """)
+
+ root = createSourceNodeAt(src, "a.foo")
+ root = createSourceNodeAt(ClassTestdata, "a.b.bah")
+ filename = os.path.abspath("a/b/bah.py")
+ refs = [x for x in findReferences(filename,1,6)]
+ self.assertEqual(refs[0].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,1)
+ self.assertEqual(refs[0].colno,18)
+ self.assertEqual(refs[0].confidence,100)
+
+ def test_findsReferenceInModuleWhichImportsClassWithFromImportStar(self):
+ src = trimLines("""
+ from b.bah import *
+ a = TheClass()
+ a.theMethod()
+ """)
+
+ root = createSourceNodeAt(src, "a.foo")
+ root = createSourceNodeAt(ClassTestdata, "a.b.bah")
+ filename = os.path.abspath("a/b/bah.py")
+ refs = [x for x in findReferences(filename,1,6)]
+ self.assertEqual(refs[0].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,2)
+ self.assertEqual(refs[0].colno,4)
+ self.assertEqual(refs[0].confidence,100)
+
+ def test_findsReferenceInModuleWhichImportsClassWithFromImportStar2(self):
+ src = trimLines("""
+ from a.b.bah import *
+ a = TheClass()
+ """)
+
+ root = createSourceNodeAt(src, "a.foo")
+ root = createSourceNodeAt(ClassTestdata, "a.b.bah")
+ filename = os.path.abspath("a/b/bah.py")
+ refs = [x for x in findReferences(filename,1,6)]
+ self.assertEqual(refs[0].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,2)
+ self.assertEqual(refs[0].colno,4)
+ self.assertEqual(refs[0].confidence,100)
+
+
+ def test_findsClassReferenceInInstanceCreation(self):
+ src = trimLines("""
+ class TheClass:
+ def theMethod(self): pass
+ TheClass().theMethod()
+ """)
+ root = createSourceNodeAt(src, "a.foo")
+ filename = os.path.abspath("a/foo.py")
+ refs = [x for x in findReferences(filename,1,6)]
+ self.assertEqual(refs[0].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,3)
+ self.assertEqual(refs[0].colno,0)
+ self.assertEqual(refs[0].confidence,100)
+
+
+ def test_findsClassReferenceInInstanceCreationWithFQN(self):
+ src = trimLines("""
+ import b.bah
+ def foo():
+ a = b.bah.TheClass()
+ a.theMethod()
+ """)
+
+ root = createSourceNodeAt(src, "a.foo")
+ root = createSourceNodeAt(ClassTestdata, "a.b.bah")
+ filename = os.path.abspath("a/b/bah.py")
+ refs = [x for x in findReferences(filename,1,6)]
+
+ self.assertEqual(refs[0].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,3)
+ self.assertEqual(refs[0].colno,14)
+ self.assertEqual(refs[0].confidence,100)
+
+ def test_doesntfindReferenceInModuleWhichDoesntImportClass(self):
+ src = trimLines("""
+ a = TheClass()
+ """)
+
+ root = createSourceNodeAt(src, "a.foo")
+ root = createSourceNodeAt(ClassTestdata, "a.b.bah")
+ filename = os.path.abspath("a/b/bah.py")
+ refs = [x for x in findReferences(filename,1,6)]
+ assert refs == []
+
+ def test_findsReferenceInClassBases(self):
+ src =trimLines("""
+ from b.bah import TheClass
+ class DerivedClass(TheClass):
+ pass
+ """)
+
+ root = createSourceNodeAt(src, "a.foo")
+ root = createSourceNodeAt(ClassTestdata, "a.b.bah")
+ filename = os.path.abspath("a/b/bah.py")
+ refs = [x for x in findReferences(filename,1,6)]
+ self.assertEqual(refs[1].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[1].lineno,2)
+ self.assertEqual(refs[1].colno,19)
+ self.assertEqual(refs[1].confidence,100)
+
+
+
+ def test_findsReferenceInMultiLineImportStatement(self):
+ src =trimLines("""
+ from b.bah import foo, \\
+ TheFooBah, TheClass, Foobah, SomethingElse
+ """)
+
+ root = createSourceNodeAt(src, "a.foo")
+ root = createSourceNodeAt(ClassTestdata, "a.b.bah")
+ filename = os.path.abspath("a/b/bah.py")
+ refs = [x for x in findReferences(filename,1,6)]
+ self.assertEqual(refs[0].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,2)
+ self.assertEqual(refs[0].colno,21)
+ self.assertEqual(refs[0].confidence,100)
+
+ def test_findsReferenceWhenModulenameSameAsClassMethodName(self):
+ # asserts that brm doesnt search class scope after not finding name
+ # in method scope (since class scope is invisible unless called on 'self'
+ src =trimLines("""
+ from a.b import bah
+ class baz:
+ def bah(self):
+ print bah.TheClass
+ """)
+
+ root = createSourceNodeAt(src, "a.foo")
+ root = createSourceNodeAt(ClassTestdata, "a.b.bah")
+ filename = os.path.abspath("a/b/bah.py")
+ refs = [x for x in findReferences(filename,1,6)]
+ self.assertEqual(refs[0].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,4)
+ self.assertEqual(refs[0].colno,18)
+ self.assertEqual(refs[0].confidence,100)
+
+
+ def test_doesntBarfOnFromImportStarWhenNameIsInFromClause(self):
+ src = trimLines("""
+ from a.b.bah import TheClass
+ a = TheClass()
+ """)
+
+ root = createSourceNodeAt(src, "a.foo")
+ root = createSourceNodeAt(ClassTestdata, "a.b.bah")
+ filename = os.path.abspath("a/b/bah.py")
+ refs = [x for x in findReferences(filename,1,6)]
+
+
+ClassTestdata = trimLines("""
+class TheClass:
+ def theMethod(self):
+ pass
+ def differentMethod(self):
+ pass
+
+class DifferentClass:
+ def theMethod(self):
+ pass
+""")
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/test_getReferencesToMethod.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,194 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+import os
+from bike import testdata
+from bike.testutils import *
+from bike.query.findReferences import findReferences
+from bike.parsing.fastparserast import Module
+
+class TestGetReferencesToMethod(BRMTestCase):
+
+ def test_getsReferenceOfSimpleMethodCall(self):
+ src = trimLines("""
+ from b.bah import TheClass
+ a = TheClass()
+ a.theMethod()
+ """)
+ root = createSourceNodeAt(src,"a.foo")
+ root = createSourceNodeAt(MethodTestdata, "a.b.bah")
+ filename = os.path.abspath("a/b/bah.py")
+ refs = [x for x in findReferences(filename,2,8)
+ if x.confidence == 100]
+ self.assertEqual(refs[0].filename,
+ os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,3)
+ self.assertEqual(refs[0].colno,2)
+ self.assertEqual(refs[0].colno,2)
+
+ def test_getsReferenceOfMethodCallFromClassImportedWithAlias(self):
+ src = trimLines("""
+ from b.bah import TheClass as MyTheClass
+
+ def foo():
+ a = MyTheClass()
+ a.theMethod()
+ """)
+ root = createSourceNodeAt(src,"a.foo")
+ root = createSourceNodeAt(MethodTestdata, "a.b.bah")
+ filename = os.path.abspath("a/b/bah.py")
+ refs = [x for x in findReferences(filename,2,8)
+ if x.confidence == 100]
+ self.assertEqual(refs[0].filename,
+ os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,5)
+ self.assertEqual(refs[0].colno,6)
+
+
+ def test_getsReferenceOfMethodCallWhenInstanceReturnedByFunction(self):
+ src = trimLines("""
+ from b.bah import TheClass
+
+ def foo():
+ return TheClass()
+ a = foo()
+ a.theMethod()
+ """)
+ root = createSourceNodeAt(src,"a.foo")
+ root = createSourceNodeAt(MethodTestdata, "a.b.bah")
+ filename = os.path.abspath("a/b/bah.py")
+ refs = [x for x in findReferences(filename,2,8)
+ if x.confidence == 100]
+ self.assertEqual(refs[0].filename,
+ os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,6)
+ self.assertEqual(refs[0].colno,2)
+
+ def test_getsReferenceOfMethodCallInSameClass(self):
+ src = trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ def anotherMethod(self):
+ self.theMethod()
+ """)
+
+ root = createSourceNodeAt(src,"a.foo")
+ filename = os.path.abspath("a/foo.py")
+ refs = [x for x in findReferences(filename,2,8)
+ if x.confidence == 100]
+ self.assertEqual(refs[0].filename,
+ os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,5)
+ self.assertEqual(refs[0].colno,13)
+
+ def test_getsReferenceOfMethodOnBaseClassInstance(self):
+ src = trimLines("""
+ class root:
+ def theMethod():
+ pass
+
+ class a(root):
+ def theMethod():
+ pass
+
+ class b(root):
+ pass
+
+ class TheClass(b):
+ def theMethod(self):
+ pass
+
+ rootinstance = root()
+ rootinstance.theMethod()
+ """)
+
+ refs =self.helper4(src,"pass",2,8)
+ self.assertEqual(refs[2].filename,pkgstructureFile1)
+ self.assertEqual(refs[2].lineno,17)
+ self.assertEqual(refs[2].colno,13)
+
+ def helper4(self, src, importedsrc, line, col):
+ try:
+ createPackageStructure(src,importedsrc)
+ filename = pkgstructureFile1
+ refs = [x for x in findReferences(filename,line,col)
+ if x.confidence == 100]
+ finally:
+ removePackageStructure()
+ return refs
+
+ def test_doesntGetReferenceToMethodWhenObjectCreatedInChildScopeToMethodReference(self):
+ src = trimLines("""
+ from b.bah import TheClass
+ a = AnotherClass()
+ def foo():
+ a = TheClass()
+ a.theMethod()
+ """)
+ root = createSourceNodeAt(src,"a.foo")
+ root = createSourceNodeAt(MethodTestdata, "a.b.bah")
+ filename = os.path.abspath("a/b/bah.py")
+ refs = [x for x in findReferences(filename,2,8)
+ if x.confidence == 100]
+ assert len(refs) == 0
+
+ def test_renamesMethodReferenceOfInstanceCreatedInSubsequentFunction(self):
+ src = trimLines("""
+ class TheClass:
+ def theMethod():
+ pass
+ class NotTheClass:
+ def theMethod():
+ pass
+
+ def foo():
+ a = bah()
+ a.theMethod()
+
+ def bah():
+ return TheClass()
+ """)
+ root = createSourceNodeAt(src,"a.foo")
+ filename = os.path.abspath("a/foo.py")
+ refs = [x for x in findReferences(filename,2,8)
+ if x.confidence == 100]
+ self.assertEqual(refs[0].filename,
+ os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,10)
+ self.assertEqual(refs[0].colno,6)
+
+
+ def test_getsReferenceInMiddleOfBiggerCompoundCall(self):
+ src = trimLines("""
+ class TheClass:
+ def theMethod(self): return AnotherClass()
+ TheClass().theMethod().anotherMethod()
+ """)
+
+ root = createSourceNodeAt(src,"a.foo")
+ filename = os.path.abspath("a/foo.py")
+ refs = [x for x in findReferences(filename,2,8)
+ if x.confidence == 100]
+ self.assertEqual(refs[0].filename,
+ os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,3)
+ self.assertEqual(refs[0].colno,11)
+ self.assertEqual(refs[0].colend,20)
+
+
+MethodTestdata = trimLines("""
+class TheClass:
+ def theMethod(self):
+ pass
+ def differentMethod(self):
+ pass
+
+class DifferentClass:
+ def theMethod(self):
+ pass
+""")
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/test_getReferencesToModule.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,179 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+import os
+from bike import testdata
+from bike.testutils import *
+from bike.query.getReferencesToModule import *
+from bike.parsing.fastparserast import Module
+
+class TestGetReferencesToModule(BRMTestCase):
+
+ def test_returnsEmptyListIfNoReferences(self):
+ src = trimLines("""
+ class MyClass:
+ pass
+ a = TheClass()
+ """)
+ root = createSourceNodeAt(src,"mymodule")
+ self.assertEqual([x for x in getReferencesToModule(root,"myothermodule")],[])
+
+ def test_findsReferencesInModuleWhichImportsModule(self):
+ src = trimLines("""
+ import b.bah
+ def foo():
+ a = b.bah.TheClass()
+ a.theMethod()
+ """)
+
+ root = createSourceNodeAt( src, "a.foo")
+ root = createSourceNodeAt( testdata.TheClass, "a.b.bah")
+ refs = [x for x in getReferencesToModule(root,"a.b.bah")]
+
+
+ self.assertEqual(refs[0].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,1)
+ self.assertEqual(refs[0].colno,9)
+ self.assertEqual(refs[0].confidence,100)
+
+ self.assertEqual(refs[1].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[1].lineno,3)
+ self.assertEqual(refs[1].colno,10)
+ self.assertEqual(refs[0].confidence,100)
+
+ def test_findsReferenceInModuleWhichImportsModuleWithFrom(self):
+ src = trimLines("""
+ from b import bah
+ def foo():
+ a = bah.TheClass()
+ a.theMethod()
+ """)
+
+ root = createSourceNodeAt( src, "a.foo")
+ root = createSourceNodeAt( testdata.TheClass, "a.b.bah")
+ refs = [x for x in getReferencesToModule(root,"a.b.bah")]
+
+ self.assertEqual(refs[0].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,1)
+ self.assertEqual(refs[0].colno,14)
+ self.assertEqual(refs[0].confidence,100)
+
+ self.assertEqual(refs[1].filename,os.path.abspath(os.path.join("a/foo.py")))
+ self.assertEqual(refs[1].lineno,3)
+ self.assertEqual(refs[1].colno,8)
+ self.assertEqual(refs[0].confidence,100)
+
+ def test_findsReferenceInModuleWhichImportsModuleWithFromAndAlias(self):
+ src = trimLines("""
+ from b import bah as mymodule
+ def foo():
+ a = mymodule.MyTheClass()
+ a.theMethod()
+ """)
+
+
+ root = createSourceNodeAt( src, "a.foo")
+ root = createSourceNodeAt( testdata.TheClass, "a.b.bah")
+ refs = [x for x in getReferencesToModule(root,"a.b.bah")]
+
+ self.assertEqual(refs[0].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,1)
+ self.assertEqual(refs[0].colno,14)
+ self.assertEqual(refs[0].confidence,100)
+
+ """ # mymodule.MyTheClass
+ self.assertEqual(refs[1].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[1].lineno,3)
+ self.assertEqual(refs[1].colno,10)
+ self.assertEqual(refs[1].confidence,100)
+ """
+
+ def test_findsReferenceInModuleWhichImportsModuleWithFromImportStar(self):
+ src = trimLines("""
+ from b.bah import *
+ a = TheClass()
+ a.theMethod()
+ """)
+
+ root = createSourceNodeAt( src, "a.foo")
+ root = createSourceNodeAt( testdata.TheClass, "a.b.bah")
+ refs = [x for x in getReferencesToModule(root,"a.b.bah")]
+
+ self.assertEqual(refs[0].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,1)
+ self.assertEqual(refs[0].colno,7)
+ self.assertEqual(refs[0].confidence,100)
+
+ ''' Dont think this is a valid test, since cant import a module with
+ from package import *
+ def test_findsReferenceInModuleWhichImportsClassWithFromImportStar2(self):
+ src = trimLines("""
+ from a.b import *
+ a = bah.TheClass()
+ """)
+
+ root = createSourceNodeAt( src, "a.foo")
+ root = createSourceNodeAt( testdata.TheClass, "a.b.bah")
+ refs = [x for x in getReferencesToModule(root,"a.b.bah")]
+
+ self.assertEqual(refs[0].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,2)
+ self.assertEqual(refs[0].colno,4)
+ self.assertEqual(refs[0].confidence,100)
+ '''
+
+ def test_findsReferenceInClassBases(self):
+ src =trimLines("""
+ from b import bah
+ class DerivedClass(bah.TheClass):
+ pass
+ """)
+
+ root = createSourceNodeAt(src, "a.foo")
+ root = createSourceNodeAt(testdata.TheClass, "a.b.bah")
+ refs = [x for x in getReferencesToModule(root,"a.b.bah")]
+
+ self.assertEqual(refs[1].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[1].lineno,2)
+ self.assertEqual(refs[1].colno,19)
+ self.assertEqual(refs[1].confidence,100)
+
+ def test_findsReferenceInMultiLineImportStatement(self):
+ src =trimLines("""
+ from b import foo, \\
+ TheFooBah, TheClass, TheBastard, SomethingElse, bah
+ """)
+
+ root = createSourceNodeAt( src, "a.foo")
+ root = createSourceNodeAt( testdata.TheClass, "a.b.bah")
+ refs = [x for x in getReferencesToModule(root,"a.b.bah")]
+ self.assertEqual(refs[0].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,2)
+ self.assertEqual(refs[0].colno,58)
+ self.assertEqual(refs[0].confidence,100)
+
+ def test_findsReferenceWhenModulenameSameAsClassMethodName(self):
+ # asserts that brm doesnt search class scope after not finding name
+ # in method scope (since class scope is invisible unless called on 'self'
+ src =trimLines("""
+ from a.b import bah
+ class baz:
+ def bah(self):
+ print bah.TheClass
+ """)
+
+ root = createSourceNodeAt( src, "a.foo")
+ root = createSourceNodeAt( testdata.TheClass, "a.b.bah")
+ refs = [x for x in getReferencesToModule(root,"a.b.bah")]
+
+ self.assertEqual(refs[0].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,1)
+ self.assertEqual(refs[0].colno,16)
+ self.assertEqual(refs[0].confidence,100)
+
+ assert (len(refs))==2
+
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/test_getTypeOf.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,191 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+import os
+from bike import testdata
+from bike.testutils import *
+from bike.query.getTypeOf import getTypeOf, UnfoundType,\
+ attemptToConvertGetattrToFqn
+from bike.parsing.fastparserast import Class, Function, Instance
+from bike.parsing.newstuff import getModuleOrPackageUsingFQN
+from compiler.ast import Getattr,CallFunc,Name
+
+class TestGetTypeOf(BRMTestCase):
+ def test_getsTypeOfSimpleClassInstanceReference(self):
+ src = trimLines("""
+ from b.bah import TheClass
+ a = TheClass()
+ a.theMethod()
+ """)
+ root = createSourceNodeAt(src,"a.foo")
+ root = createSourceNodeAt(testdata.TheClass, "a.b.bah")
+ module = getModuleOrPackageUsingFQN("a.foo")
+ res = getTypeOf(module,"a")
+ assert isinstance(res,Instance)
+ assert isinstance(res.getType(),Class)
+ assert res.getType().name == "TheClass"
+
+ def test_getsTypeOfImportedClassReference(self):
+ src = trimLines("""
+ import b.bah
+ a = b.bah.TheClass()
+ """)
+ root = createSourceNodeAt(src,"a.foo")
+ root = createSourceNodeAt(testdata.TheClass, "a.b.bah")
+ module = getModuleOrPackageUsingFQN("a.foo")
+ res = getTypeOf(module,"a")
+ assert isinstance(res,Instance)
+ assert isinstance(res.getType(),Class)
+ assert res.getType().name == "TheClass"
+
+ def test_getsTypeOfClassReferenceFromImportedPackage(self):
+ src = trimLines("""
+ import b
+ a = b.bah.TheClass()
+ """)
+ root = createSourceNodeAt(src,"a.foo")
+ root = createSourceNodeAt(testdata.TheClass, "a.b.bah")
+ module = getModuleOrPackageUsingFQN("a.foo")
+ res = getTypeOf(module,"a")
+ assert isinstance(res,Instance)
+ assert isinstance(res.getType(),Class)
+ assert res.getType().name == "TheClass"
+
+ def test_getsTypeOfInstanceThatIsAnAttributeOfSelf(self):
+ src = trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+
+ class AnotherClass:
+ def __init__(self):
+ self.a = TheClass()
+ def anotherFn(self):
+ self.a.theMethod()
+ """)
+ root = createSourceNodeAt(src,"a.foo")
+ module = getModuleOrPackageUsingFQN('a.foo')
+ theclass = getTypeOf(module,"TheClass")
+ fn = getTypeOf(module,"AnotherClass.anotherFn")
+ self.assertEqual(getTypeOf(fn,"self.a").getType().name, "TheClass")
+ #self.assertEqual(getTypeOf(fn,"self.a").getType(), theclass)
+
+
+
+ def test_doesntGetTypeDefinedInChildFunction(self):
+ src = trimLines("""
+ from b.bah import TheClass
+ a = AnotherClass()
+ def foo():
+ a = TheClass()
+ a.theMethod()
+ """)
+ root = createSourceNodeAt(src,"a.foo")
+ root = createSourceNodeAt(testdata.TheClass, "a.b.bah")
+
+ themodule = getModuleOrPackageUsingFQN("a.foo")
+ assert isinstance(getTypeOf(themodule,"a"),UnfoundType)
+
+
+ def test_getsTypeOfClassReferencedViaAlias(self):
+ src = trimLines("""
+ from b.bah import TheClass as FooBah
+ FooBah()
+ """)
+ root = createSourceNodeAt(src,"a.foo")
+ root = createSourceNodeAt(testdata.TheClass, "a.b.bah")
+ themodule = getModuleOrPackageUsingFQN("a.foo")
+ self.assertEqual(getTypeOf(themodule,"FooBah").name,"TheClass")
+ self.assertEqual(getTypeOf(themodule,"FooBah").filename,
+ os.path.abspath(os.path.join("a","b","bah.py")))
+
+
+ def test_getsTypeOfClassImportedFromPackageScope(self):
+ initfile = trimLines("""
+ from bah import TheClass
+ """)
+ src = trimLines("""
+ from a import b
+ b.TheClass()
+ """)
+ createSourceNodeAt(src,"a.foo")
+ createSourceNodeAt(testdata.TheClass, "a.b.bah")
+ createSourceNodeAt(initfile,"a.b.__init__")
+ themodule = getModuleOrPackageUsingFQN("a.foo")
+ self.assertEqual(getTypeOf(themodule,"b.TheClass").name,"TheClass")
+ self.assertEqual(getTypeOf(themodule,"b.TheClass").filename,
+ os.path.abspath(os.path.join("a","b","bah.py")))
+
+
+ def test_attemptToConvertGetattrToFqn_returnsNoneIfFails(self):
+ ast = Getattr(CallFunc(Name("foo"),[],[],[]),"hello")
+ assert attemptToConvertGetattrToFqn(ast) is None
+
+ def test_attemptToConvertGetattrToFqn_works(self):
+ ast = Getattr(Getattr(Name("foo"),"bah"),"hello")
+ assert attemptToConvertGetattrToFqn(ast) == "foo.bah.hello"
+
+
+ def test_handlesRecursionProblem(self):
+ src = trimLines("""
+ def fn(root):
+ node = root
+ node = node.getPackage('something')
+ """)
+ root = createSourceNodeAt(src,"a.foo")
+ m = getModuleOrPackageUsingFQN("a.foo")
+ fn = getTypeOf(m,"fn")
+ getTypeOf(fn,"node") # stack overflow!
+
+
+ def test_doesntGotIntoRecursiveLoopWhenEvaluatingARecursiveFunction(self):
+ src = trimLines("""
+ def fn(v):
+ if v < 45:
+ return fn(root+1)
+ val = fn(3)
+ """)
+ root = createSourceNodeAt(src,"a.foo")
+ mod = getModuleOrPackageUsingFQN("a.foo")
+ getTypeOf(mod,"val") # stack overflow!
+
+ def test_getsModuleImportedWithFrom(self):
+ importsrc=trimLines("""
+ from a.b import bah
+ """)
+ src=trimLines("""
+ mytext = 'hello'
+ """)
+ type = self.helper(importsrc,src,'bah')
+ self.assertEqual(pkgstructureFile2,type.filename)
+
+ def helper(self,importsrc,src,name):
+ try:
+ createPackageStructure(importsrc,src)
+ from bike.parsing.newstuff import getModule
+ scope = getModule(pkgstructureFile1)
+ return getTypeOf(scope,name)
+ finally:
+ removePackageStructure()
+
+
+ def test_getsTypeOfClassImportedAsAlias(self):
+ importsrc = trimLines("""
+ from b.bah import TheClass as MyTheClass
+
+ def foo():
+ a = MyTheClass()
+ a.theMethod()
+ """)
+ src=trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ """)
+ type = self.helper(importsrc,src,'MyTheClass')
+ self.assertEqual("TheClass",type.name)
+
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/test_relationships.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,94 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+import os
+from bike import testdata
+from bike.testutils import *
+from bike.query.getTypeOf import getTypeOf
+from bike.parsing.fastparserast import Module
+from bike.query.relationships import getRootClassesOfHierarchy
+from bike.parsing.newstuff import getModule
+
+
+class TestGetRootClassesOfHierarchy(BRMTestCase):
+ def test_getsRootClassFromDerivedClass(self):
+ src = trimLines("""
+ from b.bah import TheClass as BaseClass
+
+ class DerivedClass(BaseClass):
+ pass
+
+ """)
+ rootclasses = self.helper(src,"DerivedClass")
+ self.assertEqual("TheClass",rootclasses[0].name)
+ self.assertEqual(len(rootclasses),1)
+
+ def test_getsRootClassFromDerivedDerivedClass(self):
+ src = trimLines("""
+ from b.bah import TheClass as BaseClass
+
+ class DerivedClass(BaseClass):
+ pass
+ class DerivedDerivedClass(DerivedClass):
+ pass
+ """)
+ rootclasses = self.helper(src,"DerivedDerivedClass")
+ self.assertEqual("TheClass",rootclasses[0].name)
+ self.assertEqual(len(rootclasses),1)
+
+
+ def test_getsRootClassFromDiamondOfClasses(self):
+ src = trimLines("""
+ from b.bah import TheClass as BaseClass
+
+ class DerivedClass(BaseClass):
+ pass
+ class DerivedDerivedClass(DerivedClass,BaseClass):
+ pass
+ """)
+ rootclasses = self.helper(src,"DerivedDerivedClass")
+ self.assertEqual("TheClass",rootclasses[0].name)
+ self.assertEqual("TheClass",rootclasses[1].name)
+ self.assertEqual(len(rootclasses),2)
+
+
+ def test_getsRootClassesFromMultipleInheritance(self):
+ src = trimLines("""
+ from b.bah import TheClass as BaseClass
+
+ class DerivedClass:
+ pass
+ class DerivedDerivedClass(DerivedClass,BaseClass):
+ pass
+ """)
+ rootclasses = self.helper(src,"DerivedDerivedClass")
+ self.assertEqual("DerivedClass",rootclasses[0].name)
+ self.assertEqual("TheClass",rootclasses[1].name)
+ self.assertEqual(len(rootclasses),2)
+
+ def test_getsRootClassesFromMultipleInheritanceWithNewStyleClass(self):
+ src = trimLines("""
+ from b.bah import TheClass as BaseClass
+
+ class DerivedClass(Object):
+ pass
+ class DerivedDerivedClass(DerivedClass,BaseClass):
+ pass
+ """)
+ rootclasses = self.helper(src,"DerivedDerivedClass")
+ self.assertEqual("DerivedClass",rootclasses[0].name)
+ self.assertEqual("TheClass",rootclasses[1].name)
+ self.assertEqual(len(rootclasses),2)
+
+
+ def helper(self,src,classname):
+ try:
+ createPackageStructure(src,testdata.TheClass)
+ classobj = getTypeOf(getModule(pkgstructureFile1),classname)
+ return getRootClassesOfHierarchy(classobj)
+ finally:
+ removePackageStructure()
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/testall.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+
+import setpath
+
+from test_common import *
+from test_getReferencesToClass import *
+from test_getReferencesToMethod import *
+#from test_getReferencesToModule import *
+from test_findDefinition import *
+from test_findReferences import *
+from test_relationships import *
+from test_getTypeOf import *
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/refactor/__init__.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,1 @@
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/refactor/extractMethod.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,363 @@
+import re
+import compiler
+from bike.parsing import visitor
+from bike.query.common import getScopeForLine
+from bike.parsing.parserutils import generateLogicalLines, \
+ makeLineParseable, maskStringsAndRemoveComments
+from parser import ParserError
+from bike.parsing.fastparserast import Class
+from bike.transformer.undo import getUndoStack
+from bike.refactor.utils import getTabWidthOfLine, getLineSeperator, \
+ reverseCoordsIfWrongWayRound
+from bike.transformer.save import queueFileToSave
+from bike.parsing.load import getSourceNode
+TABSIZE = 4
+
+class coords:
+ def __init__(self, line, column):
+ self.column = column
+ self.line = line
+ def __str__(self):
+ return "("+str(self.column)+","+str(self.line)+")"
+
+commentRE = re.compile(r"#.*?$")
+
+class ParserException(Exception): pass
+
+def extractMethod(filename, startcoords, endcoords, newname):
+ ExtractMethod(getSourceNode(filename),
+ startcoords, endcoords, newname).execute()
+
+class ExtractMethod(object):
+ def __init__(self,sourcenode, startcoords, endcoords, newname):
+ self.sourcenode = sourcenode
+
+ startcoords, endcoords = \
+ reverseCoordsIfWrongWayRound(startcoords,endcoords)
+
+ self.startline = startcoords.line
+ self.endline = endcoords.line
+ self.startcol = startcoords.column
+ self.endcol= endcoords.column
+
+ self.newfn = NewFunction(newname)
+
+ self.getLineSeperator()
+ self.adjustStartColumnIfLessThanTabwidth()
+ self.adjustEndColumnIfStartsANewLine()
+ self.fn = self.getFunctionObject()
+ self.getRegionToBuffer()
+ #print "-"*80
+ #print self.extractedLines
+ #print "-"*80
+ self.deduceIfIsMethodOrFunction()
+
+ def execute(self):
+ self.deduceArguments()
+ getUndoStack().addSource(self.sourcenode.filename,
+ self.sourcenode.getSource())
+ srclines = self.sourcenode.getLines()
+ newFnInsertPosition = self.fn.getEndLine()-1
+ self.insertNewFunctionIntoSrcLines(srclines, self.newfn,
+ newFnInsertPosition)
+ self.writeCallToNewFunction(srclines)
+
+ src = "".join(srclines)
+ queueFileToSave(self.sourcenode.filename,src)
+
+ def getLineSeperator(self):
+ line = self.sourcenode.getLines()[self.startline-1]
+ linesep = getLineSeperator(line)
+ self.linesep = linesep
+
+ def adjustStartColumnIfLessThanTabwidth(self):
+ tabwidth = getTabWidthOfLine(self.sourcenode.getLines()[self.startline-1])
+ if self.startcol < tabwidth: self.startcol = tabwidth
+
+ def adjustEndColumnIfStartsANewLine(self):
+ if self.endcol == 0:
+ self.endline -=1
+ nlSize = len(self.linesep)
+ self.endcol = len(self.sourcenode.getLines()[self.endline-1])-nlSize
+
+
+ def getFunctionObject(self):
+ return getScopeForLine(self.sourcenode,self.startline)
+
+
+ def getTabwidthOfParentFunction(self):
+ line = self.sourcenode.getLines()[self.fn.getStartLine()-1]
+ match = re.match("\s+",line)
+ if match is None:
+ return 0
+ else:
+ return match.end(0)
+
+ # should be in the transformer module
+ def insertNewFunctionIntoSrcLines(self,srclines,newfn,insertpos):
+ tabwidth = self.getTabwidthOfParentFunction()
+
+ while re.match("\s*"+self.linesep,srclines[insertpos-1]):
+ insertpos -= 1
+
+ srclines.insert(insertpos, self.linesep)
+ insertpos +=1
+
+ fndefn = "def "+newfn.name+"("
+
+ if self.isAMethod:
+ fndefn += "self"
+ if newfn.args != []:
+ fndefn += ", "+", ".join(newfn.args)
+ else:
+ fndefn += ", ".join(newfn.args)
+
+ fndefn += "):"+self.linesep
+
+
+ srclines.insert(insertpos,tabwidth*" "+fndefn)
+ insertpos +=1
+
+ tabwidth += TABSIZE
+
+
+ if self.extractedCodeIsAnExpression(srclines):
+ assert len(self.extractedLines) == 1
+
+ fnbody = [tabwidth*" "+ "return "+self.extractedLines[0]]
+
+
+ else:
+ fnbody = [tabwidth*" "+line for line in self.extractedLines]
+ if newfn.retvals != []:
+ fnbody.append(tabwidth*" "+"return "+
+ ", ".join(newfn.retvals) + self.linesep)
+
+ for line in fnbody:
+ srclines.insert(insertpos,line)
+ insertpos +=1
+
+
+ def writeCallToNewFunction(self, srclines):
+ startline = self.startline
+ endline = self.endline
+ startcol = self.startcol
+ endcol= self.endcol
+
+ fncall = self.constructFunctionCallString(self.newfn.name, self.newfn.args,
+ self.newfn.retvals)
+
+ self.replaceCodeWithFunctionCall(srclines, fncall,
+ startline, endline, startcol, endcol)
+
+
+ def replaceCodeWithFunctionCall(self, srclines, fncall,
+ startline, endline, startcol, endcol):
+ if startline == endline: # i.e. extracted code part of existing line
+ line = srclines[startline-1]
+ srclines[startline-1] = self.replaceSectionOfLineWithFunctionCall(line,
+ startcol, endcol, fncall)
+ else:
+ self.replaceLinesWithFunctionCall(srclines, startline, endline, fncall)
+
+
+ def replaceLinesWithFunctionCall(self, srclines, startline, endline, fncall):
+ tabwidth = getTabWidthOfLine(srclines[startline-1])
+ line = tabwidth*" " + fncall + self.linesep
+ srclines[startline-1:endline] = [line]
+
+
+
+ def replaceSectionOfLineWithFunctionCall(self, line, startcol, endcol, fncall):
+ line = line[:startcol] + fncall + line[endcol:]
+ if not line.endswith(self.linesep):
+ line+=self.linesep
+ return line
+
+
+
+ def constructFunctionCallString(self, fnname, fnargs, retvals):
+ fncall = fnname + "("+", ".join(fnargs)+")"
+ if self.isAMethod:
+ fncall = "self." + fncall
+
+ if retvals != []:
+ fncall = ", ".join(retvals) + " = "+fncall
+ return fncall
+
+
+ def deduceArguments(self):
+ lines = self.fn.getLinesNotIncludingThoseBelongingToChildScopes()
+
+ # strip off comments
+ lines = [commentRE.sub(self.linesep,line) for line in lines]
+ extractedLines = maskStringsAndRemoveComments("".join(self.extractedLines)).splitlines(1)
+
+ linesbefore = lines[:(self.startline - self.fn.getStartLine())]
+ linesafter = lines[(self.endline - self.fn.getStartLine()) + 1:]
+
+ # split into logical lines
+ linesbefore = [line for line in generateLogicalLines(linesbefore)]
+ extractedLines = [line for line in generateLogicalLines(extractedLines)]
+ linesafter = [line for line in generateLogicalLines(linesafter)]
+
+ if self.startline == self.endline:
+ # need to include the line code is extracted from
+ line = generateLogicalLines(lines[self.startline - self.fn.getStartLine():]).next()
+ linesbefore.append(line[:self.startcol] + "dummyFn()" + line[self.endcol:])
+ assigns = getAssignments(linesbefore)
+ fnargs = getFunctionArgs(linesbefore)
+ candidateArgs = assigns + fnargs
+ refs = getVariableReferencesInLines(extractedLines)
+ self.newfn.args = [ref for ref in refs if ref in candidateArgs]
+
+ assignsInExtractedBlock = getAssignments(extractedLines)
+ usesAfterNewFunctionCall = getVariableReferencesInLines(linesafter)
+ usesInPreceedingLoop = getVariableReferencesInLines(
+ self.getPreceedingLinesInLoop(linesbefore,line))
+ self.newfn.retvals = [ref for ref in usesInPreceedingLoop+usesAfterNewFunctionCall
+ if ref in assignsInExtractedBlock]
+
+ def getPreceedingLinesInLoop(self,linesbefore,firstLineToExtract):
+ if linesbefore == []: return []
+ tabwidth = getTabWidthOfLine(firstLineToExtract)
+ rootTabwidth = getTabWidthOfLine(linesbefore[0])
+ llines = [line for line in generateLogicalLines(linesbefore)]
+ startpos = len(llines)-1
+ loopTabwidth = tabwidth
+ for idx in range(startpos,0,-1):
+ line = llines[idx]
+ if re.match("(\s+)for",line) is not None or \
+ re.match("(\s+)while",line) is not None:
+ candidateLoopTabwidth = getTabWidthOfLine(line)
+ if candidateLoopTabwidth < loopTabwidth:
+ startpos = idx
+ return llines[startpos:]
+
+
+
+
+
+
+ def getRegionToBuffer(self):
+ startline = self.startline
+ endline = self.endline
+ startcol = self.startcol
+ endcol= self.endcol
+
+
+ self.extractedLines = self.sourcenode.getLines()[startline-1:endline]
+
+ match = re.match("\s*",self.extractedLines[0])
+ tabwidth = match.end(0)
+
+ self.extractedLines = [line[startcol:] for line in self.extractedLines]
+
+ # above cropping can take a blank line's newline off.
+ # this puts it back
+ for idx in range(len(self.extractedLines)):
+ if self.extractedLines[idx] == '':
+ self.extractedLines[idx] = self.linesep
+
+ if startline == endline:
+ # need to crop the end
+ # (n.b. if region is multiple lines, then whole lines are taken)
+ self.extractedLines[-1] = self.extractedLines[-1][:endcol-startcol]
+
+ if self.extractedLines[-1][-1] != '\n':
+ self.extractedLines[-1] += self.linesep
+
+ def extractedCodeIsAnExpression(self,lines):
+ if len(self.extractedLines) == 1:
+ charsBeforeSelection = lines[self.startline-1][:self.startcol]
+ if re.match("^\s*$",charsBeforeSelection) is not None:
+ return 0
+ if re.search(":\s*$",charsBeforeSelection) is not None:
+ return 0
+ return 1
+ return 0
+
+ def deduceIfIsMethodOrFunction(self):
+ if isinstance(self.fn.getParent(),Class):
+ self.isAMethod = 1
+ else:
+ self.isAMethod = 0
+
+
+# holds information about the new function
+class NewFunction:
+ def __init__(self,name):
+ self.name = name
+
+
+# lines = list of lines.
+# Have to have strings masked and comments removed
+def getAssignments(lines):
+ class AssignVisitor:
+ def __init__(self):
+ self.assigns = []
+
+ def visitAssTuple(self, node):
+ for a in node.nodes:
+ if a.name not in self.assigns:
+ self.assigns.append(a.name)
+
+ def visitAssName(self, node):
+ if node.name not in self.assigns:
+ self.assigns.append(node.name)
+
+ def visitAugAssign(self, node):
+ if isinstance(node.node, compiler.ast.Name):
+ if node.node.name not in self.assigns:
+ self.assigns.append(node.node.name)
+
+ assignfinder = AssignVisitor()
+ for line in lines:
+ doctoredline = makeLineParseable(line)
+ try:
+ ast = compiler.parse(doctoredline)
+ except ParserError:
+ raise ParserException("couldnt parse:"+doctoredline)
+ visitor.walk(ast, assignfinder)
+ return assignfinder.assigns
+
+
+# lines = list of lines.
+# Have to have strings masked and comments removed
+def getFunctionArgs(lines):
+ if lines == []: return []
+
+ class FunctionVisitor:
+ def __init__(self):
+ self.result = []
+ def visitFunction(self, node):
+ for n in node.argnames:
+ if n != "self":
+ self.result.append(n)
+ fndef = generateLogicalLines(lines).next()
+ doctoredline = makeLineParseable(fndef)
+ try:
+ ast = compiler.parse(doctoredline)
+ except ParserError:
+ raise ParserException("couldnt parse:"+doctoredline)
+ return visitor.walk(ast, FunctionVisitor()).result
+
+
+
+# lines = list of lines. Have to have strings masked and comments removed
+def getVariableReferencesInLines(lines):
+ class NameVisitor:
+ def __init__(self):
+ self.result = []
+ def visitName(self, node):
+ if node.name not in self.result:
+ self.result.append(node.name)
+ reffinder = NameVisitor()
+ for line in lines:
+ doctoredline = makeLineParseable(line)
+ try:
+ ast = compiler.parse(doctoredline)
+ except ParserError:
+ raise ParserException("couldnt parse:"+doctoredline)
+ visitor.walk(ast, reffinder)
+ return reffinder.result
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/refactor/extractVariable.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,32 @@
+from bike.parsing.parserutils import maskStringsAndRemoveComments
+from bike.transformer.undo import getUndoStack
+from parser import ParserError
+import compiler
+from bike.refactor.extractMethod import coords
+from bike.refactor.utils import getTabWidthOfLine, getLineSeperator,\
+ reverseCoordsIfWrongWayRound
+from bike.transformer.save import queueFileToSave
+from bike.parsing.load import getSourceNode
+
+
+def extractLocalVariable(filename, startcoords, endcoords, varname):
+ sourceobj = getSourceNode(filename)
+ if startcoords.line != endcoords.line:
+ raise "Can't do multi-line extracts yet"
+ startcoords, endcoords = \
+ reverseCoordsIfWrongWayRound(startcoords,endcoords)
+ line = sourceobj.getLine(startcoords.line)
+ tabwidth = getTabWidthOfLine(line)
+ linesep = getLineSeperator(line)
+ region = line[startcoords.column:endcoords.column]
+
+ getUndoStack().addSource(sourceobj.filename,sourceobj.getSource())
+ sourceobj.getLines()[startcoords.line-1] = \
+ line[:startcoords.column] + varname + line[endcoords.column:]
+
+ defnline = tabwidth*" " + varname + " = " + region + linesep
+
+ sourceobj.getLines().insert(startcoords.line-1,defnline)
+
+ queueFileToSave(sourceobj.filename,"".join(sourceobj.getLines()))
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/refactor/inlineVariable.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,94 @@
+from bike.query.findDefinition import findAllPossibleDefinitionsByCoords
+from bike.query.findReferences import findReferences
+from bike.parsing.parserutils import maskStringsAndRemoveComments, linecontinueRE
+from bike.transformer.undo import getUndoStack
+from bike.transformer.save import queueFileToSave
+from parser import ParserError
+from bike.parsing.load import getSourceNode
+import compiler
+import re
+
+
+def inlineLocalVariable(filename, lineno,col):
+ sourceobj = getSourceNode(filename)
+ return inlineLocalVariable_old(sourceobj, lineno,col)
+
+def inlineLocalVariable_old(sourcenode,lineno,col):
+ definition, region, regionlinecount = getLocalVariableInfo(sourcenode, lineno, col)
+ addUndo(sourcenode)
+ replaceReferences(sourcenode, findReferences(sourcenode.filename, definition.lineno, definition.colno), region)
+ delLines(sourcenode, definition.lineno-1, regionlinecount)
+ updateSource(sourcenode)
+
+def getLocalVariableInfo(sourcenode, lineno, col):
+ definition = findDefinition(sourcenode, lineno, col)
+ region, linecount = getRegionToInline(sourcenode, definition)
+ return definition, region, linecount
+
+def findDefinition(sourcenode, lineno, col):
+ definition = findAllPossibleDefinitionsByCoords(sourcenode.filename,
+ lineno,col).next()
+ assert definition.confidence == 100
+ return definition
+
+def getRegionToInline(sourcenode, defn):
+ line, linecount = getLineAndContinues(sourcenode, defn.lineno)
+ start, end = findRegionToInline(maskStringsAndRemoveComments(line))
+ return line[start:end], linecount
+
+def findRegionToInline(maskedline):
+ match = re.compile("[^=]+=\s*(.+)$\n", re.DOTALL).match(maskedline)
+ assert match
+ return match.start(1), match.end(1)
+
+# Possible refactoring: move to class of sourcenode
+def getLineAndContinues(sourcenode, lineno):
+ line = sourcenode.getLine(lineno)
+
+ linecount = 1
+ while linecontinueRE.search(line):
+ line += sourcenode.getLine(lineno + linecount)
+ linecount += 1
+
+ return line, linecount
+
+def addUndo(sourcenode):
+ getUndoStack().addSource(sourcenode.filename,sourcenode.getSource())
+
+def replaceReferences(sourcenode, references, replacement):
+ for reference in safeReplaceOrder( references ):
+ replaceReference(sourcenode, reference, replacement)
+
+def safeReplaceOrder( references ):
+ """
+ When inlining a variable, if multiple instances occur on the line, then the
+ last reference must be replaced first. Otherwise the remaining intra-line
+ references will be incorrect.
+ """
+ def safeReplaceOrderCmp(self, other):
+ return -cmp(self.colno, other.colno)
+
+ result = list(references)
+ result.sort(safeReplaceOrderCmp)
+ return result
+
+
+def replaceReference(sourcenode, ref, replacement):
+ """ sourcenode.getLines()[ref.lineno-1][ref.colno:ref.colend] = replacement
+ But strings don't support slice assignment as they are immutable. :(
+ """
+ sourcenode.getLines()[ref.lineno-1] = \
+ replaceSubStr(sourcenode.getLines()[ref.lineno-1],
+ ref.colno, ref.colend, replacement)
+
+def replaceSubStr(str, start, end, replacement):
+ return str[:start] + replacement + str[end:]
+
+# Possible refactoring: move to class of sourcenode
+def delLines(sourcenode, lineno, linecount=1):
+ del sourcenode.getLines()[lineno:lineno+linecount]
+
+def updateSource(sourcenode):
+ queueFileToSave(sourcenode.filename,"".join(sourcenode.getLines()))
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/refactor/moveToModule.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,148 @@
+import bike.globals
+from bike.parsing.load import getSourceNode
+from bike.parsing.fastparserast import Module
+from bike.query.common import getScopeForLine, convertNodeToMatchObject
+from bike.transformer.save import queueFileToSave, save
+from bike.transformer.undo import getUndoStack
+from bike.refactor.extractMethod import getVariableReferencesInLines
+from bike.refactor.utils import getLineSeperator
+from bike.query.findDefinition import findDefinitionFromASTNode
+from bike.query.findReferences import findReferences
+from bike.parsing.pathutils import filenameToModulePath
+from compiler.ast import Name
+import re
+
+def moveClassToNewModule(origfile,line,newfile):
+ srcnode = getSourceNode(origfile)
+ targetsrcnode = getSourceNode(newfile)
+ classnode = getScopeForLine(srcnode,line)
+ classlines = srcnode.getLines()[classnode.getStartLine()-1:
+ classnode.getEndLine()-1]
+ getUndoStack().addSource(srcnode.filename,
+ srcnode.getSource())
+ getUndoStack().addSource(targetsrcnode.filename,
+ targetsrcnode.getSource())
+
+ srcnode.getLines()[classnode.getStartLine()-1:
+ classnode.getEndLine()-1] = []
+
+ targetsrcnode.getLines().extend(classlines)
+
+ queueFileToSave(srcnode.filename,srcnode.getSource())
+ queueFileToSave(targetsrcnode.filename,targetsrcnode.getSource())
+
+
+exactFromRE = "(from\s+\S+\s+import\s+%s)(.*)"
+fromRE = "from\s+\S+\s+import\s+(.*)"
+
+def moveFunctionToNewModule(origfile,line,newfile):
+
+ srcnode = getSourceNode(origfile)
+ targetsrcnode = getSourceNode(newfile)
+ scope = getScopeForLine(srcnode,line)
+
+ linesep = getLineSeperator(srcnode.getLines()[0])
+
+ matches =[m for m in findReferences(origfile, line, scope.getColumnOfName())]
+
+ origFileImport = []
+ fromline = 'from %s import %s'%(filenameToModulePath(newfile),scope.name)
+
+ for match in matches:
+ if match.filename == origfile:
+ origFileImport = fromline + linesep
+ else:
+ s = getSourceNode(match.filename)
+ m = s.fastparseroot
+ if match.lineno in m.getImportLineNumbers():
+ getUndoStack().addSource(s.filename,
+ s.getSource())
+
+ maskedline = m.getLogicalLine(match.lineno)
+ origline = s.getLines()[match.lineno-1]
+ reMatch = re.match(exactFromRE%(scope.name),maskedline)
+ if reMatch and not (',' in reMatch.group(2) or \
+ '\\' in reMatch.group(2)):
+ # i.e. line is 'from module import foo'
+
+ if match.filename == newfile:
+ #remove the import
+ s.getLines()[match.lineno-1:match.lineno] = []
+ pass
+ else:
+ restOfOrigLine = origline[len(reMatch.group(1)):]
+ s.getLines()[match.lineno-1] = fromline + restOfOrigLine
+
+ elif re.match(fromRE,maskedline):
+ # i.e. line is 'from module import foo,bah,baz'
+ #remove the element from the import stmt
+ line = removeNameFromMultipleImportLine(scope.name, origline)
+ s.getLines()[match.lineno-1] = line
+ #and add a new line
+ nextline = match.lineno + maskedline.count('\\') + 1
+ s.getLines()[nextline-1:nextline-1] = [fromline+linesep]
+
+ queueFileToSave(s.filename,s.getSource())
+
+
+ refs = getVariableReferencesInLines(scope.getMaskedLines())
+
+ scopeLines = srcnode.getLines()[scope.getStartLine()-1:
+ scope.getEndLine()-1]
+ importModules = deduceImportsForNewFile(refs, scope)
+ importlines = composeNewFileImportLines(importModules, linesep)
+
+
+
+ getUndoStack().addSource(srcnode.filename,
+ srcnode.getSource())
+ getUndoStack().addSource(targetsrcnode.filename,
+ targetsrcnode.getSource())
+
+ srcnode.getLines()[scope.getStartLine()-1:
+ scope.getEndLine()-1] = origFileImport
+
+ targetsrcnode.getLines().extend(importlines+scopeLines)
+
+ queueFileToSave(srcnode.filename,srcnode.getSource())
+ queueFileToSave(targetsrcnode.filename,targetsrcnode.getSource())
+
+def removeNameFromMultipleImportLine(name, origline):
+ def replacefn(match):
+ return match.group(1)
+ line = re.sub('(\W)%s\s*?,'%(name),replacefn,origline)
+ return line
+
+
+
+
+def composeNewFileImportLines(importModules, linesep):
+ importlines = []
+ for mpath in importModules:
+ importlines += "from %s import %s"%(mpath,
+ ', '.join(importModules[mpath]))
+ importlines += linesep
+ return importlines
+
+def deduceImportsForNewFile(refs, scope):
+ importModules = {}
+ for ref in refs:
+ match = findDefinitionFromASTNode(scope,Name(ref))
+
+ if match.filename == scope.module.filename:
+ tgtscope = getScopeForLine(getSourceNode(match.filename),
+ match.lineno)
+ while tgtscope != scope and not isinstance(tgtscope,Module):
+ tgtscope = tgtscope.getParent()
+
+ if not isinstance(tgtscope,Module):
+ continue # was defined in this function
+
+ mpath = filenameToModulePath(match.filename)
+ if mpath in importModules:
+ importModules[mpath].append(ref)
+ else:
+ importModules[mpath] = [ref]
+ return importModules
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/refactor/rename.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,19 @@
+from bike.transformer.WordRewriter import WordRewriter
+from bike.query.findReferences import findReferencesIncludingDefn
+from bike.transformer.save import save
+
+def rename(filename,lineno,col,newname,promptcallback=None):
+ strrewrite = WordRewriter()
+ for match in findReferencesIncludingDefn(filename,lineno,col):
+ #print "rename match ",match
+ if match.confidence == 100 or promptUser(promptcallback,match):
+ strrewrite.rewriteString(match.sourcenode,
+ match.lineno,match.colno,newname)
+ strrewrite.commit()
+
+def promptUser(promptCallback,match):
+ if promptCallback is not None and \
+ promptCallback(match.filename, match.lineno, match.colno, match.colend):
+ return 1
+ return 0
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/refactor/setpath.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,5 @@
+import sys,os
+if not os.path.abspath("../..") in sys.path:
+ from bike import log
+ print >> log.warning, "Appending to the system path. This should only happen in unit tests"
+ sys.path.append(os.path.abspath("../.."))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/refactor/test_extractMethod.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,791 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+
+from bike.refactor.extractMethod import ExtractMethod, \
+ extractMethod, coords
+from bike import testdata
+from bike.testutils import *
+from bike.parsing.load import Cache
+
+def assertTokensAreSame(t1begin, t1end, tokens):
+ it = t1begin.clone()
+ pos = 0
+ while it != t1end:
+ assert it.deref() == tokens[pos]
+ it.incr()
+ pos+=1
+ assert pos == len(tokens)
+
+
+def helper(src,startcoords, endcoords, newname):
+ sourcenode = createAST(src)
+ extractMethod(tmpfile, startcoords, endcoords, newname)
+ return sourcenode.getSource()
+
+class TestExtractMethod(BRMTestCase):
+
+ def test_extractsPass(self):
+ srcBefore=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ pass
+ """)
+ srcAfter=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ self.newMethod()
+
+ def newMethod(self):
+ pass
+ """)
+ src = helper(srcBefore, coords(3, 8), coords(3, 12), "newMethod")
+ self.assertEqual(src,srcAfter)
+
+ def test_extractsPassWhenFunctionAllOnOneLine(self):
+ srcBefore=trimLines("""
+ class MyClass:
+ def myMethod(self): pass # comment
+ """)
+
+ srcAfter=trimLines("""
+ class MyClass:
+ def myMethod(self): self.newMethod() # comment
+
+ def newMethod(self):
+ pass
+ """)
+ src = helper(srcBefore, coords(2, 24), coords(2, 28),"newMethod")
+ self.assertEqual(src,srcAfter)
+
+ def test_extractsPassFromForLoop(self):
+ srcBefore=trimLines("""
+ class MyClass:
+ def myMethod(self): # comment
+ for i in foo:
+ pass
+ """)
+ srcAfter=trimLines("""
+ class MyClass:
+ def myMethod(self): # comment
+ for i in foo:
+ self.newMethod()
+
+ def newMethod(self):
+ pass
+ """)
+ src = helper(srcBefore, coords(4, 12), coords(4, 16), "newMethod")
+ self.assertEqual(srcAfter, src)
+
+ def test_newMethodHasArgumentsForUsedTemporarys(self):
+
+ srcBefore=trimLines("""
+ class MyClass:
+ def myMethod(self, c):
+ a = something()
+ b = somethingelse()
+ print a + b + c + d
+ print \"hello\"
+ dosomethingelse(a, b)
+ """)
+ srcAfter=trimLines("""
+ class MyClass:
+ def myMethod(self, c):
+ a = something()
+ b = somethingelse()
+ self.newMethod(a, b, c)
+ dosomethingelse(a, b)
+
+ def newMethod(self, a, b, c):
+ print a + b + c + d
+ print \"hello\"
+ """)
+
+ src = helper(srcBefore, coords(5, 8), coords(6, 21), "newMethod")
+ self.assertEqual(srcAfter, src)
+
+ def test_newMethodHasSingleArgument(self):
+ srcBefore=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ a = something()
+ print a
+ print \"hello\"
+ dosomethingelse(a, b)
+ """)
+ srcAfter=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ a = something()
+ self.newMethod(a)
+ dosomethingelse(a, b)
+
+ def newMethod(self, a):
+ print a
+ print \"hello\"
+ """)
+ src = helper(srcBefore, coords(4, 8), coords(5, 21), "newMethod")
+ self.assertEqual(srcAfter, src)
+
+
+ def test_doesntHaveDuplicateArguments(self):
+ srcBefore=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ a = 3
+ print a
+ print a
+ """)
+
+ srcAfter=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ a = 3
+ self.newMethod(a)
+
+ def newMethod(self, a):
+ print a
+ print a
+ """)
+ src = helper(srcBefore, coords(4, 0), coords(6, 0), "newMethod")
+ self.assertEqual(srcAfter, src)
+
+ def test_extractsQueryWhenFunctionAllOnOneLine(self):
+ srcBefore=trimLines("""
+ class MyClass:
+ def myMethod(self, a): print a # comment
+ """)
+
+ srcAfter=trimLines("""
+ class MyClass:
+ def myMethod(self, a): self.newMethod(a) # comment
+
+ def newMethod(self, a):
+ print a
+ """)
+ src = helper(srcBefore, coords(2, 27), coords(2, 34), "newMethod")
+ self.assertEqual(srcAfter, src)
+
+
+ def test_worksWhenAssignmentsToTuples(self):
+ srcBefore=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ a, b, c = 35, 36, 37
+ print a + b
+ """)
+ srcAfter=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ a, b, c = 35, 36, 37
+ self.newMethod(a, b)
+
+ def newMethod(self, a, b):
+ print a + b
+ """)
+
+ src = helper(srcBefore, coords(4, 8), coords(4, 19), "newMethod")
+ self.assertEqual(srcAfter, src)
+
+ def test_worksWhenUserSelectsABlockButDoesntSelectTheHangingDedent(self):
+ srcBefore=trimLines("""
+ class MyClass:
+ def myMethod(self): # comment
+ for i in foo:
+ pass
+ """)
+ srcAfter=trimLines("""
+ class MyClass:
+ def myMethod(self): # comment
+ for i in foo:
+ self.newMethod()
+
+ def newMethod(self):
+ pass
+ """)
+
+ src = helper(srcBefore, coords(4, 8), coords(4, 16), "newMethod")
+ self.assertEqual(srcAfter, src)
+
+ def test_newMethodHasSingleReturnValue(self):
+ srcBefore=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ a = 35 # <-- extract me
+ print a
+ """)
+ srcAfter=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ a = self.newMethod()
+ print a
+
+ def newMethod(self):
+ a = 35 # <-- extract me
+ return a
+ """)
+
+ src = helper(srcBefore, coords(3, 4),
+ coords(3, 34), "newMethod")
+ self.assertEqual(srcAfter, src)
+
+
+
+ def test_newMethodHasMultipleReturnValues(self):
+ srcBefore=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ a = 35
+ b = 352
+ print a + b
+ """)
+ srcAfter=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ a, b = self.newMethod()
+ print a + b
+
+ def newMethod(self):
+ a = 35
+ b = 352
+ return a, b
+ """)
+ src = helper(srcBefore, coords(3, 8),
+ coords(4, 15), "newMethod")
+ self.assertEqual(srcAfter, src)
+
+
+
+ def test_worksWhenMovingCodeJustAfterDedent(self):
+ srcBefore=trimLines("""
+ class MyClass:
+ def myMethod(self): # comment
+ for i in foo:
+ pass
+ print \"hello\"
+ """)
+ srcAfter=trimLines("""
+ class MyClass:
+ def myMethod(self): # comment
+ for i in foo:
+ pass
+ self.newMethod()
+
+ def newMethod(self):
+ print \"hello\"
+ """)
+
+ src = helper(srcBefore, coords(5, 8),
+ coords(5, 21), "newMethod")
+ self.assertEqual(srcAfter, src)
+
+
+ def test_extractsPassWhenSelectionCoordsAreReversed(self):
+ srcBefore=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ pass
+ """)
+ srcAfter=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ self.newMethod()
+
+ def newMethod(self):
+ pass
+ """)
+ src = helper(srcBefore, coords(3, 12), coords(3, 8), "newMethod")
+ self.assertEqual(srcAfter, src)
+
+
+ def test_extractsExpression(self):
+ srcBefore=trimLines("""
+ class MyClass:
+ def myMethod(self): # comment
+ a = 32
+ b = 2 + a * 1 + 2
+ """)
+ srcAfter=trimLines("""
+ class MyClass:
+ def myMethod(self): # comment
+ a = 32
+ b = 2 + self.newMethod(a) + 2
+
+ def newMethod(self, a):
+ return a * 1
+ """)
+ src = helper(srcBefore, coords(4, 16), coords(4, 21), "newMethod")
+ self.assertEqual(srcAfter, src)
+
+
+ def test_extractsExpression2(self):
+ srcBefore=trimLines("""
+ class MyClass:
+ def myMethod(self): # comment
+ g = 32
+ assert output.thingy(g) == \"bah\"
+ """)
+ srcAfter=trimLines("""
+ class MyClass:
+ def myMethod(self): # comment
+ g = 32
+ assert self.newMethod(g) == \"bah\"
+
+ def newMethod(self, g):
+ return output.thingy(g)
+ """)
+ src = helper(srcBefore, coords(4, 15), coords(4, 31), "newMethod")
+ self.assertEqual(srcAfter, src)
+
+
+
+class TestExtractFunction(BRMTestCase):
+ def runTarget(self, src, begincoords, endcoords, newname):
+ ast = createAST(src)
+ extractFunction(ast, begincoords, endcoords, newname)
+ return ast
+
+ def test_extractsFunction(self):
+ srcBefore=trimLines("""
+ def myFunction(): # comment
+ a = 3
+ c = a + 99
+ b = c * 1
+ print b
+ """)
+ srcAfter=trimLines("""
+ def myFunction(): # comment
+ a = 3
+ b = newFunction(a)
+ print b
+
+ def newFunction(a):
+ c = a + 99
+ b = c * 1
+ return b
+ """)
+
+ src = helper(srcBefore, coords(3, 4),
+ coords(4, 13), "newFunction")
+ self.assertEqual(srcAfter, src)
+
+ def test_extractsAssignToAttribute(self):
+ srcBefore=trimLines("""
+ def simulateLoad(path):
+ item = foo()
+ item.decl = line
+ """)
+ srcAfter=trimLines("""
+ def simulateLoad(path):
+ item = foo()
+ newFunction(item)
+
+ def newFunction(item):
+ item.decl = line
+ """)
+
+ src = helper(srcBefore, coords(3, 0),
+ coords(4, 0), "newFunction")
+ self.assertEqual(srcAfter, src)
+
+
+ def test_extractsFromFirstBlockOfIfElseStatement(self):
+ srcBefore=trimLines("""
+ def foo():
+ if bah:
+ print \"hello1\"
+ print \"hello2\"
+
+ elif foo:
+ pass
+ """)
+ srcAfter=trimLines("""
+ def foo():
+ if bah:
+ newFunction()
+ print \"hello2\"
+
+ elif foo:
+ pass
+
+ def newFunction():
+ print \"hello1\"
+ """)
+ src = helper(srcBefore, coords(3, 0),
+ coords(4, 0), "newFunction")
+ self.assertEqual(srcAfter, src)
+
+
+ def test_extractsAugAssign(self):
+ srcBefore=trimLines("""
+ def foo():
+ a = 3
+ a += 1
+ print a
+ """)
+ srcAfter=trimLines("""
+ def foo():
+ a = 3
+ a = newFunction(a)
+ print a
+
+ def newFunction(a):
+ a += 1
+ return a
+ """)
+ src = helper(srcBefore, coords(3, 0),
+ coords(4, 0), "newFunction")
+ self.assertEqual(srcAfter, src)
+
+ def test_extractsForLoopUsingLoopVariable(self):
+ srcBefore=trimLines("""
+ def foo():
+ for i in range(1, 3):
+ print i
+ """)
+ srcAfter=trimLines("""
+ def foo():
+ for i in range(1, 3):
+ newFunction(i)
+
+ def newFunction(i):
+ print i
+ """)
+
+ src = helper(srcBefore, coords(3, 0),
+ coords(4, 0), "newFunction")
+ self.assertEqual(srcAfter, src)
+
+ def test_extractWhileLoopVariableIncrement(self):
+ srcBefore=trimLines("""
+ def foo():
+ a = 0
+ while a != 3:
+ a = a+1
+ """)
+ srcAfter=trimLines("""
+ def foo():
+ a = 0
+ while a != 3:
+ a = newFunction(a)
+
+ def newFunction(a):
+ a = a+1
+ return a
+ """)
+ src = helper(srcBefore, coords(4, 0),
+ coords(5, 0), "newFunction")
+ self.assertEqual(srcAfter, src)
+
+ def test_extractAssignedVariableUsedInOuterForLoop(self):
+ srcBefore=trimLines("""
+ def foo():
+ b = 0
+ for a in range(1, 3):
+ b = b+1
+ while b != 2:
+ print a
+ b += 1
+ """)
+ srcAfter=trimLines("""
+ def foo():
+ b = 0
+ for a in range(1, 3):
+ b = b+1
+ while b != 2:
+ b = newFunction(a, b)
+
+ def newFunction(a, b):
+ print a
+ b += 1
+ return b
+ """)
+
+ src = helper(srcBefore, coords(6, 0),
+ coords(8, 0), "newFunction")
+ self.assertEqual(srcAfter, src)
+
+
+ def test_extractsConditionalFromExpression(self):
+ srcBefore=trimLines("""
+ def foo():
+ if 123+3:
+ print aoue
+ """)
+ srcAfter=trimLines("""
+ def foo():
+ if newFunction():
+ print aoue
+
+ def newFunction():
+ return 123+3
+ """)
+ src = helper(srcBefore, coords(2, 7),
+ coords(2, 12), "newFunction")
+ self.assertEqual(srcAfter, src)
+
+ def test_extractCodeAfterCommentInMiddleOfFnDoesntRaiseParseException(self):
+ srcBefore=trimLines("""
+ def theFunction():
+ print 1
+ # comment
+ print 2
+ """)
+ srcAfter=trimLines("""
+ def theFunction():
+ print 1
+ # comment
+ newFunction()
+
+ def newFunction():
+ print 2
+ """)
+ src = helper(srcBefore, coords(4, 0),
+ coords(5, 0), "newFunction")
+ self.assertEqual(srcAfter, src)
+
+
+ def test_canExtractQueryFromNestedIfStatement(self):
+ srcBefore=trimLines("""
+ def theFunction():
+ if foo: # comment
+ if bah:
+ pass
+ """)
+ srcAfter=trimLines("""
+ def theFunction():
+ if foo: # comment
+ if newFunction():
+ pass
+
+ def newFunction():
+ return bah
+ """)
+ src = helper(srcBefore, coords(3, 11),
+ coords(3, 14), "newFunction")
+ self.assertEqual(srcAfter, src)
+
+
+
+ def test_doesntMessUpTheNextFunctionOrClass(self):
+ srcBefore=trimLines("""
+ def myFunction():
+ a = 3
+ print \"hello\"+a # extract me
+
+ class MyClass:
+ def myMethod(self):
+ b = 12 # extract me
+ c = 3 # and me
+ d = 2 # and me
+ print b, c
+ """)
+ srcAfter=trimLines("""
+ def myFunction():
+ a = 3
+ newFunction(a)
+
+ def newFunction(a):
+ print \"hello\"+a # extract me
+
+ class MyClass:
+ def myMethod(self):
+ b = 12 # extract me
+ c = 3 # and me
+ d = 2 # and me
+ print b, c
+ """)
+
+ # extract code on one line
+ src = helper(srcBefore, coords(3, 4),
+ coords(3, 34), "newFunction")
+ self.assertEqual(srcAfter, src)
+
+ # extract code on 2 lines (most common user method)
+ resetRoot()
+ Cache.instance.reset()
+ Root()
+ src = helper(srcBefore, coords(3, 0),
+ coords(4, 0), "newFunction")
+ self.assertEqual(srcAfter, src)
+
+
+ def test_doesntBallsUpIndentWhenTheresALineWithNoSpacesInIt(self):
+ srcBefore=trimLines("""
+ def theFunction():
+ if 1:
+ pass
+
+ pass
+ """)
+ srcAfter=trimLines("""
+ def theFunction():
+ newFunction()
+
+ def newFunction():
+ if 1:
+ pass
+
+ pass
+ """)
+ src = helper(srcBefore, coords(2, 4),
+ coords(5, 8), "newFunction")
+ self.assertEqual(srcAfter, src)
+
+
+ def test_doesntHaveToBeInsideAFunction(self):
+ srcBefore=trimLines(r"""
+ a = 1
+ print a + 2
+ f(b)
+ """)
+ srcAfter=trimLines(r"""
+ a = 1
+ newFunction(a)
+
+ def newFunction(a):
+ print a + 2
+ f(b)
+ """)
+ src = helper(srcBefore, coords(2, 0),
+ coords(3, 4), "newFunction")
+ self.assertEqual(srcAfter, src)
+
+
+ def test_doesntBarfWhenEncountersMethodCalledOnCreatedObj(self):
+ srcBefore=trimLines(r"""
+ results = QueryEngine(q).foo()
+ """)
+ srcAfter=trimLines(r"""
+ newFunction()
+
+ def newFunction():
+ results = QueryEngine(q).foo()
+ """)
+ src = helper(srcBefore, coords(1, 0),
+ coords(2, 0), "newFunction")
+ self.assertEqual(srcAfter, src)
+
+
+ def test_worksIfNoLinesBeforeExtractedCode(self):
+ srcBefore=trimLines(r"""
+ print a + 2
+ f(b)
+ """)
+ srcAfter=trimLines(r"""
+ newFunction()
+
+ def newFunction():
+ print a + 2
+ f(b)
+ """)
+ src = helper(srcBefore, coords(1, 0),
+ coords(2, 4), "newFunction")
+ self.assertEqual(srcAfter, src)
+
+
+class TestGetRegionAsString(BRMTestCase):
+ def test_getsHighlightedSingleLinePassStatement(self):
+ src=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ pass
+ """)
+ sourcenode = createAST(src)
+ em = ExtractMethod(sourcenode, coords(3, 8),
+ coords(3, 12), "foobah")
+ em.getRegionToBuffer()
+ self.assertEqual(len(em.extractedLines), 1)
+ self.assertEqual(em.extractedLines[0], "pass\n")
+
+ def test_getsSingleLinePassStatementWhenWholeLineIsHighlighted(self):
+ src=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ pass
+ """)
+ sourcenode = createAST(src)
+ em = ExtractMethod(sourcenode, coords(3, 0),
+ coords(3, 12), "foobah")
+ em.getRegionToBuffer()
+ self.assertEqual(len(em.extractedLines), 1)
+ self.assertEqual(em.extractedLines[0], "pass\n")
+
+
+ def test_getsMultiLineRegionWhenJustRegionIsHighlighted(self):
+ src=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ print 'hello'
+ pass
+ """)
+ region=trimLines("""
+ print 'hello'
+ pass
+ """)
+ sourcenode = createAST(src)
+ em = ExtractMethod(sourcenode, coords(3, 8),
+ coords(4, 12), "foobah")
+ em.getRegionToBuffer()
+ self.assertEqual(em.extractedLines, region.splitlines(1))
+
+ def test_getsMultiLineRegionWhenRegionLinesAreHighlighted(self):
+ src=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ print 'hello'
+ pass
+
+ """)
+ region=trimLines("""
+ print 'hello'
+ pass
+ """)
+ sourcenode = createAST(src)
+ em = ExtractMethod(sourcenode, coords(3, 0),
+ coords(5, 0), "foobah")
+ em.getRegionToBuffer()
+ self.assertEqual(em.extractedLines, region.splitlines(1))
+
+ def test_getsHighlightedSubstringOfLine(self):
+ src=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ if a == 3:
+ pass
+ """)
+ region=trimLines("""
+ a == 3
+ """)
+ sourcenode = createAST(src)
+ em = ExtractMethod(sourcenode, coords(3, 11),
+ coords(3, 17), "foobah")
+ em.getRegionToBuffer()
+ self.assertEqual(em.extractedLines, region.splitlines(1))
+
+
+class TestGetTabwidthOfParentFunction(BRMTestCase):
+ def test_getsTabwidthForSimpleMethod(self):
+ src=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ pass
+ """)
+ sourcenode = createAST(src)
+ em = ExtractMethod(sourcenode, coords(3, 11),
+ coords(3, 17), "foobah")
+ self.assertEqual(em.getTabwidthOfParentFunction(), 4)
+
+ def test_getsTabwidthForFunctionAtRootScope(self):
+ src=trimLines("""
+ def myFn(self):
+ pass
+ """)
+ sourcenode = createAST(src)
+ em = ExtractMethod(sourcenode, coords(2, 0),
+ coords(2, 9), "foobah")
+ self.assertEqual(em.getTabwidthOfParentFunction(), 0)
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/refactor/test_extractVariable.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+from bike.testutils import *
+from bike.refactor.extractVariable import coords, extractLocalVariable
+
+class TestExtractLocalVariable(BRMTestCase):
+ def test_worksOnSimpleCase(self):
+ srcBefore=trimLines("""
+ def foo():
+ print 3 + 2
+ """)
+ srcAfter=trimLines("""
+ def foo():
+ a = 3 + 2
+ print a
+ """)
+ sourcenode = createAST(srcBefore)
+ extractLocalVariable(tmpfile,coords(2,10),coords(2,15),'a')
+ self.assertEqual(sourcenode.getSource(),srcAfter)
+
+ def test_worksIfCoordsTheWrongWayRound(self):
+ srcBefore=trimLines("""
+ def foo():
+ print 3 + 2
+ """)
+ srcAfter=trimLines("""
+ def foo():
+ a = 3 + 2
+ print a
+ """)
+ sourcenode = createAST(srcBefore)
+ extractLocalVariable(tmpfile,coords(2,15),coords(2,10),'a')
+ self.assertEqual(sourcenode.getSource(),srcAfter)
+
+
+if __name__ == "__main__":
+ unittest.main()
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/refactor/test_inlineVariable.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,114 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+from bike.testutils import trimLines,createAST, BRMTestCase
+from inlineVariable import inlineLocalVariable_old
+
+class TestInlineLocalVariable(BRMTestCase):
+
+ def test_worksWhenUserDoesItAgainstReference(self):
+ srcBefore=r"""
+ def foo():
+ b = 'hello'
+ print b
+ """
+ srcAfter=r"""
+ def foo():
+ print 'hello'
+ """
+
+ self.helper( srcBefore, 3, 10, srcAfter )
+
+ def test_worksWhenInlinedCodeIsOverTwoLines(self):
+ srcBefore=r"""
+ def foo():
+ b = 3 + \
+ 2
+ print b
+ """
+
+ srcAfter=r"""
+ def foo():
+ print 3 + \
+ 2
+ """
+
+ self.helper(srcBefore, 2, 4, srcAfter)
+
+ ''' Needs Adding Again
+ def test_addsBracketsWhenInlinedCodeHasPresidenceOverSurroundingCode(self):
+ srcBefore=trimLines(r"""
+ def foo():
+ b = 3 + 2
+ print 3 * b
+ """)
+ srcAfter=trimLines(r"""
+ def foo():
+ print 3 * (3 + 2)
+ """)
+ assert 0
+ '''
+
+ def test_worksWithMultipleInstancesOfVariableOnLine(self):
+ srcBefore=r"""
+ def foo():
+ x = 11
+ print x, x
+ """
+
+ srcAfter=r"""
+ def foo():
+ print 11, 11
+ """
+
+ self.helper(srcBefore, 2, 4, srcAfter)
+
+ def test_worksWithMultipleMultilineCode(self):
+ srcBefore=r"""
+ def foo():
+ b = 3 + \
+ 2
+ print b
+ print b
+ """
+
+ srcAfter=r"""
+ def foo():
+ print 3 + \
+ 2
+ print 3 + \
+ 2
+ """
+
+ self.helper(srcBefore, 2, 4, srcAfter)
+
+ ''' Can't do this without some hairy logic to deduce how to inline
+ the variables. E.g. how do you inline a,b = foo() ?
+
+ def test_handlesTupleAssignment(self):
+ srcBefore=r"""
+ def foo():
+ x, y = 1, 2
+ print x
+ print y
+ """
+
+ srcAfter=r"""
+ def foo():
+ y = 2
+ print 1
+ print y
+ """
+
+ self.helper(srcBefore, 2, 4, srcAfter)
+ '''
+
+
+ def helper(self, srcBefore, y, x, srcAfter):
+ sourcenode = createAST(trimLines(srcBefore))
+ inlineLocalVariable_old(sourcenode,y,x)
+ self.assertEqual(sourcenode.getSource(),trimLines(srcAfter))
+
+if __name__ == "__main__":
+ unittest.main()
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/refactor/test_moveToModule.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,241 @@
+#!/usr/bin/env python
+import setpath
+from bike.testutils import *
+from bike.transformer.save import save
+
+from moveToModule import *
+
+class TestMoveClass(BRMTestCase):
+ def test_movesTheText(self):
+ src1=trimLines("""
+ def before(): pass
+ class TheClass:
+ pass
+ def after(): pass
+ """)
+ src1after=trimLines("""
+ def before(): pass
+ def after(): pass
+ """)
+ src2after=trimLines("""
+ class TheClass:
+ pass
+ """)
+
+ try:
+ createPackageStructure(src1, "")
+ moveClassToNewModule(pkgstructureFile1,2,
+ pkgstructureFile2)
+ save()
+ self.assertEqual(src1after,file(pkgstructureFile1).read())
+ self.assertEqual(src2after,file(pkgstructureFile2).read())
+ finally:
+ removePackageStructure()
+
+class TestMoveFunction(BRMTestCase):
+ def test_importsNameReference(self):
+ src1=trimLines("""
+ a = 'hello'
+ def theFunction(self):
+ print a
+ """)
+ src2after=trimLines("""
+ from a.foo import a
+ def theFunction(self):
+ print a
+ """)
+ self.helper(src1, src2after)
+
+
+
+ def test_importsExternalReference(self):
+ src0=("""
+ a = 'hello'
+ """)
+ src1=trimLines("""
+ from top import a
+ def theFunction(self):
+ print a
+ """)
+ src2after=trimLines("""
+ from top import a
+ def theFunction(self):
+ print a
+ """)
+ try:
+ createPackageStructure(src1, "", src0)
+ moveFunctionToNewModule(pkgstructureFile1,2,
+ pkgstructureFile2)
+ save()
+ self.assertEqual(src2after,file(pkgstructureFile2).read())
+ finally:
+ removePackageStructure()
+
+ def test_doesntImportRefCreatedInFunction(self):
+ src1=trimLines("""
+ def theFunction(self):
+ a = 'hello'
+ print a
+ """)
+ src2after=trimLines("""
+ def theFunction(self):
+ a = 'hello'
+ print a
+ """)
+
+ self.helper(src1, src2after)
+
+
+ def test_doesntImportRefCreatedInFunction(self):
+ src1=trimLines("""
+ def theFunction(self):
+ a = 'hello'
+ print a
+ """)
+ src2after=trimLines("""
+ def theFunction(self):
+ a = 'hello'
+ print a
+ """)
+
+ self.helper(src1, src2after)
+
+
+ def test_addsImportStatementToOriginalFileIfRequired(self):
+ src1=trimLines("""
+ def theFunction(self):
+ pass
+ b = theFunction()
+ """)
+
+ src1after=trimLines("""
+ from a.b.bah import theFunction
+ b = theFunction()
+ """)
+ try:
+ createPackageStructure(src1,"")
+ moveFunctionToNewModule(pkgstructureFile1,1,
+ pkgstructureFile2)
+ save()
+ self.assertEqual(src1after,file(pkgstructureFile1).read())
+ finally:
+ removePackageStructure()
+
+ def test_updatesFromImportStatementsInOtherModules(self):
+ src0=trimLines("""
+ from a.foo import theFunction
+ print theFunction()
+ """)
+ src1=trimLines("""
+ def theFunction(self):
+ pass
+ """)
+
+ src0after=trimLines("""
+ from a.b.bah import theFunction
+ print theFunction()
+ """)
+ try:
+ createPackageStructure(src1,"",src0)
+ moveFunctionToNewModule(pkgstructureFile1,1,
+ pkgstructureFile2)
+ save()
+ self.assertEqual(src0after,file(pkgstructureFile0).read())
+ finally:
+ removePackageStructure()
+
+ def test_updatesFromImportMultiplesInOtherModules(self):
+ src0=trimLines("""
+ from a.foo import something,theFunction,somethingelse #comment
+ print theFunction()
+ """)
+ src1=trimLines("""
+ def theFunction(self):
+ pass
+ something = ''
+ somethingelse = 0
+ """)
+
+ src0after=trimLines("""
+ from a.foo import something,somethingelse #comment
+ from a.b.bah import theFunction
+ print theFunction()
+ """)
+ try:
+ createPackageStructure(src1,"",src0)
+ moveFunctionToNewModule(pkgstructureFile1,1,
+ pkgstructureFile2)
+ save()
+ self.assertEqual(src0after,file(pkgstructureFile0).read())
+ finally:
+ removePackageStructure()
+
+ def test_updatesFromImportMultiplesInTargetModule(self):
+ src0=trimLines("""
+ from a.foo import something,theFunction,somethingelse #comment
+ print theFunction()
+ """)
+ src1=trimLines("""
+ def theFunction(self):
+ pass
+ something = ''
+ somethingelse = 0
+ """)
+
+ src0after=trimLines("""
+ from a.foo import something,somethingelse #comment
+ print theFunction()
+ def theFunction(self):
+ pass
+ """)
+ try:
+ createPackageStructure(src1,"",src0)
+ moveFunctionToNewModule(pkgstructureFile1,1,
+ pkgstructureFile0)
+ save()
+ #print file(pkgstructureFile0).read()
+ self.assertEqual(src0after,file(pkgstructureFile0).read())
+ finally:
+ removePackageStructure()
+
+
+ def test_updatesFromImportInTargetModule(self):
+ src0=trimLines("""
+ from a.foo import theFunction
+ print theFunction()
+ """)
+ src1=trimLines("""
+ def theFunction(self):
+ pass
+ """)
+
+ src0after=trimLines("""
+ print theFunction()
+ def theFunction(self):
+ pass
+ """)
+ try:
+ createPackageStructure(src1,"",src0)
+ moveFunctionToNewModule(pkgstructureFile1,1,
+ pkgstructureFile0)
+ save()
+ self.assertEqual(src0after,file(pkgstructureFile0).read())
+ finally:
+ removePackageStructure()
+
+
+
+ def helper(self, src1, src2after):
+ try:
+ createPackageStructure(src1, "")
+ moveFunctionToNewModule(pkgstructureFile1,2,
+ pkgstructureFile2)
+ save()
+ self.assertEqual(src2after,file(pkgstructureFile2).read())
+ finally:
+ removePackageStructure()
+
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/refactor/test_rename.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+from bike import testdata
+from rename import rename
+from bike.testutils import *
+
+class TestRenameTemporary(BRMTestCase):
+ def test_renamesSimpleReferencesGivenAssignment(self):
+ src=trimLines("""
+ def foo():
+ a = 3
+ print a
+ """)
+ srcAfter=trimLines("""
+ def foo():
+ b = 3
+ print b
+ """)
+ src = self.helper(src,"",2,4,"b")
+ self.assertEqual(srcAfter,src)
+
+ def helper(self, src, classsrc, line, col, newname):
+ try:
+ createPackageStructure(src,classsrc)
+ filename = pkgstructureFile1
+ rename(filename,line,col,newname)
+ # modify me once save is moved
+ #return readFile(filename)
+ from bike.transformer.save import outputqueue
+ return outputqueue[filename]
+ finally:
+ removePackageStructure()
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/refactor/test_renameClass.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,257 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+from rename import rename
+from bike.transformer.save import save
+from bike.testutils import *
+import compiler
+
+class RenameClassTests:
+
+ def testRenamesClassDcl(self):
+ srcBefore=trimLines("""
+ class TheClass:
+ def theMethod():
+ pass
+ """)
+ srcAfter=trimLines("""
+ class NewName:
+ def theMethod():
+ pass
+ """)
+
+ src = self.rename(srcBefore, 1,6,"NewName")
+ self.assertEqual(srcAfter,src)
+
+ # i.e. a = TheClass()
+ def testRenamesClassReference(self):
+ srcBefore=trimLines("""
+ class TheClass:
+ pass
+ a = TheClass()
+ """)
+ srcAfter=trimLines("""
+ class NewName:
+ pass
+ a = NewName()
+ """)
+ src = self.rename(srcBefore, 1,6,"NewName")
+ self.assertEqual(srcAfter,src)
+
+ # i.e. a = TheClass.TheClass()
+ def testRenamesClassReferenceWhenScopeIsSameNameAsClass(self):
+ srcBefore = trimLines("""
+ class TheClass:
+ class TheClass:
+ pass
+ a = TheClass.TheClass()
+ """)
+ srcAfter=trimLines("""
+ class TheClass:
+ class NewName:
+ pass
+ a = TheClass.NewName()
+ """)
+ src = self.rename(srcBefore, 2,10, "NewName")
+ self.assertEqual(srcAfter,src)
+
+ # i.e. a = TheClass.TheClass()
+ def testRenamesClassReferenceWhenChildIsSameNameAsClass(self):
+ srcBefore = trimLines("""
+ class TheClass:
+ class TheClass:
+ pass
+ a = TheClass.TheClass()
+ """)
+ srcAfter=trimLines("""
+ class NewName:
+ class TheClass:
+ pass
+ a = NewName.TheClass()
+ """)
+ src = self.rename(srcBefore, 1,6,"NewName")
+ self.assertEqual(srcAfter,src)
+
+
+ # a = TheClass() + TheClass()
+ def testRenamesClassReferenceWhenTwoRefsInTheSameLine(self):
+ srcBefore=trimLines("""
+ class TheClass:
+ pass
+ a = TheClass() + TheClass()
+ """)
+ srcAfter=trimLines("""
+ class NewName:
+ pass
+ a = NewName() + NewName()
+ """)
+ src = self.rename(srcBefore,1,6, "NewName")
+ self.assertEqual(srcAfter,src)
+
+ def testRenamesClassReferenceInInstanceCreation(self):
+ srcBefore=trimLines("""
+ class TheClass:
+ def theMethod(self): pass
+ TheClass().theMethod()
+ """)
+ srcAfter=trimLines("""
+ class NewName:
+ def theMethod(self): pass
+ NewName().theMethod()
+ """)
+ src = self.rename(srcBefore,1,6,"NewName")
+ self.assertEqual(srcAfter,src)
+
+ # i.e. if renaming TheClass, shouldnt rename a.b.c.TheClass
+ def testDoesntRenameBugusClassReferenceOnEndOfGetattrNest(self):
+ srcBefore=trimLines("""
+ class TheClass:
+ pass
+ a.b.c.TheClass # Shouldn't be renamed
+ """)
+ srcAfter=trimLines("""
+ class NewName:
+ pass
+ a.b.c.TheClass # Shouldn't be renamed
+ """)
+ src = self.rename(srcBefore,1,6,"NewName")
+ self.assertEqual(srcAfter,src)
+
+ def testRenamesClassRefUsedInExceptionRaise(self):
+ srcBefore=trimLines("""
+ class TheClass:
+ pass
+ raise TheClass, \"hello mum\"
+ """)
+ srcAfter=trimLines("""
+ class NewName:
+ pass
+ raise NewName, \"hello mum\"
+ """)
+ src = self.rename(srcBefore, 1,6, "NewName")
+ self.assertEqual(srcAfter,src)
+
+ def testRenamesClassReferenceNameInInheritenceSpec(self):
+ srcBefore=trimLines("""
+ class TheClass:
+ pass
+ class DerivedClass(TheClass):
+ pass
+ """)
+ srcAfter=trimLines("""
+ class NewName:
+ pass
+ class DerivedClass(NewName):
+ pass
+ """)
+ src = self.rename(srcBefore, 1,6, "NewName")
+ self.assertEqual(srcAfter,src)
+
+
+
+class RenameClassTests_importsClass:
+
+ def testRenamesClassReferenceInInstanceCreationWithFQN(self):
+ srcBefore=trimLines("""
+ import b.bah
+ def foo():
+ a = b.bah.TheClass()
+ """)
+ srcAfter=trimLines("""
+ import b.bah
+ def foo():
+ a = b.bah.NewName()
+ """)
+ src = self.renameClass(srcBefore,"NewName")
+ self.assertEqual(srcAfter,src)
+
+ def testRenamesClassReferencesInInheritenceSpecs(self):
+
+ srcBefore=trimLines("""
+ import b
+ class DerivedClass(b.bah.TheClass):
+ pass
+ """)
+ srcAfter=trimLines("""
+ import b
+ class DerivedClass(b.bah.NewName):
+ pass
+ """)
+ src = self.renameClass(srcBefore,"NewName")
+ self.assertEqual(srcAfter,src)
+
+ def testRenamesFromImportReferenceWhenInBodyOfClass(self):
+ srcBefore=trimLines("""
+ class AnotherClass:
+ from b.bah import TheClass
+ TheClass.baz = 0
+ """)
+ srcAfter=trimLines("""
+ class AnotherClass:
+ from b.bah import NewName
+ NewName.baz = 0
+ """)
+ src = self.renameClass(srcBefore,"NewName")
+ self.assertEqual(srcAfter,src)
+
+
+ def testRenamesReferenceToClassImportedInSameClassScope(self):
+ srcBefore=trimLines("""
+ class AnotherClass:
+ from b.bah import TheClass
+ TheClass.baz = 0
+ """)
+ srcAfter=trimLines("""
+ class AnotherClass:
+ from b.bah import NewName
+ NewName.baz = 0
+ """)
+ src = self.renameClass(srcBefore,"NewName")
+ self.assertEqual(srcAfter,src)
+
+ def testRenamesReferenceToClassImportedWithFromImportStar(self):
+ srcBefore=trimLines("""
+ from a.b.bah import *
+ a = TheClass()
+ """)
+ srcAfter=trimLines("""
+ from a.b.bah import *
+ a = NewName()
+ """)
+ src = self.renameClass(srcBefore,"NewName")
+ self.assertEqual(srcAfter,src)
+
+class TestRenameClass(BRMTestCase, RenameClassTests):
+
+ def rename(self, src, line, col, newname):
+ createPackageStructure(src,"pass")
+ rename(pkgstructureFile1,line,col, newname)
+ save()
+ return file(pkgstructureFile1).read()
+
+
+class TestRenameClassReferenceWithDirectoryStructure(BRMTestCase,
+ RenameClassTests_importsClass):
+
+ def renameClass(self, src, newname):
+ createPackageStructure(src,TheClassTestdata)
+ rename(pkgstructureFile2,1,6, newname)
+ save()
+ return file(pkgstructureFile1).read()
+
+
+TheClassTestdata = trimLines("""
+class TheClass:
+ def theMethod(self):
+ pass
+ def differentMethod(self):
+ pass
+
+class DifferentClass:
+ def theMethod(self):
+ pass
+""")
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/refactor/test_renameFunction.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,118 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+from rename import rename
+from bike import testdata
+from bike.testutils import *
+from bike.transformer.save import save
+import compiler
+
+class RenameFunctionTests:
+ def runTarget(self, src, klassfqn, newname):
+ # see concrete subclasses for implementation
+ pass
+
+ def testRenamesFunctionDcl(self):
+ srcBefore=trimLines("""
+ def theFunction():
+ pass
+ """)
+ srcAfter=trimLines("""
+ def newName():
+ pass
+ """)
+ src = self.rename(srcBefore,1,4,"newName")
+ self.assertEqual(srcAfter,src)
+
+ def testDoesntBarfWhenFunctionIncludesBrackettedExpression(self):
+ srcBefore=trimLines("""
+ def theFunction():
+ return ('\\n').strip()
+ """)
+ srcAfter=trimLines("""
+ def newName():
+ return ('\\n').strip()
+ """)
+ src = self.rename(srcBefore,1,4, "newName")
+ self.assertEqual(srcAfter,src)
+
+
+
+
+class RenameFunctionTests_importsFunction:
+
+ def testRenamesImportedFunctionReference(self):
+ srcBefore=trimLines("""
+ import b.bah
+ b.bah.theFunction()
+ """)
+ srcAfter=trimLines("""
+ import b.bah
+ b.bah.newName()
+ """)
+ src = self.renameFunction(srcBefore,"newName")
+ self.assertEqual(srcAfter,src)
+
+ def testRenamesFunctionReferenceImportedWithFromClause(self):
+ srcBefore=trimLines("""
+ from b.bah import theFunction
+ theFunction()
+ """)
+ srcAfter=trimLines("""
+ from b.bah import newName
+ newName()
+ """)
+ src = self.renameFunction(srcBefore,"newName")
+ self.assertEqual(srcAfter,src)
+
+ def testRenamesFunctionRefInImportClause(self):
+ srcBefore=trimLines("""
+ import b.bah
+ b.bah.theFunction()
+ """)
+ srcAfter=trimLines("""
+ import b.bah
+ b.bah.newName()
+ """)
+ src = self.renameFunction(srcBefore,"newName")
+ self.assertEqual(srcAfter,src)
+
+
+ def testRenamesFunctionRefInImportFromClause(self):
+ srcBefore=trimLines("""
+ from b.bah import theFunction
+ theFunction()
+ """)
+ srcAfter=trimLines("""
+ from b.bah import newName
+ newName()
+ """)
+ src = self.renameFunction(srcBefore,"newName")
+ self.assertEqual(srcAfter,src)
+
+
+
+class TestRenameFunction(BRMTestCase, RenameFunctionTests):
+ def rename(self, src, line, col, newname):
+ writeTmpTestFile(src)
+ rename(tmpfile,line,col, newname)
+ save()
+ return file(tmpfile).read()
+
+
+class TestRenameFunctionReferenceWithDirectoryStructure(BRMTestCase, RenameFunctionTests_importsFunction):
+
+ def renameFunction(self, src, newname):
+ createPackageStructure(src,FunctionTestdata)
+ rename(pkgstructureFile2,1,4, newname)
+ save()
+ return file(pkgstructureFile1).read()
+
+FunctionTestdata = trimLines("""
+def theFunction():
+ pass
+""")
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/refactor/test_renameMethod.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,710 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+from rename import rename
+import compiler
+from bike import testdata
+
+from bike.testutils import*
+
+from bike.transformer.save import save
+
+class RenameMethodTests:
+
+ def test_renamesTheMethod(self):
+ srcBefore=trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ """)
+ srcAfter=trimLines("""
+ class TheClass:
+ def newName(self):
+ pass
+ """)
+ src = self.rename(srcBefore,2,8,"newName")
+ self.assertEqual(srcAfter,src)
+
+ def test_doesntRenameMethodOfSameNameOnOtherClasses(self):
+ srcBefore=trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ class b:
+ def theMethod(self):
+ pass
+ """)
+ srcAfter=trimLines("""
+ class TheClass:
+ def newName(self):
+ pass
+ class b:
+ def theMethod(self):
+ pass
+ """)
+ src = self.rename(srcBefore,2,8,"newName")
+ self.assertEqual(srcAfter,src)
+
+ def test_doesntRenameOtherMethodsOfSameClass(self):
+ srcBefore=trimLines("""
+ class TheClass:
+ def theMethod(self):
+ a=b
+ def aMethod(self):
+ pass
+ """)
+ srcAfter=trimLines("""
+ class TheClass:
+ def newName(self):
+ a=b
+ def aMethod(self):
+ pass
+ """)
+ src = self.rename(srcBefore,2,8,"newName")
+ self.assertEqual(srcAfter,src)
+
+ def test_renamesMethodWhenClassNestedInFunction(self):
+ srcBefore=trimLines("""
+ def theFunction():
+ class TheClass:
+ def theMethod(self):
+ pass
+ """)
+ srcAfter=trimLines("""
+ def theFunction():
+ class TheClass:
+ def newName(self):
+ pass
+ """)
+ src = self.rename(srcBefore,3,12,"newName")
+ self.assertEqual(srcAfter,src)
+
+ def test_doesntBarfOnInheritanceHierarchies(self):
+ srcBefore=trimLines("""
+ from b.bah import DifferentClass
+ class TheClass(foo.bah):
+ def theMethod(self):
+ pass
+ """)
+ src = self.rename(srcBefore,2,8,"newName")
+
+ def test_renamesMethodWhenMethodCallFromOtherMethodInSameClass(self):
+ srcBefore=trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ def anotherMethod(self):
+ self.theMethod()
+ """)
+ srcAfter=trimLines("""
+ class TheClass:
+ def newName(self):
+ pass
+ def anotherMethod(self):
+ self.newName()
+ """)
+ src = self.rename(srcBefore,2,8,"newName")
+ self.assertEqual(srcAfter,src)
+
+ def test_doesntBarfOnNestedClasses(self):
+ srcBefore=trimLines("""
+ class TheClass:
+ class AnotherClass:
+ pass
+ def theMethod(self):
+ pass
+ """)
+ src = self.rename(srcBefore,4,8,"newName")
+
+ def test_renamesMethodWhenBaseClassesArentInAST(self):
+ srcBefore=trimLines("""
+ class TheClass(notInAst):
+ def theMethod(self):
+ pass
+ """)
+ srcAfter=trimLines("""
+ class TheClass(notInAst):
+ def newName(self):
+ pass
+ """)
+ src = self.rename(srcBefore,2,8,"newName")
+ self.assertEqual(srcAfter,src)
+
+ def test_renamesMethodInRelatedClasses(self):
+ srcBefore=trimLines("""
+ class root:
+ def theMethod(self):
+ pass
+
+ class a(root):
+ def theMethod(self):
+ pass
+
+ class b(root):
+ pass
+
+ class TheClass(b):
+ def theMethod(self):
+ pass
+ """)
+ srcAfter=trimLines("""
+ class root:
+ def newName(self):
+ pass
+
+ class a(root):
+ def newName(self):
+ pass
+
+ class b(root):
+ pass
+
+ class TheClass(b):
+ def newName(self):
+ pass
+ """)
+ src = self.rename(srcBefore,13,8,"newName")
+ self.assertEqual(srcAfter,src)
+
+
+ def test_renameMethodDoesntBarfOnNoneAsDefaultArgToMethod(self):
+ srcBefore=trimLines("""
+ class TheClass:
+ def theMethod(self, root, flist, stack=None):
+ pass
+ """)
+ src = self.rename(srcBefore,2,8,"newName")
+
+
+
+class RenameMethodTests_ImportsClass:
+ def test_renamesMethodOnDerivedClassInstance(self):
+ srcBefore = trimLines("""
+ from b.bah import TheClass as BaseClass
+
+ class DerivedClass(BaseClass):
+ pass
+
+ class DerivedDerivedClass(DerivedClass):
+ def theMethod(self):
+ print 'hello'
+ """)
+ srcAfter = trimLines("""
+ from b.bah import TheClass as BaseClass
+
+ class DerivedClass(BaseClass):
+ pass
+
+ class DerivedDerivedClass(DerivedClass):
+ def newName(self):
+ print 'hello'
+ """)
+ src = self.renameMethod(srcBefore, 2,8, "newName")
+ self.assertEqual(srcAfter,src)
+
+class RenameMethodReferenceTests:
+ # Generic tests. These tests are designed to be run in the context of a ui
+ # and in a package hierarchy structure
+
+ def test_doesntBarfWhenConfrontedWithComplexReturnTypes(self):
+ src = trimLines("""
+ import a
+ class TheClass:
+ def theMethod(self):
+ pass
+
+ def bah():
+ return a[35]
+
+ b = bah()
+ b.theMethod()
+ """)
+ self.rename(src,3,8,"newName")
+
+ def test_doesntbarfWhenCallMadeOnInstanceReturnedFromFnCall(self):
+ srcBefore=trimLines("""
+ from foo import e
+ class TheClass:
+ def theMethod(self):
+ pass
+ ast = e().f(src)
+ """)
+ self.rename(srcBefore,3,8,"newName")
+
+ def test_doesntStackOverflowOnRecursiveFunctions(self):
+ srcBefore=trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+
+ def foo(a):
+ return foo(a)
+ """)
+ self.rename(srcBefore,2,8,"newName")
+
+ def test_renamesMethodReferenceOfInstanceCreatedInParentScopeAfterFunction(self):
+ srcBefore=trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ a = TheClass()
+ def foo():
+ a.theMethod()
+ """)
+ srcAfter=trimLines("""
+ class TheClass:
+ def newName(self):
+ pass
+ a = TheClass()
+ def foo():
+ a.newName()
+ """)
+ src = self.rename(srcBefore,2,8,"newName")
+ self.assertEqual(srcAfter,src)
+
+ def test_renamesMethodReferenceOfInstanceObtainedByCallingFunction(self):
+ srcBefore=trimLines("""
+ class TheClass:
+ def theMethod():
+ pass
+ def foo():
+ b = TheClass()
+ return b
+ a = foo()
+ a.theMethod()
+ """)
+ srcAfter=trimLines("""
+ class TheClass:
+ def newName():
+ pass
+ def foo():
+ b = TheClass()
+ return b
+ a = foo()
+ a.newName()
+ """)
+ src = self.rename(srcBefore,2,8,"newName")
+ self.assertEqual(srcAfter,src)
+
+ def test_renamesMethodReferenceOfInstanceCreatedInAnotherFunction(self):
+
+ srcBefore=trimLines("""
+ class TheClass:
+ def theMethod():
+ pass
+ def bah():
+ return TheClass()
+ def foo():
+ a = bah()
+ a.theMethod()
+ """)
+ srcAfter=trimLines("""
+ class TheClass:
+ def newName():
+ pass
+ def bah():
+ return TheClass()
+ def foo():
+ a = bah()
+ a.newName()
+ """)
+ src = self.rename(srcBefore,2,8,"newName")
+ self.assertEqual(srcAfter,src)
+
+ def test_renamesMethodReferenceOfInstanceCreatedInSubsequentFunction(self):
+ srcBefore = trimLines("""
+ class TheClass:
+ def theMethod():
+ pass
+ class NotTheClass:
+ def theMethod():
+ pass
+
+ def foo():
+ a = bah()
+ a.theMethod()
+
+ def bah():
+ return TheClass()
+ """)
+ srcAfter=trimLines("""
+ class TheClass:
+ def newName():
+ pass
+ class NotTheClass:
+ def theMethod():
+ pass
+
+ def foo():
+ a = bah()
+ a.newName()
+
+ def bah():
+ return TheClass()
+ """)
+ src = self.rename(srcBefore,2,8,"newName")
+ self.assertEqual(srcAfter,src)
+
+ def test_renamesMethodReferenceOnInstanceThatIsAnAttributeOfSelf(self):
+ srcBefore = trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+
+ class AnotherClass:
+ def __init__(self):
+ self.a = TheClass()
+ def anotherFn(self):
+ self.a.theMethod()
+ """)
+ srcAfter=trimLines("""
+ class TheClass:
+ def newName(self):
+ pass
+
+ class AnotherClass:
+ def __init__(self):
+ self.a = TheClass()
+ def anotherFn(self):
+ self.a.newName()
+ """)
+ src = self.rename(srcBefore,2,8,"newName")
+ self.assertEqual(srcAfter,src)
+
+ def test_doesntBarfOnGetattrThatItCantDeduceTypeOf(self):
+ srcBefore=trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ a = TheClass
+
+ a.b.bah = 3
+ """)
+ self.rename(srcBefore,2,8,"newName")
+
+
+class RenameMethodReferenceTests_ImportsClass:
+
+ def test_renamesReferenceOfClassImportedAsAnotherName(self):
+ srcBefore=trimLines("""
+ from b.bah import TheClass as MyTheClass
+ def foo():
+ a = MyTheClass()
+ a.theMethod()
+ """)
+ srcAfter=trimLines("""
+ from b.bah import TheClass as MyTheClass
+ def foo():
+ a = MyTheClass()
+ a.newName()
+ """)
+ src = self.renameMethod(srcBefore,2,8, "newName")
+ self.assertEqual(srcAfter,src)
+
+ def test_renamesReferenceWhenObjectCreationAndReferenceInModuleScope(self):
+ srcBefore=trimLines("""
+ from b.bah import TheClass
+ a = TheClass()
+ a.theMethod()
+ """)
+ srcAfter=trimLines("""
+ from b.bah import TheClass
+ a = TheClass()
+ a.newName()
+ """)
+ src = self.renameMethod(srcBefore, 2,8, "newName")
+ self.assertEqual(srcAfter,src)
+
+
+ def test_renamesReferenceWhenObjectCreatedInSameFunctionAsReference(self):
+ srcBefore=trimLines("""
+ import b.bah
+ def foo():
+ a = b.bah.TheClass()
+ a.theMethod()
+ """)
+ srcAfter=trimLines("""
+ import b.bah
+ def foo():
+ a = b.bah.TheClass()
+ a.newName()
+ """)
+ src = self.renameMethod(srcBefore, 2,8, "newName")
+ self.assertEqual(srcAfter,src)
+
+ def test_doesntrenameDifferentMethodReferenceWhenObjectCreatedInSameScope(self):
+ srcBefore=trimLines("""
+ import b.bah.TheClass
+ def foo():
+ a = b.bah.TheClass()
+ a.theMethod()
+ """)
+ src = self.renameMethod(srcBefore, 4,8, "newName")
+ self.assertEqual(srcBefore,src)
+
+ def test_doesntrenameMethodReferenceWhenDifferentObjectCreatedInSameScope(self):
+ srcBefore=trimLines("""
+ import b.bah.TheClass
+ def foo():
+ a = b.bah.TheClass()
+ a.theMethod()
+ """)
+ src = self.renameMethod(srcBefore, 8,8,"newName")
+ self.assertEqual(srcBefore,src)
+
+ def test_renamesReferenceOfImportedClass(self):
+ srcBefore=trimLines("""
+ import b.bah
+
+ def foo():
+ a = b.bah.TheClass()
+ a.theMethod()
+ """)
+ srcAfter=trimLines("""
+ import b.bah
+
+ def foo():
+ a = b.bah.TheClass()
+ a.newName()
+ """)
+ src = self.renameMethod(srcBefore, 2,8, "newName")
+ self.assertEqual(srcAfter,src)
+
+ def test_doesntRenameReferenceOfDifferentImportedClass(self):
+ srcBefore=trimLines("""
+ from b.bah import DifferentClass
+
+ def foo():
+ a = b.bah.TheClass()
+ a.theMethod()
+ """)
+ src = self.renameMethod(srcBefore, 8,8,
+ "newName")
+ self.assertEqual(srcBefore,src)
+
+ def test_renamesReferenceOfClassImportedWithFromClause(self):
+ srcBefore=trimLines("""
+ from b.bah import TheClass
+
+ def foo():
+ a = TheClass()
+ a.theMethod()
+ """)
+ srcAfter=trimLines("""
+ from b.bah import TheClass
+
+ def foo():
+ a = TheClass()
+ a.newName()
+ """)
+ src = self.renameMethod(srcBefore, 2,8, "newName")
+ self.assertEqual(srcAfter,src)
+
+ def test_doesntrenameReferenceOfClassImportedWithDifferentAsClause(self):
+ srcBefore = trimLines("""
+ from b.bah import TheClass as MyClass
+
+ def foo():
+ a = TheClass()
+ a.theMethod()
+ """)
+
+ src = self.renameMethod(srcBefore, 2,8, "newName")
+ self.assertEqual(srcBefore,src)
+
+ def test_renamesReferenceOfClassImportedWithFromFooImportStar(self):
+ srcBefore=trimLines("""
+ from b.bah import *
+ a = TheClass()
+ a.theMethod()
+ """)
+ srcAfter=trimLines("""
+ from b.bah import *
+ a = TheClass()
+ a.newName()
+ """)
+ src = self.renameMethod(srcBefore, 2,8, "newName")
+ self.assertEqual(srcAfter,src)
+
+ def test_renamesMethodReferenceOfInstanceCreatedInParentScope(self):
+ srcBefore=trimLines("""
+ from b.bah import TheClass
+ a = TheClass()
+ def foo():
+ a.theMethod()
+ """)
+ srcAfter=trimLines("""
+ from b.bah import TheClass
+ a = TheClass()
+ def foo():
+ a.newName()
+ """)
+ src = self.renameMethod(srcBefore, 2,8, "newName")
+ self.assertEqual(srcAfter,src)
+
+ def test_doesntRenameMethodWhenObjectCreatedInChildScopeToMethodReference(self):
+ srcBefore = trimLines("""
+ from b.bah import TheClass
+ a = AnotherClass()
+ def foo():
+ a = TheClass()
+ a.theMethod()
+ """)
+ src = self.renameMethod(srcBefore, 2,8, "newName")
+ self.assertEqual(srcBefore,src)
+
+ def test_renamesReferenceOnDerivedClassInstance(self):
+ srcBefore=trimLines("""
+ import b
+ class DerivedClass(b.bah.TheClass):
+ pass
+ class DerivedDerivedClass(DerivedClass):
+ pass
+ theInstance = DerivedDerivedClass()
+ theInstance.theMethod()
+ """)
+ srcAfter=trimLines("""
+ import b
+ class DerivedClass(b.bah.TheClass):
+ pass
+ class DerivedDerivedClass(DerivedClass):
+ pass
+ theInstance = DerivedDerivedClass()
+ theInstance.newName()
+ """)
+ src = self.renameMethod(srcBefore, 2,8, "newName")
+ self.assertEqual(srcAfter,src)
+
+
+
+# tests that cover stuff not renamed automatically
+# (I.e. are renamed after user manually expresses desire to do so)
+class RenameMethodAfterPromptTests:
+ def test_renamesReferenceWhenMethodCallDoneOnInstanceCreation(self):
+
+ srcBefore=trimLines("""
+ class TheClass:
+ def theMethod(self): pass
+ TheClass().theMethod()
+ """)
+ srcAfter=trimLines("""
+ class TheClass:
+ def newName(self): pass
+ TheClass().newName()
+ """)
+ src = self.renameMethod(srcBefore,2,8, "newName")
+ self.assertEqual(srcAfter,src)
+
+
+ def test_renamesReferenceInMiddleOfBiggerCompoundCall(self):
+ srcBefore = trimLines("""
+ class TheClass:
+ def theMethod(self): return AnotherClass()
+ TheClass().theMethod().anotherMethod()
+ """)
+ srcAfter=trimLines("""
+ class TheClass:
+ def newName(self): return AnotherClass()
+ TheClass().newName().anotherMethod()
+ """)
+ src = self.renameMethod(srcBefore, 2,8, "newName")
+ self.assertEqual(srcAfter,src)
+
+
+class TestRenameMethodWithSingleModule(BRMTestCase, RenameMethodTests, RenameMethodReferenceTests):
+ # template method
+ def rename(self, src, line, col, newname):
+ try:
+ createPackageStructure(src, "pass")
+ rename(pkgstructureFile1,line,col,newname)
+ save()
+ return file(pkgstructureFile1).read()
+ finally:
+ removePackageStructure()
+
+
+class TestRenameMethodWithDirectoryStructure(RenameMethodTests, RenameMethodReferenceTests, BRMTestCase):
+
+ def rename(self, src, line, col, newname):
+ try:
+ createPackageStructure("pass",src)
+ rename(pkgstructureFile2,line,col,newname)
+ save()
+ return file(pkgstructureFile2).read()
+ finally:
+ removePackageStructure()
+
+
+class TestRenameMethodReferenceWithDirectoryStructure(BRMTestCase, RenameMethodTests_ImportsClass, RenameMethodReferenceTests_ImportsClass):
+
+ def renameMethod(self, src, line, col, newname):
+ try:
+ createPackageStructure(src,MethodTestdata)
+ rename(pkgstructureFile2,line,col,newname)
+ save()
+ return file(pkgstructureFile1).read()
+ finally:
+ removePackageStructure()
+
+class TestRenameMethodStuffCorrectlyAfterPromptReturnsTrue(BRMTestCase,
+ RenameMethodAfterPromptTests):
+
+ def callback(self, filename, line, colbegin, colend):
+ return 1
+
+
+ def renameMethod(self, src, line, col, newname):
+ createPackageStructure(src, MethodTestdata)
+ rename(pkgstructureFile1,line,col,newname,self.callback)
+ save()
+ return file(pkgstructureFile1).read()
+
+
+
+class TestDoesntRenameMethodIfPromptReturnsFalse(BRMTestCase):
+ def callback(self, filename, line, colbegin, colend):
+ return 0
+
+ def renameMethod(self, src, line, col, newname):
+ createPackageStructure(src, MethodTestdata)
+ rename(pkgstructureFile1,line,col,newname,self.callback)
+ save()
+ return file(pkgstructureFile1).read()
+
+ def test_doesntRenameMethodIfPromptReturnsFalse(self):
+ srcBefore = trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ b = TheClass()
+ b.theMethod()
+ a = someFunction()
+ a.theMethod()
+ """)
+ srcAfter=trimLines("""
+ class TheClass:
+ def newName(self):
+ pass
+ b = TheClass()
+ b.newName()
+ a = someFunction()
+ a.theMethod()
+ """)
+ src = self.renameMethod(srcBefore, 2,8, "newName")
+ self.assertEqual(srcAfter,src)
+
+
+MethodTestdata = trimLines("""
+class TheClass:
+ def theMethod(self):
+ pass
+ def differentMethod(self):
+ pass
+
+class DifferentClass:
+ def theMethod(self):
+ pass
+""")
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/refactor/testall.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,13 @@
+#!/usr/bin/env python
+import setpath
+from test_renameMethod import *
+from test_renameClass import *
+from test_renameFunction import *
+from test_rename import *
+from test_extractMethod import *
+from test_inlineVariable import *
+from test_extractVariable import *
+from test_moveToModule import *
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/refactor/utils.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,24 @@
+import re
+
+def getLineSeperator(line):
+ if line.endswith("\r\n"):
+ linesep = "\r\n" # windoze
+ else:
+ linesep = line[-1] # mac or unix
+ return linesep
+
+
+def getTabWidthOfLine(line):
+ match = re.match("\s+",line)
+ if match is None:
+ return 0
+ else:
+ return match.end(0)
+
+def reverseCoordsIfWrongWayRound(startcoords,endcoords):
+ if(startcoords.line > endcoords.line) or \
+ (startcoords.line == endcoords.line and \
+ startcoords.column > endcoords.column):
+ return endcoords,startcoords
+ else:
+ return startcoords,endcoords
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/setpath.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,5 @@
+import sys,os
+if not os.path.abspath("..") in sys.path:
+ from bike import log
+ print >> log.warning, "Appending to the system path. This should only happen in unit tests"
+ sys.path.append(os.path.abspath(".."))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/test_bikefacade.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,377 @@
+#!/usr/bin/env python
+import unittest
+import setpath
+import sys
+
+from bike import testdata
+from bike.testutils import *
+import bike
+from bike.refactor.test_renameFunction import RenameFunctionTests, RenameFunctionTests_importsFunction, FunctionTestdata
+from bike.refactor.test_renameClass import RenameClassTests, RenameClassTests_importsClass, TheClassTestdata
+from bike.refactor.test_renameMethod import RenameMethodTests, RenameMethodTests_ImportsClass, RenameMethodReferenceTests, RenameMethodReferenceTests_ImportsClass, RenameMethodAfterPromptTests, TestDoesntRenameMethodIfPromptReturnsFalse,MethodTestdata
+from bike.refactor import test_extractMethod
+import bikefacade
+from bike import UndoStackEmptyException
+from bike.query.getTypeOf import getTypeOf
+
+class TestPathFunctions(BRMTestCase):
+ def test_setCompletePythonPath_removesDuplicates(self):
+ origpath = sys.path
+ try:
+ sys.path = ["foobah"]
+ ctx = bike.init()
+ ctx._setCompletePythonPath(sys.path[-1])
+ self.assertEqual(1,ctx._getCurrentSearchPath().count(sys.path[-1]))
+ finally:
+ sys.path = origpath
+
+
+ def test_setNonLibPathonPath_removesLibDirectories(self):
+ origpath = sys.path
+ try:
+ writeTmpTestFile("pass")
+ libdir = os.path.join(sys.prefix,"lib","python"+sys.version[:3])
+ sys.path = [libdir,os.path.join(libdir,"site-packages")]
+ ctx = bike.init()
+ ctx._setNonLibPythonPath(tmproot)
+ self.assertEqual([tmproot],ctx._getCurrentSearchPath())
+ finally:
+ sys.path = origpath
+
+class TestRenameMethodAfterPrompt(BRMTestCase,RenameMethodAfterPromptTests):
+ def callback(self, filename, line, colstart, colend):
+ return 1
+
+ def renameMethod(self, src, line, col, newname):
+ writeTmpTestFile(src)
+ ctx = bike.init()
+ ctx.setRenameMethodPromptCallback(self.callback)
+ ctx.renameByCoordinates(tmpfile,line,col,newname)
+ ctx.save()
+ newsrc = readFile(tmpfile)
+ return newsrc
+
+class TestDoesntRenameMethodIfPromptReturnsFalse(TestDoesntRenameMethodIfPromptReturnsFalse):
+
+ def callback(self, filename, line, colstart, colend):
+ return 0
+
+ def renameMethod(self, src, line, col, newname):
+ writeTmpTestFile(src)
+ ctx = bike.init()
+ ctx.setRenameMethodPromptCallback(self.callback)
+ ctx.renameByCoordinates(tmpfile,line,col,newname)
+ ctx.save()
+ newsrc = readFile(tmpfile)
+ return newsrc
+
+
+class TestRenameByCoordinates2(RenameMethodTests,RenameMethodReferenceTests, RenameClassTests,RenameFunctionTests,BRMTestCase):
+ def rename(self, src, line, col, newname):
+ writeTmpTestFile(src)
+ ctx = bike.init()
+ ctx.renameByCoordinates(os.path.abspath(tmpfile),line,col,newname)
+ ctx.save()
+ newsrc = readFile(tmpfile)
+ return newsrc
+
+
+class TestRenameByCoordinatesWithDirectoryStructure(
+ RenameClassTests_importsClass,
+ RenameFunctionTests_importsFunction,
+ RenameMethodTests_ImportsClass,
+ RenameMethodReferenceTests_ImportsClass,
+ BRMTestCase):
+ def renameClass(self, src, newname):
+ try:
+ createPackageStructure(src, TheClassTestdata)
+ ctx = bike.init()
+ ctx.renameByCoordinates(pkgstructureFile2,1,6,newname)
+ ctx.save()
+ newsrc = readFile(pkgstructureFile1)
+ return newsrc
+ finally:
+ removePackageStructure()
+
+
+ def renameMethod(self, src, line, col, newname):
+ try:
+ createPackageStructure(src, MethodTestdata)
+ ctx = bike.init()
+ ctx.renameByCoordinates(pkgstructureFile2,line,col,newname)
+ ctx.save()
+ newsrc = readFile(pkgstructureFile1)
+ return newsrc
+ finally:
+ removePackageStructure()
+
+ def renameFunction(self, src, newname):
+ try:
+ createPackageStructure(src, FunctionTestdata)
+ ctx = bike.init()
+ ctx.renameByCoordinates(pkgstructureFile2,1,4,newname)
+ ctx.save()
+ newsrc = readFile(pkgstructureFile1)
+ return newsrc
+ finally:
+ removePackageStructure()
+
+
+
+class Test_deducePackageOfFile(BRMTestCase):
+ def test_returnsEmptyStringIfFileNotInPackage(self):
+ try:
+ # this doesnt have __init__.py file, so
+ # isnt package
+ os.makedirs("a")
+ writeFile(os.path.join("a","foo.py"),"pass")
+ pkg = bikefacade._deducePackageOfFile(os.path.join("a","foo.py"))
+ assert pkg == ""
+ finally:
+ os.remove(os.path.join("a","foo.py"))
+ os.removedirs(os.path.join("a"))
+
+ def test_returnsNestedPackage(self):
+ try:
+ os.makedirs(os.path.join("a","b"))
+ writeFile(os.path.join("a","__init__.py"),"# ")
+ writeFile(os.path.join("a","b","__init__.py"),"# ")
+ writeFile(os.path.join("a","b","foo.py"),"pass")
+ pkg = bikefacade._deducePackageOfFile(os.path.join("a","b","foo.py"))
+ assert pkg == "a.b"
+ finally:
+ os.remove(os.path.join("a","__init__.py"))
+ os.remove(os.path.join("a","b","__init__.py"))
+ os.remove(os.path.join("a","b","foo.py"))
+ os.removedirs(os.path.join("a","b"))
+
+
+class TestExtractMethod(test_extractMethod.TestExtractMethod):
+
+ def test_extractsPass(self):
+ srcBefore=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ pass
+ """)
+
+ srcAfter=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ self.newMethod()
+
+ def newMethod(self):
+ pass
+ """)
+
+ writeTmpTestFile(srcBefore)
+ ctx = bike.init()
+ ctx.extractMethod(os.path.abspath(tmpfile),3,8,3,12,"newMethod")
+ ctx.save()
+ self.assertEqual(readTmpTestFile(),srcAfter)
+ ctx.undo()
+ ctx.save()
+ self.assertEqual(readTmpTestFile(),srcBefore)
+
+
+class TestExtractFunction(test_extractMethod.TestExtractFunction):
+ def test_extractsFunction(self):
+ srcBefore=trimLines("""
+ def myFunction(): # comment
+ a = 3
+ c = a + 99
+ b = c * 1
+ print b
+ """)
+ srcAfter=trimLines("""
+ def myFunction(): # comment
+ a = 3
+ b = newFunction(a)
+ print b
+
+ def newFunction(a):
+ c = a + 99
+ b = c * 1
+ return b
+ """)
+ writeTmpTestFile(srcBefore)
+ ctx = bike.init()
+ ctx.extractMethod(os.path.abspath(tmpfile),3,4,4,13,"newFunction")
+ ctx.save()
+ self.assertEqual(readTmpTestFile(),srcAfter)
+ ctx.undo()
+ ctx.save()
+ self.assertEqual(readTmpTestFile(),srcBefore)
+
+
+class TestUndo(BRMTestCase):
+
+ def test_undoesTheTextOfASingleFile(self):
+ src = trimLines("""
+ class a:
+ def foo(self):
+ pass
+ """)
+ writeTmpTestFile(src)
+ #ctx = bike.init()
+ ctx = bike.init()
+
+ ctx.renameByCoordinates(tmpfile,2,8,"c")
+ ctx.save()
+ ctx.undo()
+ ctx.save()
+ newsrc = readFile(tmpfile)
+ self.assertEqual(newsrc,src)
+
+
+ def test_undoesTwoConsecutiveRefactorings(self):
+ try:
+ src = trimLines("""
+ class a:
+ def foo(self):
+ pass
+ """)
+ writeTmpTestFile(src)
+ ctx = bike.init()
+ ctx.renameByCoordinates(tmpfile,2,8,"c")
+ ctx.save()
+
+ newsrc1 = readFile(tmpfile)
+
+ ctx.renameByCoordinates(tmpfile,2,8,"d")
+ ctx.save()
+
+
+ # 1st undo
+ ctx.undo()
+ ctx.save()
+ newsrc = readFile(tmpfile)
+ self.assertEqual(newsrc,
+ newsrc1)
+
+ # 2nd undo
+ ctx.undo()
+ ctx.save()
+ newsrc = readFile(tmpfile)
+ self.assertEqual(newsrc,src)
+ finally:
+ pass
+ #deleteTmpTestFile()
+
+
+ def test_undoesTheTextOfAFileTwice(self):
+ for i in range(3):
+ src = trimLines("""
+ class foo:
+ def bah(self):
+ pass
+ """)
+ writeTmpTestFile(src)
+ ctx = bike.init()
+ ctx.renameByCoordinates(tmpfile,2,8,"c")
+ ctx.save()
+ ctx.undo()
+ ctx.save()
+ newsrc = readFile(tmpfile)
+ self.assertEqual(newsrc,src)
+ raisedexception=0
+ try:
+ ctx.undo()
+ except UndoStackEmptyException:
+ pass
+ else:
+ assert 0,"should have raised an exception"
+
+ '''
+ def test_undoesManualModificationsToFiles(self):
+ writeTmpTestFile("class foo: pass")
+ origsrc = readFile(tmpfile)
+ ctx = bike.init()
+
+ writeTmpTestFile("pass")
+ import os
+ ctx.init()
+ newsrc = readFile(tmpfile)
+ assert newsrc != origsrc
+ ctx.undo()
+ ctx.save()
+ newsrc = readFile(tmpfile)
+ assert newsrc == origsrc
+ '''
+
+class TestGetReferencesToClass_Facade(BRMTestCase):
+ def test_returnsReferences(self):
+ src = trimLines("""
+ class TheClass:
+ pass
+ a = TheClass()
+ """)
+ writeTmpTestFile(src)
+ ctx = bike.init()
+ refs = [refs for refs in ctx.findReferencesByCoordinates(tmpfile,1,6)]
+ self.assertEqual(refs[0].filename,os.path.abspath(tmpfile))
+ self.assertEqual(refs[0].lineno,3)
+ assert hasattr(refs[0],"confidence")
+
+
+class TestFindDefinitionByCoordinates(BRMTestCase):
+ def test_findsClassRef(self):
+ src=trimLines("""
+ class TheClass:
+ pass
+ a = TheClass()
+ """)
+ writeTmpTestFile(src)
+ ctx = bike.init()
+ defn = [x for x in ctx.findDefinitionByCoordinates(tmpfile,3,6)]
+ assert defn[0].filename == os.path.abspath(tmpfile)
+ assert defn[0].lineno == 1
+ assert defn[0].confidence == 100
+
+class TestBRM_InlineLocalVariable(BRMTestCase):
+ def test_works(self):
+ srcBefore=trimLines("""
+ def foo():
+ b = 'hello'
+ print b
+ """)
+ srcAfter=trimLines("""
+ def foo():
+ print 'hello'
+ """)
+
+ writeTmpTestFile(srcBefore)
+ ctx = bike.init()
+ ctx.inlineLocalVariable(tmpfile,3,10)
+ ctx.save()
+ self.assertEqual(file(tmpfile).read(),srcAfter)
+
+
+class TestBRM_ExtractLocalVariable(BRMTestCase):
+ def test_works(self):
+ srcBefore=trimLines("""
+ def foo():
+ print 3 + 2
+ """)
+ srcAfter=trimLines("""
+ def foo():
+ a = 3 + 2
+ print a
+ """)
+ try:
+ writeTmpTestFile(srcBefore)
+ ctx = bike.init()
+ ctx.extractLocalVariable(tmpfile,2,10,2,15,'a')
+ ctx.save()
+ self.assertEqual(file(tmpfile).read(),srcAfter)
+ finally:
+ pass
+ #deleteTmpTestFile()
+
+
+
+
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/test_testutils.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,9 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+from testutils import*
+import testdata
+import sys
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/testall.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,7 @@
+#!/usr/bin/env python
+
+import setpath
+from test_bikefacade import *
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/testdata.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,17 @@
+TheClass = """
+class TheClass:
+ def theMethod(self):
+ pass
+ def differentMethod(self):
+ pass
+
+class DifferentClass:
+ def theMethod(self):
+ pass
+"""
+
+
+Function = """
+def theFunction():
+ pass
+"""
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/testutils.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,170 @@
+from bike.globals import *
+import unittest
+import os
+import os.path
+from mock import Mock
+from bike.parsing.fastparserast import getRoot, Root, resetRoot
+from parsing.utils import fqn_rcar, fqn_rcdr
+import re
+from bike import log
+filesToDelete = None
+dirsToDelete = None
+
+class BRMTestCase(unittest.TestCase):
+ def setUp(self):
+ log.warning = log.SilentLogger()
+ try: os.makedirs(tmproot)
+ except: pass
+ os.chdir(tmproot)
+
+ resetRoot(Root([tmproot]))
+ getRoot().unittestmode = True
+ global filesToDelete
+ global dirsToDelete
+ filesToDelete = []
+ dirsToDelete = []
+ from bike.parsing.load import Cache
+ Cache.instance.reset()
+
+
+
+ def tearDown(self):
+ global filesToDelete
+ global dirsToDelete
+
+ for path in filesToDelete:
+ try: os.remove(path)
+ except: pass
+ filesToDelete = []
+
+ for path in dirsToDelete:
+ try: os.removedirs(path)
+ except: pass
+ dirsToDelete = []
+
+ os.chdir("..")
+ try: os.removedirs(tmproot)
+ except: pass
+
+
+
+tmproot = os.path.abspath("tmproot")
+tmpfile = os.path.join(tmproot, "bicyclerepairman_tmp_testfile.py")
+tmpmodule = "bicyclerepairman_tmp_testfile"
+
+
+def writeFile(filename, src):
+ f = open(filename, "w+")
+ f.write(src)
+ f.close()
+ filesToDelete.append(filename)
+
+def readFile(filename):
+ f = open(filename)
+ src = f.read()
+ f.close()
+ return src
+
+def writeTmpTestFile(src):
+ try:
+ os.makedirs(tmproot)
+ except OSError:
+ pass
+ writeFile(tmpfile, src)
+
+def readTmpTestFile():
+ return readFile(tmpfile)
+
+def deleteTmpTestFile():
+ os.remove(tmpfile)
+ os.removedirs(tmproot)
+
+
+pkgstructureRootDir = tmproot
+pkgstructureBasedir = os.path.join(pkgstructureRootDir, "a")
+pkgstructureChilddir = os.path.join(pkgstructureBasedir, "b")
+pkgstructureFile0 = os.path.join(pkgstructureRootDir, "top.py")
+pkgstructureFile1 = os.path.join(pkgstructureBasedir, "foo.py")
+pkgstructureFile2 = os.path.join(pkgstructureChilddir, "bah.py")
+
+
+def createPackageStructure(src1, src2, src0="pass"):
+ try: os.makedirs(pkgstructureChilddir)
+ except: pass
+ writeFile(os.path.join(pkgstructureBasedir, "__init__.py"), "#")
+ writeFile(os.path.join(pkgstructureChilddir, "__init__.py"), "#")
+ writeFile(pkgstructureFile0, src0)
+ writeFile(pkgstructureFile1, src1)
+ writeFile(pkgstructureFile2, src2)
+
+def removePackageStructure():
+ os.remove(os.path.join(pkgstructureBasedir, "__init__.py"))
+ os.remove(os.path.join(pkgstructureChilddir, "__init__.py"))
+ os.remove(pkgstructureFile0)
+ os.remove(pkgstructureFile1)
+ os.remove(pkgstructureFile2)
+ os.removedirs(pkgstructureChilddir)
+
+
+pkgstructureBasedir2 = os.path.join(pkgstructureRootDir, "c")
+pkgstructureFile3 = os.path.join(pkgstructureBasedir2, "bing.py")
+
+def createSecondPackageStructure(src3):
+ try: os.makedirs(pkgstructureBasedir2)
+ except: pass
+ writeFile(os.path.join(pkgstructureBasedir2, "__init__.py"), "#")
+ writeFile(pkgstructureFile3, src3)
+
+def removeSecondPackageStructure():
+ os.remove(os.path.join(pkgstructureBasedir2, "__init__.py"))
+ os.remove(pkgstructureFile3)
+ os.removedirs(pkgstructureBasedir2)
+
+
+
+def createAST(src):
+ from bike.parsing.load import getSourceNode
+ writeFile(tmpfile,src)
+ return getSourceNode(tmpfile)
+
+
+def createSourceNodeAt(src, fqn):
+ modname = fqn_rcar(fqn)
+ packagefqn = fqn_rcdr(fqn)
+ dirpath = os.path.join(*packagefqn.split("."))
+ filepath = os.path.join(dirpath,modname+".py")
+ try: os.makedirs(dirpath)
+ except: pass
+ dirsToDelete.append(dirpath)
+
+ # add the __init__.py files
+ path = "."
+ for pathelem in packagefqn.split("."):
+ path = os.path.join(path,pathelem)
+ initfile = os.path.join(path,"__init__.py")
+ writeFile(initfile,"#")
+ filesToDelete.append(initfile)
+ writeFile(filepath,src)
+ filesToDelete.append(filepath)
+ return getRoot()
+
+
+# takes the leading whitespace out of a multi line comment.
+# means you can imbed """
+# text like
+# this
+# """
+# in your code, and it will come out
+#"""text like
+#this"""
+def trimLines(src):
+ lines = src.splitlines(1)[1:]
+ tabwidth = re.match("\s*",lines[0]).end(0)
+ newlines = []
+ for line in lines:
+ if line == "\n" or line == "\r\n":
+ newlines.append(line)
+ else:
+ newlines.append(line[tabwidth:])
+ return "".join(newlines)
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/transformer/WordRewriter.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,48 @@
+from bike.parsing.load import getSourceNode
+from bike.transformer.undo import getUndoStack
+from bike.transformer.save import queueFileToSave
+import re
+
+# This class maintains a set of changed lines to the original source
+# nodes. This is important because the act of changing a line messes
+# up the coordinates on which renames are done.
+# Commit writes the changes back to the source nodes
+class WordRewriter:
+ def __init__(self):
+ self.modifiedsrc = {}
+
+ def rewriteString(self, srcnode, lineno, colno, newname):
+ filename = srcnode.filename
+ if not self.modifiedsrc.has_key(filename):
+ getUndoStack().addSource(filename,srcnode.getSource())
+ self.modifiedsrc[filename] = {}
+ if not self.modifiedsrc[filename].has_key(lineno):
+ line = srcnode.getLines()[lineno-1]
+ self.modifiedsrc[filename][lineno] = self._lineToDict(line)
+ self.modifiedsrc[filename][lineno][colno] = newname
+
+
+ # writes all the changes back to the src nodes
+ def commit(self):
+ for filename in self.modifiedsrc.keys():
+ srcnode = getSourceNode(filename)
+ for lineno in self.modifiedsrc[filename]:
+ lines = srcnode.getLines()
+ lines[lineno-1] = self._dictToLine(self.modifiedsrc[filename][lineno])
+ queueFileToSave(filename,"".join(srcnode.getLines()))
+
+
+ # this function creates a dictionary with each word referenced by
+ # its column position in the original line
+ def _lineToDict(self, line):
+ words = re.split("(\w+)", line)
+ h = {};i = 0
+ for word in words:
+ h[i] = word
+ i+=len(word)
+ return h
+
+ def _dictToLine(self, d):
+ cols = d.keys()
+ cols.sort()
+ return "".join([d[colno]for colno in cols])
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/transformer/__init__.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,4 @@
+"""
+Package containing modules which transform the internal
+representation of the sourcecode.
+"""
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/transformer/save.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,36 @@
+from bike import log
+
+outputqueue = {}
+
+def getQueuedFile(filename):
+ try:
+ return outputqueue[filename]
+ except:
+ pass
+ #print "HERE!"
+
+
+def resetOutputQueue():
+ global outputqueue
+ outputqueue = {}
+
+def queueFileToSave(filename,src):
+ outputqueue[filename] = src
+ from bike.parsing.load import getSourceNode
+ getSourceNode(filename).resetWithSource(src)
+
+def save():
+ from bike.transformer.undo import getUndoStack
+
+ global outputqueue
+ savedFiles = []
+ for filename,src in outputqueue.iteritems():
+ print >> log.progress, "Writing:",filename
+ f = file(filename, "w+")
+ f.write(outputqueue[filename])
+ f.close()
+ savedFiles.append(filename)
+ outputqueue = {}
+ #print "stack is "+ str(getUndoStack().stack)
+ getUndoStack().commitUndoFrame()
+ return savedFiles
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/transformer/setpath.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,5 @@
+import sys,os
+if not os.path.abspath("..") in sys.path:
+ from bike import log
+ print >> log.warning, "Appending to the system path. This should only happen in unit tests"
+ sys.path.append(os.path.abspath(".."))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/transformer/testall.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,6 @@
+#!/usr/bin/env python
+
+#from test_undo import *
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/transformer/undo.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,50 @@
+from bike import log
+from bike.transformer.save import queueFileToSave
+
+_undoStack = None
+
+def getUndoStack(forceNewStack = 0):
+ global _undoStack
+ if _undoStack is None or forceNewStack:
+ _undoStack = UndoStack()
+ return _undoStack
+
+class UndoStackEmptyException: pass
+
+class UndoStack(object):
+ def __init__(self):
+ self.stack = []
+ self.stack.append({})
+ self.frame = self.stack[-1]
+ self.setUndoBufferSize(10)
+
+ def setUndoBufferSize(self, undoBufferSize):
+ self.undoBufferSize = undoBufferSize
+
+ def addSource(self, filename, src):
+ if filename not in self.frame:
+ self.frame[filename] = src
+
+ def commitUndoFrame(self):
+ #restrict size of buffer
+ while len(self.stack) > self.undoBufferSize:
+ #print "clipping undo stack"
+ del self.stack[0]
+
+ if len(self.frame) != 0:
+ #print "commitUndoFrame"
+ self.stack.append({})
+ self.frame = self.stack[-1]
+
+ def undo(self, **opts):
+ #print "undo called",self.stack
+ if len(self.stack) < 2:
+ raise UndoStackEmptyException()
+ undoframe = self.stack[-2]
+ #print "undoframe is",undoframe
+ for filename,src in undoframe.iteritems():
+ print >>log.progress, "Undoing:",filename
+ queueFileToSave(filename,src)
+ self.stack = self.stack[:-2]
+ self.stack.append({})
+ self.frame = self.stack[-1]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/ide-integration/BicycleRepairMan_Idle.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,422 @@
+# bicycle repair man idle extension
+import bike
+from bike.transformer.undo import UndoStackEmptyException
+import bike.parsing.load
+import os
+from Tkinter import *
+import tkFileDialog
+import tkMessageBox
+import tkSimpleDialog
+import sys
+import string
+try:
+ from idlelib.PathBrowser import *
+ from idlelib.WindowList import ListedToplevel
+ from idlelib.TreeWidget import TreeNode, TreeItem, ScrolledCanvas
+ from idlelib import EditorWindow
+ from idlelib.PyShell import PyShell
+ from idlelib.OutputWindow import OutputWindow
+ try:
+ from idlelib.configHandler import idleConf
+ except ImportError:
+ pass
+
+
+
+except ImportError:
+ from PathBrowser import*
+ from WindowList import ListedToplevel
+ from TreeWidget import TreeNode, TreeItem, ScrolledCanvas
+ import EditorWindow
+ from PyShell import PyShell
+ from OutputWindow import OutputWindow
+ try:
+ from configHandler import idleConf
+ except ImportError:
+ pass
+
+brmctx = None
+shellwin = None
+loadingFiles = 0
+matchwin = None
+
+class NotHighlightedException(Exception):
+ pass
+
+class BicycleRepairMan_Idle:
+ menudefs = [
+ ('bicycleRepairMan', [
+ ('----- Queries -----',''),
+ ('_Find References','<<brm-find-references>>'),
+ ('_Find Definition','<<brm-find-definition>>'),
+ None,
+ ('--- Refactoring ---',''),
+ ('_Rename', '<<brm-rename>>'),
+ ('_Extract Method', '<<brm-extract-method>>'),
+ None,
+ ('_Undo', '<<brm-undo>>'),
+ ])
+ ]
+
+ keydefs = {
+ '<<brm-find-references>>':[],
+ '<<brm-find-definition>>':[],
+ '<<brm-rename>>':[],
+ '<<brm-extract-method>>':[],
+ '<<brm-undo>>':[],
+ }
+
+
+ try:
+ TRACE = idleConf.GetOption('extensions','BicycleRepairMan_Idle',
+ 'trace',default=1)
+ except NameError: # hasnt imported idleconf - probably python 22
+ TRACE = 1
+
+
+ def __init__(self, editwin):
+ self.editwin = editwin
+
+ if self.TRACE == 1:
+ self.progressLogger = ProgressLogger(self.editwin.flist)
+
+ if not isinstance(editwin, PyShell):
+ # sly'ly add the refactor menu to the window
+ name, label = ("bicycleRepairMan", "_BicycleRepairMan")
+ underline, label = EditorWindow.prepstr(label)
+ mbar = editwin.menubar
+ editwin.menudict[name] = menu = Menu(mbar, name = name)
+ mbar.add_cascade(label = label, menu = menu, underline = underline)
+
+ # Initialize Bicyclerepairman and import the code
+ path = self.editwin.io.filename
+ if path is not None:
+ global brmctx
+ if brmctx is None:
+ self.initbrm()
+ else:
+ global shellwin
+ shellwin = editwin
+
+
+ def initbrm(self):
+ global brmctx
+ brmctx = bike.init()
+ if self.TRACE == 1:
+ brmctx.setProgressLogger(self.progressLogger)
+
+
+ def brm_find_references_event(self,event):
+ try:
+ if not self.confirm_all_buffers_saved():
+ return
+
+ if self.editwin.text.index("sel.first") == "":
+ self.errorbox("Not highlighted", "Highlight the name of a Function, Class or Method and try again")
+ return
+
+ filename = os.path.normpath(self.editwin.io.filename)
+ line, column = string.split(self.editwin.text.index("sel.first"),'.')
+
+ numMatches = 0
+ global matchwin
+ if matchwin is None:
+ matchwin = BRMMatchesWindow(self.editwin.flist, self)
+
+ matchwin.clear()
+
+ for ref in brmctx.findReferencesByCoordinates(filename,int(line),int(column)):
+ print >>matchwin, "File \""+ref.filename+"\", line "+str(ref.lineno)+", "+str(ref.confidence)+"% confidence"
+ numMatches +=1
+
+ print >>matchwin, numMatches," matches"
+ print >>matchwin, "(Hint: right-click to open locations.)"
+ except:
+ self._handleUnexpectedException()
+
+
+ def brm_find_definition_event(self,event):
+ try:
+ if not self.confirm_all_buffers_saved():
+ return
+ filename = os.path.normpath(self.editwin.io.filename)
+
+ if self.editwin.text.index("sel.first") != "":
+ line, column = string.split(self.editwin.text.index("sel.first"),'.')
+ else:
+ line, column = string.split(self.editwin.text.index("insert"), '.')
+
+
+ defns = brmctx.findDefinitionByCoordinates(filename,int(line),
+ int(column))
+
+ try:
+
+ firstref = defns.next()
+
+ editwin = self.editwin.flist.open(firstref.filename)
+ editwin.gotoline(firstref.lineno)
+ except StopIteration:
+ self.errorbox("Couldn't Find definition","Couldn't Find definition")
+ pass
+ else:
+ numRefs = 1
+ global matchwin
+ if matchwin is None:
+ matchwin = BRMMatchesWindow(self.editwin.flist, self)
+
+ for ref in defns:
+ if numRefs == 1:
+ print >>matchwin, firstref.filename+":"+str(firstref.lineno)+": "+str(firstref.confidence)+"% confidence"
+
+ numRefs += 1
+ print >>matchwin,ref.filename+":"+str(ref.lineno)+": "+str(ref.confidence)+"% confidence"
+ if matchwin is not None:
+ print >>matchwin, "(Hint: right-click to open locations.)"
+
+ except:
+ self._handleUnexpectedException()
+
+
+ def brm_rename_event(self, event):
+ try:
+ self.renameItemByCoordinates()
+ except:
+ self._handleUnexpectedException()
+
+ def brm_extract_method_event(self, event):
+ try:
+ if not self.confirm_all_buffers_saved():
+ return
+ try:
+ filename, newname, beginline, begincolumn, endline, endcolumn = self._getExtractionInformation("Method")
+ except NotHighlightedException:
+ return
+ brmctx.extractMethod(filename, int(beginline), int(begincolumn),
+ int(endline), int(endcolumn), newname)
+ savedfiles = brmctx.save()
+ self.refreshWindows(savedfiles, beginline)
+ except:
+ self._handleUnexpectedException()
+
+
+ def brm_undo_event(self, event):
+ try:
+ line, column = string.split(self.editwin.text.index("insert"), '.')
+ brmctx.undo()
+ savedfiles = brmctx.save()
+ self.refreshWindows(savedfiles, line)
+ except UndoStackEmptyException:
+ self.errorbox("Undo Stack Empty", "Undo Stack is empty")
+ except:
+ self._handleUnexpectedException()
+
+ def _handleUnexpectedException(self):
+ import traceback
+ traceback.print_exc()
+ self.errorbox("Caught Exception", "Caught Exception "+str(sys.exc_info()[0]))
+
+ def _getExtractionInformation(self, extracttype):
+ if self.editwin.text.index("sel.first") == "":
+ self.errorbox("Code not highlighted", "Highlight the region of code you want to extract and try again")
+ raise NotHighlightedException()
+ filename = os.path.normpath(self.editwin.io.filename)
+ newname = tkSimpleDialog.askstring("Extract Method ",
+ "New "+extracttype+" Name:",
+ parent = self.editwin.text)
+ beginline, begincolumn = string.split(self.editwin.text.index("sel.first"), '.')
+ endline, endcolumn = string.split(self.editwin.text.index("sel.last"), '.')
+ return filename, newname, beginline, begincolumn, endline, endcolumn
+
+
+ def renameMethodPromptCallback(self, filename, line, colbegin, colend):
+
+ editwin = self.editwin.flist.open(filename)
+ originaltop = self.editwin.getwindowlines()[0]
+
+ # select the method call and position the window
+ editwin.text.tag_remove("sel", "1.0", "end")
+ editwin.text.tag_add("sel", str(line)+"."+str(colbegin),
+ str(line)+"."+str(colend))
+
+ line, column = string.split(editwin.text.index("sel.first"), '.')
+ editwin.text.yview(str(int(line)-2)+".0")
+
+
+ d = NoFocusDialog("Rename?",
+ "Cannot deduce the type of highlighted object reference.\nRename this declaration?",
+ parent = editwin.text)
+
+ # put the window back where it was
+ self.editwin.text.yview(float(originaltop))
+ return d.answer
+
+ def renameItemByCoordinates(self):
+ if not self.confirm_all_buffers_saved():
+ return
+ if self.editwin.text.index("sel.first") == "":
+ self.errorbox("Name not highlighted", "Double click the name of the declaration you want to rename (to highlight it) and try again")
+ return
+
+ brmctx.setRenameMethodPromptCallback(self.renameMethodPromptCallback)
+ line, column = string.split(self.editwin.text.index("sel.first"), '.')
+ filename = os.path.normpath(self.editwin.io.filename)
+ newname = tkSimpleDialog.askstring("Rename",
+ "Rename to:",
+ parent = self.editwin.text)
+ if newname is None: # cancel clicked
+ return
+ brmctx.renameByCoordinates(filename, int(line), int(column), newname)
+ savedfiles = brmctx.save()
+ self.refreshWindows(savedfiles, line)
+
+
+
+ def refreshWindows(self, savedfiles, line):
+ # refresh editor windows
+ oldtop = self.editwin.getwindowlines()[0]
+
+ global loadingFiles
+ loadingFiles = 1
+ for sf in savedfiles:
+ normsf = os.path.normcase(sf)
+ if normsf in self.editwin.flist.dict:
+ editwin = self.editwin.flist.dict[normsf]
+ editwin.io.loadfile(sf)
+ loadingFiles = 0
+
+ self.editwin.text.mark_set("insert", float(line))
+ self.editwin.text.yview(float(oldtop))
+
+
+
+ def confirm_all_buffers_saved(self):
+ filelist = self.editwin.flist.dict.keys()
+ for f in filelist:
+ #editwin = self.editwin.flist.open(f)
+ editwin = self.editwin.flist.dict[f]
+ if self.confirm_buffer_is_saved(editwin) == 0:
+ return 0
+ return 1
+
+
+ def confirm_buffer_is_saved(self, editwin):
+ if not editwin.get_saved():
+ name = (editwin.short_title()or
+ editwin.long_title()or
+ "Untitled")
+ reply = tkMessageBox.askokcancel("Bicycle Repair Man",
+ "The buffer for %s is not saved.\n\n"%name+
+ "Save it and continue?",
+ master = self.editwin.text)
+ &nbs p; self.editwin.text.focus_set()
+ if reply:
+ editwin.io.save(None)
+ else:
+ return 0
+ return 1
+
+ def errorbox(self, title, message):
+ tkMessageBox.showerror(title, message, master = self.editwin.text)
+ self.editwin.text.focus_set()
+
+
+class BRMTraceWindow(OutputWindow):
+ def short_title(self):
+ return "BicycleRepairMan Trace"
+
+class ProgressLogger:
+ def __init__(self,flist):
+ self.flist = flist
+
+ def write(self,txt):
+ if not hasattr(self,"io"):
+ self.io = BRMTraceWindow(self.flist)
+ try:
+ self.io.write(txt)
+ self.io.flush()
+ except IOError:
+ pass
+
+
+class NoFocusDialog(tkSimpleDialog._QueryDialog):
+ def __init__(self, title, prompt,
+ initialvalue = None,
+ minvalue = None, maxvalue = None,
+ parent = None):
+ self.answer = 0
+
+ if not parent:
+ import Tkinter
+ parent = Tkinter._default_root
+
+ self.prompt = prompt
+ self.minvalue = minvalue
+ self.maxvalue = maxvalue
+
+ self.initialvalue = initialvalue
+
+ Toplevel.__init__(self, parent)
+
+ if title:
+ self.title(title)
+
+ self.parent = parent
+
+ self.result = None
+
+ body = Frame(self)
+ self.initial_focus = self.body(body)
+ body.pack(padx = 5, pady = 5)
+
+ self.buttonbox()
+
+ self.grab_set()
+
+ self.protocol("WM_DELETE_WINDOW", self.cancel)
+
+ if self.parent is not None:
+ self.geometry("+%d+%d"%(parent.winfo_rootx()+50,
+ parent.winfo_rooty()+50))
+ self.wait_window(self)
+
+
+ def getresult(self):
+ self.answer = 1
+
+ def body(self, master):
+ w = Label(master, text = self.prompt, justify = LEFT)
+ w.grid(row = 0, padx = 5, sticky = W)
+
+ def buttonbox(self):
+ box = Frame(self)
+
+ w = Button(box, text = "Yes", width = 10, command = self.ok, default = ACTIVE)
+ w.pack(side = LEFT, padx = 5, pady = 5)
+ w = Button(box, text = "No", width = 10, command = self.cancel)
+ w.pack(side = LEFT, padx = 5, pady = 5)
+
+ self.bind("<Return>", self.ok)
+ self.bind("<Escape>", self.cancel)
+ box.pack()
+
+
+
+
+
+class BRMMatchesWindow(OutputWindow):
+ def __init__(self,flist,masterwin):
+ OutputWindow.__init__(self,flist)
+ self.masterwin = masterwin
+
+ def close(self):
+ global matchwin
+ matchwin = None
+ OutputWindow.close(self)
+
+ def short_title(self):
+ return "BicycleRepairMan Matches"
+
+ def clear(self):
+ self.text.delete("1.0","end-1c")
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/ide-integration/Pymacs-0.20/ChangeLog Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,417 @@
+2002-11-23 François Pinard <pinard@iro.umontreal.ca>
+
+ * : Release 0.20.
+
+ * Pymacs/__init__.py: Integrate version.py.
+ * Pymacs/version.py: Deleted.
+ * setup.py, Pymacs/pymacs.py: Adjusted.
+
+2002-11-15 François Pinard <pinard@iro.umontreal.ca>
+
+ * pymacs.el (pymacs-python-reference): Handle when function is
+ defined as a mere variable, or when a function is being advised.
+
+2002-11-14 François Pinard <pinard@iro.umontreal.ca>
+
+ * : Release 0.19.
+
+ * Pymacs/pymacs.py (List.__getitem__): Raise IndexError when
+ out of bounds. This should allow for iterating over a list.
+
+ * README.html: New, merely a template for Webert.
+
+2002-11-13 François Pinard <pinard@iro.umontreal.ca>
+
+ * pymacs.el (pymacs-call): New. Use it whenever adequate.
+
+2002-09-26 François Pinard <pinard@iro.umontreal.ca>
+
+ * Makefile (publish): Revised.
+
+2002-08-18 François Pinard <pinard@iro.umontreal.ca>
+
+ * : Release 0.18.
+
+2002-08-09 François Pinard <pinard@iro.umontreal.ca>
+
+ * Pymacs/rebox.py (Emacs_Rebox.find_comment): Correctly spell
+ backward_char, not backward-char.
+
+2002-08-08 François Pinard <pinard@iro.umontreal.ca>
+
+ * Pymacs/rebox.py (pymacs_load_hook): Compute the interactions
+ map from the bound methods, instead of from the generic ones.
+
+2002-07-14 François Pinard <pinard@iro.umontreal.ca>
+
+ * Pymacs/pymacs.py (Lisp_Interface.__call__): Wrap argument in
+ progn, so lisp() could accept a sequence of expressions.
+
+2002-07-01 François Pinard <pinard@iro.umontreal.ca>
+
+ * pymacs.el (pymacs-start-services): Disable undo for *Pymacs*.
+
+2002-06-25 François Pinard <pinard@iro.umontreal.ca>
+
+ * : Release 0.17.
+
+ * pymacs.py: Deleted, this was the compatibility module.
+ * setup: Simplified to handle the Emacs Lisp part only.
+ Deleted -P, -p and -x, as well as compile_python.
+
+ * Makefile: Adjusted. Removed pythondir and pymacsdir.
+ * pymacs.el (pymacs-load-path): Merely preset to nil.
+
+ * setup: Changes for easing installation on Win32.
+ Reported by Syver Enstad.
+
+ * Pymacs/pymacs.py (print_lisp): Produce Emacs strings more
+ explicitly, avoiding hexadecimal sequences generated by Python
+ 2.2. Those hexadecimal sequences confused Emacs when immediately
+ followed by more hexadecimal looking characters.
+
+2002-01-30 François Pinard <pinard@iro.umontreal.ca>
+
+ * pymacs.el (pymacs-load-path): Initialise with pymacsdir.
+ * pymacs-services: Do not handle a patched pymacsdir anymore.
+ * setup (complete_install): Set pymacsdir for Lisp, not Python.
+ Do not accept a -b option anymore, do not install pymacs-services,
+ as this is now to be done through setup.py.
+ * Makefile (install): Do not use -b while calling setup.
+
+2002-01-29 François Pinard <pinard@iro.umontreal.ca>
+
+ * : Release 0.16.
+
+ * Pymacs/pymacs.py: New file, previously top-level.
+ * pymacs.py: Now a mere bootstrap for Pymacs/pymacs.py.
+ * Pymacs/__init__.py: Define lisp and Let.
+ * Makefile (pythondir): Documentation amended.
+ * setup: Distinguish between empty arguments, which ask for
+ autoconfiguration, and None arguments, which inhibit it.
+ * pymacs-services: Import pymacs from Pymacs.
+
+ * Pymacs/version.py: New file. Rename pymacs to Pymacs.
+ * setup, setup.py, Pymacs/pymacs.py (main): Use it.
+
+ * setup: Substitute None for pymacsdir instead of the empty string.
+ * pymacs-services: Adjusted.
+
+ * Pymacs/pymacs.py (Let): Have all push_* methods to return self.
+
+2002-01-20 François Pinard <pinard@iro.umontreal.ca>
+
+ * pymacs.el, pymacs.el: Replace LISP by Lisp in comments.
+ Reported by Paul Foley.
+
+2002-01-10 François Pinard <pinard@iro.umontreal.ca>
+
+ * : Release 0.15.
+
+ * pymacs.el (pymacs-start-services): Properly diagnose a timeout,
+ using the timeout parameter value instead of a fixed string.
+
+2002-01-07 François Pinard <pinard@iro.umontreal.ca>
+
+ * : Release 0.14.
+
+ * pymacs.py: Set various __repr__() to yield Python code,
+ containing the corresponding expanded LISP expression.
+ Set various __str__() to yield mutable LISP code.
+
+ * pymacs.py (Let): Point markers to nowhere once done with them.
+
+2002-01-06 François Pinard <pinard@iro.umontreal.ca>
+
+ * : Release 0.13.
+
+ * pymacs.el (pymacs-load): Imply prefix correctly when the module
+ is part of a package, that is, when its name has at least one dot.
+ * pymacs.py (pymacs_load_helper): Idem.
+
+ * pymacs.py (Protocol): New name for Server.
+
+ * pymacs.py (pymacs_load_helper): Implement pymacs_load_hook.
+
+ * MANIFEST.in, setup.py, Pymacs/__init__.py: New files.
+ * Makefile: Adjusted and simplified.
+
+2002-01-03 François Pinard <pinard@iro.umontreal.ca>
+
+ * pymacs.py (pymacs_load_helper): Handle module within package.
+ Reported by Syver Enstad.
+
+2001-12-18 François Pinard <pinard@iro.umontreal.ca>
+
+ * pymacs.bat: New file.
+
+2001-11-29 François Pinard <pinard@iro.umontreal.ca>
+
+ * : Release 0.12.
+
+ * pymacs.el (pymacs-timeout-at-start, pymacs-timeout-at-reply,
+ pymacs-timeout-at-line): New variables. Use them.
+
+2001-10-17 François Pinard <pinard@iro.umontreal.ca>
+
+ * pymacs.py (pymacs_load_helper): Check the function attribute
+ before the interactions dictionary, for people having Python 2.x.
+ Reported by Carel Fellinger.
+
+ * pymacs.el, pymacs.py, pymacs-services: Add the usual GPL notices.
+ Reported by Richard Stallman.
+
+2001-10-16 François Pinard <pinard@iro.umontreal.ca>
+
+ * : Release 0.11.
+
+ * pymacs.el (pymacs-defuns): Accept interaction specifications.
+ (pymacs-defun): Process an interaction specification.
+ (pymacs-python-reference): Adjust for interactive functions.
+ * pymacs.py (pymacs_load_helper): Transmit interaction specifications.
+ Reported by Christian Tanzer and Stefan Reichör.
+
+2001-10-15 François Pinard <pinard@iro.umontreal.ca>
+
+ * pymacs.py (pymacs_load_helper): Accept dashed module names.
+ Reported by Stefan Reichör.
+
+ * pymacs.el (pymacs-python-reference): Rewrite, as it was broken.
+ (documentation): Say it is a Python function, even if no docstring.
+ Reported by Stefan Reichör.
+
+2001-10-12 François Pinard <pinard@iro.umontreal.ca>
+
+ * : Release 0.10.
+
+ * pymacs.el (pymacs-print-for-eval): Handle multi-line strings.
+ Reported by Dave Sellars.
+
+ * pymacs.el (pymacs-print-for-eval): Remove string text properties.
+ Reported by Eli Zaretskii.
+
+2001-10-06 François Pinard <pinard@iro.umontreal.ca>
+
+ * pymacs.py (Let.__nonzero__): New.
+
+2001-09-28 François Pinard <pinard@iro.umontreal.ca>
+
+ * : Release 0.9.
+
+2001-09-26 François Pinard <pinard@iro.umontreal.ca>
+
+ * pymacs.py (Let.push): Save the value of the symbol, not the
+ symbol itself.
+
+2001-09-25 François Pinard <pinard@iro.umontreal.ca>
+
+ * : Release 0.8.
+
+ * pymacs.py (Let): New class.
+
+ * pymacs.el: New variable pymacs-use-hash-tables, set to t when
+ hash tables are available, or nil otherwise. Use it. This is so
+ older Emacs would work.
+ Reported by Dirk Vleugels.
+
+2001-09-21 François Pinard <pinard@iro.umontreal.ca>
+
+ * pymacs.el (pymacs-defun): Ensure the function is registered
+ at definition, not at call time. Otherwise, it would never be
+ garbage-collected if it is never called.
+
+2001-09-20 François Pinard <pinard@iro.umontreal.ca>
+
+ * : Release 0.7.
+
+ * pymacs.el (pymacs-print-for-apply): Also accept Python objects
+ for a function, instead of requiring strings.
+ (pymacs-defun): Use a Python object, not an explicit string reference.
+ (pymacs-python): Merge pymacs-save-index.
+ (pymacs-save-index): Deleted.
+
+2001-09-18 François Pinard <pinard@iro.umontreal.ca>
+
+ * pymacs.el (pymacs-load): Accept a noerror argument.
+
+2001-09-17 François Pinard <pinard@iro.umontreal.ca>
+
+ * setup: New script.
+ * Makefile: Use it.
+
+ * pymacs.py (Symbol.set): Make things simpler when value is None.
+
+ * pymacs.el (pymacs-print-for-eval): Use Python lists to represent
+ LISP proper lists and Python tuples to represent LISP vectors,
+ instead of the other way around.
+ * pymacs.py (pymacs_load_helper, print_lisp): Similar changes.
+ Reported by John Wiegley.
+
+2001-09-16 François Pinard <pinard@iro.umontreal.ca>
+
+ * : Release 0.6.
+
+ * pymacs.el (pymacs-start-services, pymacs-print-for-eval,
+ pymacs-round-trip): Protect match data.
+
+2001-09-15 François Pinard <pinard@iro.umontreal.ca>
+
+ * pymacs.el (documentation): Completed. Now into service.
+ (pymacs-documentation): Deleted.
+ (pymacs-python-reference): New.
+
+ * pymacs.el (pymacs-print-for-eval): Use car-safe.
+
+2001-09-14 François Pinard <pinard@iro.umontreal.ca>
+
+ * pymacs.el (pymacs-print-for-eval): replace-regexp-in-string does
+ not exist in older Emacs versions, so use paraphrases.
+ Reported by Carey Evans.
+
+ * pymacs.el (pymacs-start-services): Set pymacs-transit-buffer
+ permanently only at end of the function, in case anything fails.
+ Reported by Carey Evans.
+
+2001-09-13 François Pinard <pinard@iro.umontreal.ca>
+
+ * : Release 0.5.
+
+ * pymacs.el (documentation, pymacs-documentation): New, experimental.
+ * pymacs.py (doc_string): New.
+ (pymacs_load_helper): The result should evaluate to the module.
+
+2001-09-12 François Pinard <pinard@iro.umontreal.ca>
+
+ * pymacs.py (pymacs_load_helper): Use reload instead of __import__
+ whenever the module was already loaded.
+
+ * pymacs.py (pymacs_load_helper): Return t when there is nothing
+ to define, instead of returning a noisy pymacs-defuns noop.
+
+ * Makefile (dist): Update a version-less symbolic link.
+
+ * pymacs.el (pymacs-python, pymacs-defun): New functions.
+ (pymacs-defuns): Use pymacs-defun.
+ * pymacs.py (print_lisp): Use the above.
+
+ * pymacs.py (Server): Free all accumulated LISP indices, while
+ replying for another reason. This should decrease overhead.
+ (Lisp.__del__): Delay freeing LISP, do not free one index at a time.
+ * pymacs.el (pymacs-free-lisp): Free many indices at once.
+
+ * pymacs.el (pymacs-start-services, pymacs-round-trip): Recognise
+ reply even when not at beginning of line. The Python module may
+ print incomplete lines, unrelated to the communication protocol.
+
+2001-09-11 François Pinard <pinard@iro.umontreal.ca>
+
+ * : Release 0.4.
+
+ * pymacs.py (zombie): New, so to get a clear diagnostic.
+ (zombie_python): Link objects to the above function.
+ * pymacs.el (pymacs-terminate-services): Ask for confirmation if
+ any object in LISP space is still in use on the Python side.
+ * pymacs-test.el (try-lisp): Do not terminate the helper.
+
+ * pymacs.py (Buffer): New class, yet empty for now.
+ * pymacs.el (pymacs-print-for-eval): Use it.
+ Reported by Brian McErlean.
+
+ * pymacs.py (Table): New class.
+ * pymacs.el (pymacs-print-for-eval): Use it.
+ Reported by Brian McErlean.
+
+ * pymacs.py (List, Vector): New classes, split out of Lisp class.
+ * pymacs.el (pymacs-print-for-eval): Use them.
+ (pymacs-lisp-length, pymacs-lisp-ref, pymacs-list-set): Deleted.
+ Reported by Brian McErlean.
+
+ * pymacs.py (Server.loop): Allow keyboard interrupts through.
+
+ * pymacs.el: Use Lisp instead of Handle. Rename
+ pymacs-handle-length to pymacs-lisp-length, pymacs-handle-ref to
+ pymacs-lisp-ref, pymacs-handle-set o pymacs-lisp-set,
+ pymacs-allocate-handle to pymacs-allocate-lisp and
+ pymacs-free-handle to pymacs-free-lisp.
+
+ * pymacs.py: Rename Lisp to Lisp_Interface, and Handle to Lisp.
+ Adjust for other renamings above.
+
+ * pymacs.el: Rename pymacs-id to pymacs-python. Ajust for below.
+ * pymacs.py: Rename handles to python, free_handles to
+ free_python, zombie_handles to zombie_python and allocate_handle
+ to allocate_python.
+
+ * pymacs.el (pymacs-proper-list-p): New function. Use it
+ everywhere instead of listp, which is not what I thought it was!
+
+ * pymacs.el (pymacs-serve-until-reply): In case of LISP error,
+ transmit a list of one argument, instead of the argument itself,
+ to print-for-apply. This was preventing proper diagnostic.
+ Correct a similar error for when expansion is requested.
+
+ * pymacs.el (pymacs-print-for-eval): Do not transmit a symbol
+ by its name, when it comes from another oblist than the main one.
+
+ * pymacs.py (print_lisp): Transmit pymacs-id as a dotted pair.
+ * pymacs.el (pymacs-print-for-eval): Adjusted.
+
+ * pymacs.el (pymacs-print-for-eval): Use lisp[], not sym[].
+ Avoid double escaping of the transmitted string in this case.
+ Reported by Brian McErlean.
+
+2001-09-10 François Pinard <pinard@iro.umontreal.ca>
+
+ * : Release 0.3.
+
+ * pymacs.py (Server.send): Ensure an end of line after reply.
+ * pymacs.el (pymacs-round-trip): Do not add one after Python replies.
+
+ * pymacs.el (pymacs-round-trip): Check for vanishing helper process.
+ (pymacs-serve-until-reply): Get text without catching errors, than
+ eval. Else, protocol errors get reported back to Python.
+ * pymacs.py (Server.ProtocolError): New. Better than AssertError.
+ If it occurs, get out of program, do not keep returning errors.
+ Reported by Carey Evans.
+
+ * pymacs.el (pymacs-round-trip): If point coincides with marker,
+ just keep it that way as the buffer grows.
+
+ * pymacs.el (pymacs-start-services): If the hash table already
+ exists, inform the Python side of IDs that it should not reuse.
+ Otherwise, old lambdas may randomly refer to new Python objects.
+ (pymacs-terminate-services): Remember Python IDs, do not reset them.
+
+ * pymacs.py (zombie_handles): New.
+
+ * Makefile: Transmit $(pymacsdir) to pymacs-services.
+ * pymacs-services: Handle it.
+
+ * pymacs.py (print_lisp): Process an empty tuple properly.
+ Reported by Carey Evans.
+
+ * pymacs.el (pymacs-start-services): With run-at-time, use `20 20'
+ instead of `t 20', so XEmacs is happy.
+ Reported by Carey Evans.
+
+ * pymacs.el (pymacs-start-services, pymacs-terminate-services):
+ Use `post-gc-hook' if available, instead of using a timer.
+ Reported by Gerd Möllman.
+
+ * pymacs.py (Symbol.value, Symbol.copy): Add argument self.
+ (print_lisp): Quote symbols if quoted=1.
+
+2001-09-09 François Pinard <pinard@iro.umontreal.ca>
+
+ * pymacs.el (pymacs-defuns): New function.
+ * pymacs.py: Use it. This should allow faster imports.
+
+ * Makefile, pymacs.el, pymacs.py: Use `(pymacs-version VERSION)',
+ not `(started)'. Check for version discrepancies.
+
+ * Makefile: A bit more parameterization.
+
+ * : Release 0.2, including ideas and suggestions from others.
+ Reported by Brian McErlean, Carel Fellinger, Cedric Adjih,
+ Marcin Qrczak Kowalczyk, Paul Winkler and Steffen Ries.
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/ide-integration/Pymacs-0.20/ChangeLog-rebox Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,146 @@
+2002-01-29 François Pinard <pinard@iro.umontreal.ca>
+
+ * Pymacs/rebox.py: Use an interactions map instead of the
+ interaction attribute, so it works with earlier Python versions.
+
+ * Pymacs/rebox.py: Import lisp and Let from Pymacs.
+
+2002-01-13 François Pinard <pinard@iro.umontreal.ca>
+
+ * Pymacs/rebox.py (Emacs_Rebox.emacs_engine): Expand flag value,
+ when it is neither the - symbol nor a number.
+
+2002-01-08 François Pinard <pinard@iro.umontreal.ca>
+
+ * Pymacs/rebox.py (Template.build): Subtract margin from width
+ just before actually rebuilding the box.
+ Reported by Paul Provost.
+
+2002-01-07 François Pinard <pinard@iro.umontreal.ca>
+
+ * Pymacs/rebox.py (main): Implement -v option.
+
+ * Pymacs/rebox.py (pymacs_load_hook): Declare set_default_style.
+
+ * Pymacs/rebox.py (Emacs_Rebox.clean_undo_after): Debugged.
+
+ * Pymacs/rebox.py (Template): New class. Reorgnise all code.
+ * Pymacs/rebox.py (engine): Moved out of Rebox class.
+ * Pymacs/rebox.py (Rebox, Batch_Rebox): Deleted, as they got empty.
+
+ * Pymacs/rebox.py (Emacs_Rebox.clean_undo_after): Rewrite in LISP.
+
+2002-01-06 François Pinard <pinard@iro.umontreal.ca>
+
+ * rebox: New file.
+
+2002-01-03 François Pinard <pinard@iro.umontreal.ca>
+
+ * Pymacs/rebox.py: New file, translated from Libit/rebox.el.
+
+2000-09-28 François Pinard <pinard@iro.umontreal.ca>
+
+ * rebox.el: Replace statistical heuristics for box style recognition
+ by more precise checks and explicit priorities between styles. To do
+ so, add weights to rebox-templates, replace rebox-building-data by
+ rebox-style-data holding regexps, delete rebox-recognition-data.
+
+ * rebox.el (rebox-regexp-ruler): New function.
+ (rebox-regexp-quote): Add matching for following white space.
+ Don't force two characters on each middle line, nor in blank rulers.
+ Reported by Paul Provost.
+
+2000-04-28 François Pinard <pinard@iro.umontreal.ca>
+
+ * rebox.el (rebox-guess-style): When two styles have equal weight,
+ retain the highest numbered, as it probably is the richest.
+ Otherwise, simple C++ comments end up with a single slash.
+ Reported by Akim Demaille.
+
+2000-04-19 François Pinard <pinard@iro.umontreal.ca>
+
+ * rebox.el: Reorganize from bottom-up into top-down.
+ (taarna-mode): Deleted.
+
+2000-04-18 François Pinard <pinard@iro.umontreal.ca>
+
+ * rebox.el (rebox-show-style, rebox-help-string-for-language,
+ rebox-help-string-for-quality, rebox-help-string-for-type): Deleted.
+ (rebox-rstrip, rebox-regexp-quote, rebox-unbuild): New functions.
+ (rebox-build): New name for rebox-reconstruct.
+
+2000-04-15 François Pinard <pinard@iro.umontreal.ca>
+
+ * rebox.el (rebox-guess-style): New function.
+ (rebox-engine): Use it. Simplified by using template information.
+
+2000-04-14 François Pinard <pinard@iro.umontreal.ca>
+
+ * rebox.el (rebox-templates): New variable.
+ (rebox-register-template): New function.
+ (rebox-reconstruct): Much simplified by using the above.
+
+2000-04-12 François Pinard <pinard@iro.umontreal.ca>
+
+ * rebox.el: Rework the initial documentation block.
+ (rebox-reconstruct): Guarantee newline at end for style 241.
+ Reported by Marc Feeley and Paul Provost.
+
+2000-02-22 François Pinard <pinard@iro.umontreal.ca>
+
+ * rebox.el: Little speed cleanup. Avoid looking-at when easy.
+
+2000-02-10 François Pinard <pinard@iro.umontreal.ca>
+
+ * rebox.el: Adjust comment to suggest add-hook instead of setq.
+ Reported by Akim Demaille.
+
+2000-01-30 François Pinard <pinard@iro.umontreal.ca>
+
+ * rebox.el: Prefer when, unless and cond over if and progn.
+ Combine successive setq.
+
+ * rebox.el (rebox-engine): Recognise quality for shell boxes.
+ Reported by Akim Demaille.
+
+1999-06-30 François Pinard <pinard@iro.umontreal.ca>
+
+ * rebox.el: Add GPL comment.
+ Reported by Paul Eggert.
+
+1998-03-28 François Pinard <pinard@iro.umontreal.ca>
+
+ * rebox.el (rebox-reconstruct): Refill a closing */ with the rest.
+ Do not add spaces to a line which is otherwise empty.
+
+1997-12-01 François Pinard <pinard@iro.umontreal.ca>
+
+ * rebox.el (rebox-engine): Simplify two regexps, for XEmacs.
+ Reported by Ulrich Drepper.
+
+1997-02-17 François Pinard <pinard@iro.umontreal.ca>
+
+ * rebox.el (rebox-reconstruct): Ensure indent-tabs-mode is nil.
+
+1997-02-14 François Pinard <pinard@iro.umontreal.ca>
+
+ * rebox.el: Corrected a bug demonstrated as the beginning line
+ of a paragraph spuriously jumping right spuriously. The full
+ match of the beginning of comment was replaced by spaces on the
+ initial line, while only \1 needed replacement. This shortened
+ this line, causing later nasty effects.
+
+1996-07-10 François Pinard <pinard@iro.umontreal.ca>
+
+ * rebox.el: Recognise style 241, so margin does not get doubled.
+ Reported by Marc Feeley.
+
+1996-07-09 François Pinard <pinard@iro.umontreal.ca>
+
+ * rebox.el: Use symbolic constants for language, quality and type.
+
+1996-06-09 François Pinard <pinard@iro.umontreal.ca>
+
+ * rebox.el (rebox-find-and-narrow): Take care of a missing end of
+ line after a comment being at end of buffer.
+ Reported by Ulrich Drepper.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/ide-integration/Pymacs-0.20/Makefile Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,40 @@
+# Interface between Emacs LISP and Python - Makefile.
+# Copyright İ 2001, 2002 Progiciels Bourbeau-Pinard inc.
+# François Pinard <pinard@iro.umontreal.ca>, 2001.
+
+# The `README' file provides a few good hints about installation.
+
+### Start of customisation.
+#
+# Somewhere on your Emacs LISP load-path.
+lispdir =
+#
+### End of customisation.
+
+PYSETUP = python setup.py
+DISTRIBUTION := $(shell ./setup -V)
+
+all:
+ $(PYSETUP) build
+
+install: all
+ @./setup -l '$(lispdir)'
+ $(PYSETUP) install
+
+tags:
+ (find bin -type f; find -name '*.py') | grep -v '~$$' | etags -
+
+dist:
+ $(PYSETUP) sdist
+ mv dist/$(DISTRIBUTION).tar.gz .
+ rmdir dist
+ ls -l *.gz
+
+publish: dist
+ traiter README.html > index.html
+ chmod 644 index.html $(DISTRIBUTION).tar.gz
+ scp -p index.html $(DISTRIBUTION).tar.gz bor:w/pymacs/
+ rm index.html $(DISTRIBUTION).tar.gz
+ ssh bor rm -vf w/pymacs/Pymacs.tar.gz
+ ssh bor ln -vs $(DISTRIBUTION).tar.gz w/pymacs/Pymacs.tar.gz
+ ssh bor ls -Llt w/pymacs
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/ide-integration/Pymacs-0.20/PKG-INFO Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,10 @@
+Metadata-Version: 1.0
+Name: Pymacs
+Version: 0.20
+Summary: Interface between Emacs LISP and Python.
+Home-page: http://www.iro.umontreal.ca/~pinard
+Author: François Pinard
+Author-email: pinard@iro.umontreal.ca
+License: UNKNOWN
+Description: UNKNOWN
+Platform: UNKNOWN
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/ide-integration/Pymacs-0.20/Pymacs/.cvsignore Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,1 @@
+*.pyc
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/ide-integration/Pymacs-0.20/Pymacs/__init__.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,30 @@
+#!/usr/bin/env python
+# Copyright İ 2002 Progiciels Bourbeau-Pinard inc.
+# François Pinard <pinard@iro.umontreal.ca>, 2002.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+"""\
+Interface between Emacs Lisp and Python - Module initialisation.
+
+A few symbols are moved in here so they appear to be defined at this level.
+"""
+
+from pymacs import Let, lisp
+
+# Identification of version.
+
+package = 'Pymacs'
+version = '0.20'
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/ide-integration/Pymacs-0.20/Pymacs/pymacs.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,587 @@
+#!/usr/bin/env python
+# Copyright İ 2001, 2002 Progiciels Bourbeau-Pinard inc.
+# François Pinard <pinard@iro.umontreal.ca>, 2001.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+"""\
+Interface between Emacs Lisp and Python - Python part.
+
+Emacs may launch this module as a stand-alone program, in which case it
+acts as a server of Python facilities for that Emacs session, reading
+requests from standard input and writing replies on standard output.
+
+This module may also be usefully imported by those other Python modules.
+See the Pymacs documentation (in `README') for more information.
+"""
+
+## Note: This code is currently compatible down to Python version 1.5.2.
+## It is probably worth keeping it that way for a good while, still.
+
+import os, string, sys, types
+
+# Python services for Emacs applications.
+
+def main(*arguments):
+ """\
+Execute Python services for Emacs, and Emacs services for Python.
+This program is meant to be called from Emacs, using `pymacs.el'.
+
+The program arguments are additional search paths for Python modules.
+"""
+ from Pymacs import version
+ arguments = list(arguments)
+ arguments.reverse()
+ for argument in arguments:
+ if os.path.isdir(argument):
+ sys.path.insert(0, argument)
+ lisp._protocol.send('(pymacs-version "%s")' % version)
+ lisp._protocol.loop()
+
+class Protocol:
+
+ # FIXME: The following should work, but does not:
+ #
+ # * pymacs.py (Protocol): Declare exceptions as classes, not strings.
+ #
+ #class ProtocolError(Exception): pass
+ #class ReplyException(Exception): pass
+ #class ErrorException(Exception): pass
+ #
+ # I get:
+ # (pymacs-eval "lisp('\"abc\"').__class__.__name__")
+ # "ReplyException"
+
+ ProtocolError = 'ProtocolError'
+ ReplyException = 'ReplyException'
+ ErrorException = 'ErrorException'
+
+ def __init__(self):
+ self.freed = []
+
+ def loop(self):
+ # The server loop repeatedly receives a request from Emacs and
+ # returns a response, which is either the value of the received
+ # Python expression, or the Python traceback if an error occurs
+ # while evaluating the expression.
+
+ # The server loop may also be executed, as a recursive invocation,
+ # in the context of Emacs serving a Python request. In which
+ # case, we might also receive a notification from Emacs telling
+ # that the reply has been transmitted, or that an error occurred.
+ # A reply notification from Emacs interrupts the loop: the result
+ # of this function is the value returned from Emacs.
+ while 1:
+ try:
+ text = self.receive()
+ if text[:5] == 'exec ':
+ exec eval(text[5:], {}, {})
+ status = 'reply'
+ argument = None
+ else:
+ status = 'reply'
+ argument = eval(text)
+ except Protocol.ReplyException, value:
+ return value
+ except Protocol.ErrorException, message:
+ status = 'error'
+ argument = message
+ except Protocol.ProtocolError, message:
+ sys.stderr.write("Protocol error: %s\n" % message)
+ sys.exit(1)
+ except KeyboardInterrupt:
+ raise
+ except:
+ import StringIO, traceback
+ message = StringIO.StringIO()
+ traceback.print_exc(file=message)
+ status = 'error'
+ argument = message.getvalue()
+ # Send an expression to EMACS applying FUNCTION over ARGUMENT,
+ # where FUNCTION is `pymacs-STATUS'.
+ fragments = []
+ write = fragments.append
+ if self.freed:
+ write('(progn (pymacs-free-lisp')
+ for index in self.freed:
+ write(' %d' % index)
+ write(') ')
+ write('(pymacs-%s ' % status)
+ print_lisp(argument, write, quoted=1)
+ write(')')
+ if self.freed:
+ write(')')
+ self.freed = []
+ self.send(string.join(fragments, ''))
+
+ def receive(self):
+ # Receive a Python expression from Emacs, return its text unevaluated.
+ text = sys.stdin.read(3)
+ if not text or text[0] != '>':
+ raise Protocol.ProtocolError, "`>' expected."
+ while text[-1] != '\t':
+ text = text + sys.stdin.read(1)
+ return sys.stdin.read(int(text[1:-1]))
+
+ def send(self, text):
+ # Send TEXT to Emacs, which is an expression to evaluate.
+ if text[-1] == '\n':
+ sys.stdout.write('<%d\t%s' % (len(text), text))
+ else:
+ sys.stdout.write('<%d\t%s\n' % (len(text) + 1, text))
+ sys.stdout.flush()
+
+def reply(value):
+ # This function implements the `reply' pseudo-function.
+ raise Protocol.ReplyException, value
+
+def error(message):
+ # This function implements the `error' pseudo-function.
+ raise Protocol.ErrorException, "Emacs: %s" % message
+
+def pymacs_load_helper(file_without_extension, prefix):
+ # This function imports a Python module, then returns a Lisp expression
+ # which, when later evaluated, will install trampoline definitions in
+ # Emacs for accessing the Python module facilities. MODULE may be a
+ # full path, yet without the `.py' or `.pyc' extension, in which case
+ # the directory is temporarily added to the Python search path for
+ # the sole duration of that import. All defined symbols on the Lisp
+ # side have have PREFIX prepended, and have Python underlines in Python
+ # turned into dashes. If PREFIX is None, it then defaults to the base
+ # name of MODULE with underlines turned to dashes, followed by a dash.
+ directory, module_name = os.path.split(file_without_extension)
+ module_components = string.split(module_name, '.')
+ if prefix is None:
+ prefix = string.replace(module_components[-1], '_', '-') + '-'
+ try:
+ object = sys.modules.get(module_name)
+ if object:
+ reload(object)
+ else:
+ try:
+ if directory:
+ sys.path.insert(0, directory)
+ object = __import__(module_name)
+ finally:
+ if directory:
+ del sys.path[0]
+ # Whenever MODULE_NAME is of the form [PACKAGE.]...MODULE,
+ # __import__ returns the outer PACKAGE, not the module.
+ for component in module_components[1:]:
+ object = getattr(object, component)
+ except ImportError:
+ return None
+ load_hook = object.__dict__.get('pymacs_load_hook')
+ if load_hook:
+ load_hook()
+ interactions = object.__dict__.get('interactions', {})
+ if type(interactions) != types.DictType:
+ interactions = {}
+ arguments = []
+ for name, value in object.__dict__.items():
+ if callable(value) and value is not lisp:
+ arguments.append(allocate_python(value))
+ arguments.append(lisp[prefix + string.replace(name, '_', '-')])
+ try:
+ interaction = value.interaction
+ except AttributeError:
+ interaction = interactions.get(value)
+ if callable(interaction):
+ arguments.append(allocate_python(interaction))
+ else:
+ arguments.append(interaction)
+ if arguments:
+ return [lisp.progn,
+ [lisp.pymacs_defuns, [lisp.quote, arguments]],
+ object]
+ return [lisp.quote, object]
+
+def doc_string(object):
+ if hasattr(object, '__doc__'):
+ return object.__doc__
+
+# Garbage collection matters.
+
+# Many Python types do not have direct Lisp equivalents, and may not be
+# directly returned to Lisp for this reason. They are rather allocated in
+# a list of handles, below, and a handle index is used for communication
+# instead of the Python value. Whenever such a handle is freed from the
+# Lisp side, its index is added of a freed list for later reuse.
+
+python = []
+freed_list = []
+
+def allocate_python(value):
+ assert type(value) != type(''), (type(value), `value`)
+ # Allocate some handle to hold VALUE, return its index.
+ if freed_list:
+ index = freed_list[-1]
+ del freed_list[-1]
+ python[index] = value
+ else:
+ index = len(python)
+ python.append(value)
+ return index
+
+def free_python(*indices):
+ # Return many handles to the pool.
+ for index in indices:
+ python[index] = None
+ freed_list.append(index)
+
+def zombie_python(*indices):
+ # Ensure that some handles are _not_ in the pool.
+ for index in indices:
+ while index >= len(python):
+ freed_list.append(len(python))
+ python.append(None)
+ python[index] = zombie
+ freed_list.remove(index)
+ # Merely to make `*Pymacs*' a bit more readable.
+ freed_list.sort()
+
+def zombie(*arguments):
+ error("Object vanished when helper was killed.")
+
+# Emacs services for Python applications.
+
+class Let:
+
+ def __init__(self, **keywords):
+ self.stack = []
+ apply(self.push, (), keywords)
+
+ def __del__(self):
+ while self.stack:
+ method = self.stack[-1][0]
+ if method == 'variables':
+ self.pop()
+ elif method == 'excursion':
+ self.pop_excursion()
+ elif method == 'match_data':
+ self.pop_match_data()
+ elif method == 'restriction':
+ self.pop_restriction()
+ elif method == 'selected_window':
+ self.pop_selected_window()
+ elif method == 'window_excursion':
+ self.pop_window_excursion()
+
+ def __nonzero__(self):
+ # So stylistic `if let:' executes faster.
+ return 1
+
+ def push(self, **keywords):
+ pairs = []
+ for name, value in keywords.items():
+ pairs.append((name, getattr(lisp, name).value()))
+ setattr(lisp, name, value)
+ self.stack.append(('variables', pairs))
+ return self
+
+ def pop(self):
+ method, pairs = self.stack[-1]
+ assert method == 'variables', self.stack[-1]
+ del self.stack[-1]
+ for name, value in pairs:
+ setattr(lisp, name, value)
+
+ def push_excursion(self):
+ self.stack.append(('excursion',
+ (lisp.current_buffer(),
+ lisp.point_marker(), lisp.mark_marker())))
+ return self
+
+ def pop_excursion(self):
+ method, (buffer, point_marker, mark_marker) = self.stack[-1]
+ assert method == 'excursion', self.stack[-1]
+ del self.stack[-1]
+ lisp.set_buffer(buffer)
+ lisp.goto_char(point_marker)
+ lisp.set_mark(mark_marker)
+ lisp.set_marker(point_marker, None)
+ lisp.set_marker(mark_marker, None)
+
+ def push_match_data(self):
+ self.stack.append(('match_data', lisp.match_data()))
+ return self
+
+ def pop_match_data(self):
+ method, match_data = self.stack[-1]
+ assert method == 'match_data', self.stack[-1]
+ del self.stack[-1]
+ lisp.set_match_data(match_data)
+
+ def push_restriction(self):
+ self.stack.append(('restriction',
+ (lisp.point_min_marker(), lisp.point_max_marker())))
+ return self
+
+ def pop_restriction(self):
+ method, (point_min_marker, point_max_marker) = self.stack[-1]
+ assert method == 'restriction', self.stack[-1]
+ del self.stack[-1]
+ lisp.narrow_to_region(point_min_marker, point_max_marker)
+ lisp.set_marker(point_min_marker, None)
+ lisp.set_marker(point_max_marker, None)
+
+ def push_selected_window(self):
+ self.stack.append(('selected_window', lisp.selected_window()))
+ return self
+
+ def pop_selected_window(self):
+ method, selected_window = self.stack[-1]
+ assert method == 'selected_window', self.stack[-1]
+ del self.stack[-1]
+ lisp.select_window(selected_window)
+
+ def push_window_excursion(self):
+ self.stack.append(('window_excursion',
+ lisp.current_window_configuration()))
+ return self
+
+ def pop_window_excursion(self):
+ method, current_window_configuration = self.stack[-1]
+ assert method == 'window_excursion', self.stack[-1]
+ del self.stack[-1]
+ lisp.set_window_configuration(current_window_configuration)
+
+class Symbol:
+
+ def __init__(self, text):
+ self.text = text
+
+ def __repr__(self):
+ return 'lisp[%s]' % repr(self.text)
+
+ def __str__(self):
+ return '\'' + self.text
+
+ def value(self):
+ return lisp(self.text)
+
+ def copy(self):
+ return lisp('(pymacs-expand %s)' % self.text)
+
+ def set(self, value):
+ if value is None:
+ lisp('(setq %s nil)' % self.text)
+ else:
+ fragments = []
+ write = fragments.append
+ write('(progn (setq %s ' % self.text)
+ print_lisp(value, write, quoted=1)
+ write(') nil)')
+ lisp(string.join(fragments, ''))
+
+ def __call__(self, *arguments):
+ fragments = []
+ write = fragments.append
+ write('(%s' % self.text)
+ for argument in arguments:
+ write(' ')
+ print_lisp(argument, write, quoted=1)
+ write(')')
+ return lisp(string.join(fragments, ''))
+
+class Lisp:
+
+ def __init__(self, index):
+ self.index = index
+
+ def __del__(self):
+ lisp._protocol.freed.append(self.index)
+
+ def __repr__(self):
+ return ('lisp(%s)' % repr(lisp('(prin1-to-string %s)' % self)))
+
+ def __str__(self):
+ return '(aref pymacs-lisp %d)' % self.index
+
+ def value(self):
+ return self
+
+ def copy(self):
+ return lisp('(pymacs-expand %s)' % self)
+
+class Buffer(Lisp):
+ pass
+
+ #def write(text):
+ # # So you could do things like
+ # # print >>lisp.current_buffer(), "Hello World"
+ # lisp.insert(text, self)
+
+ #def point(self):
+ # return lisp.point(self)
+
+class List(Lisp):
+
+ def __call__(self, *arguments):
+ fragments = []
+ write = fragments.append
+ write('(%s' % self)
+ for argument in arguments:
+ write(' ')
+ print_lisp(argument, write, quoted=1)
+ write(')')
+ return lisp(string.join(fragments, ''))
+
+ def __len__(self):
+ return lisp('(length %s)' % self)
+
+ def __getitem__(self, key):
+ value = lisp('(nth %d %s)' % (key, self))
+ if value is None and key >= len(self):
+ raise IndexError, key
+ return value
+
+ def __setitem__(self, key, value):
+ fragments = []
+ write = fragments.append
+ write('(setcar (nthcdr %d %s) ' % (key, self))
+ print_lisp(value, write, quoted=1)
+ write(')')
+ lisp(string.join(fragments, ''))
+
+class Table(Lisp):
+
+ def __getitem__(self, key):
+ fragments = []
+ write = fragments.append
+ write('(gethash ')
+ print_lisp(key, write, quoted=1)
+ write(' %s)' % self)
+ return lisp(string.join(fragments, ''))
+
+ def __setitem__(self, key, value):
+ fragments = []
+ write = fragments.append
+ write('(puthash ')
+ print_lisp(key, write, quoted=1)
+ write(' ')
+ print_lisp(value, write, quoted=1)
+ write(' %s)' % self)
+ lisp(string.join(fragments, ''))
+
+class Vector(Lisp):
+
+ def __len__(self):
+ return lisp('(length %s)' % self)
+
+ def __getitem__(self, key):
+ return lisp('(aref %s %d)' % (self, key))
+
+ def __setitem__(self, key, value):
+ fragments = []
+ write = fragments.append
+ write('(aset %s %d ' % (self, key))
+ print_lisp(value, write, quoted=1)
+ write(')')
+ lisp(string.join(fragments, ''))
+
+class Lisp_Interface:
+
+ def __init__(self):
+ self.__dict__['_cache'] = {'nil': None}
+ self.__dict__['_protocol'] = Protocol()
+
+ def __call__(self, text):
+ self._protocol.send('(progn %s)' % text)
+ return self._protocol.loop()
+
+ def __getattr__(self, name):
+ if name[0] == '_':
+ raise AttributeError, name
+ return self[string.replace(name, '_', '-')]
+
+ def __setattr__(self, name, value):
+ if name[0] == '_':
+ raise AttributeError, name
+ self[string.replace(name, '_', '-')] = value
+
+ def __getitem__(self, name):
+ try:
+ return self._cache[name]
+ except KeyError:
+ symbol = self._cache[name] = Symbol(name)
+ return symbol
+
+ def __setitem__(self, name, value):
+ try:
+ symbol = self._cache[name]
+ except KeyError:
+ symbol = self._cache[name] = Symbol(name)
+ symbol.set(value)
+
+lisp = Lisp_Interface()
+
+print_lisp_quoted_specials = {'"': '\\"', '\\': '\\\\', '\b': '\\b',
+ '\f': '\\f', '\n': '\\n', '\t': '\\t'}
+
+def print_lisp(value, write, quoted=0):
+ if value is None:
+ write('nil')
+ elif type(value) == types.IntType:
+ write(repr(value))
+ elif type(value) == types.FloatType:
+ write(repr(value))
+ elif type(value) == types.StringType:
+ write('"')
+ for character in value:
+ special = print_lisp_quoted_specials.get(character)
+ if special is not None:
+ write(special)
+ elif 32 <= ord(character) < 127:
+ write(character)
+ else:
+ write('\\%.3o' % ord(character))
+ write('"')
+ elif type(value) == types.ListType:
+ if quoted:
+ write("'")
+ if len(value) == 0:
+ write('nil')
+ elif len(value) == 2 and value[0] == lisp.quote:
+ write("'")
+ print_lisp(value[1], write)
+ else:
+ write('(')
+ print_lisp(value[0], write)
+ for sub_value in value[1:]:
+ write(' ')
+ print_lisp(sub_value, write)
+ write(')')
+ elif type(value) == types.TupleType:
+ write('[')
+ if len(value) > 0:
+ print_lisp(value[0], write)
+ for sub_value in value[1:]:
+ write(' ')
+ print_lisp(sub_value, write)
+ write(']')
+ elif isinstance(value, Lisp):
+ write(str(value))
+ elif isinstance(value, Symbol):
+ if quoted:
+ write("'")
+ write(value.text)
+ elif callable(value):
+ write('(pymacs-defun %d)' % allocate_python(value))
+ else:
+ write('(pymacs-python %d)' % allocate_python(value))
+
+if __name__ == '__main__':
+ apply(main, sys.argv[1:])
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/ide-integration/Pymacs-0.20/Pymacs/rebox.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,1235 @@
+#!/usr/bin/env python
+# Copyright İ 1991-1998, 2000, 2002 Progiciels Bourbeau-Pinard inc.
+# François Pinard <pinard@iro.umontreal.ca>, April 1991.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+"""\
+Handling of boxed comments in various box styles.
+
+Introduction
+------------
+
+For comments held within boxes, it is painful to fill paragraphs, while
+stretching or shrinking the surrounding box "by hand", as needed. This piece
+of Python code eases my life on this. It may be used interactively from
+within Emacs through the Pymacs interface, or in batch as a script which
+filters a single region to be reformatted. I find only fair, while giving
+all sources for a package using such boxed comments, to also give the
+means I use for nicely modifying comments. So here they are!
+
+Box styles
+----------
+
+Each supported box style has a number associated with it. This number is
+arbitrary, yet by _convention_, it holds three non-zero digits such the the
+hundreds digit roughly represents the programming language, the tens digit
+roughly represents a box quality (or weight) and the units digit roughly
+a box type (or figure). An unboxed comment is merely one of box styles.
+Language, quality and types are collectively referred to as style attributes.
+
+When rebuilding a boxed comment, attributes are selected independently
+of each other. They may be specified by the digits of the value given
+as Emacs commands argument prefix, or as the `-s' argument to the `rebox'
+script when called from the shell. If there is no such prefix, or if the
+corresponding digit is zero, the attribute is taken from the value of the
+default style instead. If the corresponding digit of the default style
+is also zero, than the attribute is recognised and taken from the actual
+boxed comment, as it existed before prior to the command. The value 1,
+which is the simplest attribute, is ultimately taken if the parsing fails.
+
+A programming language is associated with comment delimiters. Values are
+100 for none or unknown, 200 for `/*' and `*/' as in plain C, 300 for `//'
+as in C++, 400 for `#' as in most scripting languages, 500 for `;' as in
+LISP or assembler and 600 for `%' as in TeX or PostScript.
+
+Box quality differs according to language. For unknown languages (100) or
+for the C language (200), values are 10 for simple, 20 for rounded, and
+30 or 40 for starred. Simple quality boxes (10) use comment delimiters
+to left and right of each comment line, and also for the top or bottom
+line when applicable. Rounded quality boxes (20) try to suggest rounded
+corners in boxes. Starred quality boxes (40) mostly use a left margin of
+asterisks or X'es, and use them also in box surroundings. For all others
+languages, box quality indicates the thickness in characters of the left
+and right sides of the box: values are 10, 20, 30 or 40 for 1, 2, 3 or 4
+characters wide. With C++, quality 10 is not useful, it is not allowed.
+
+Box type values are 1 for fully opened boxes for which boxing is done
+only for the left and right but not for top or bottom, 2 for half
+single lined boxes for which boxing is done on all sides except top,
+3 for fully single lined boxes for which boxing is done on all sides,
+4 for half double lined boxes which is like type 2 but more bold,
+or 5 for fully double lined boxes which is like type 3 but more bold.
+
+The special style 221 is for C comments between a single opening `/*'
+and a single closing `*/'. The special style 111 deletes a box.
+
+Batch usage
+-----------
+
+Usage is `rebox [OPTION]... [FILE]'. By default, FILE is reformatted to
+standard output by refilling the comment up to column 79, while preserving
+existing boxed comment style. If FILE is not given, standard input is read.
+Options may be:
+
+ -n Do not refill the comment inside its box, and ignore -w.
+ -s STYLE Replace box style according to STYLE, as explained above.
+ -t Replace initial sequence of spaces by TABs on each line.
+ -v Echo both the old and the new box styles on standard error.
+ -w WIDTH Try to avoid going over WIDTH columns per line.
+
+So, a single boxed comment is reformatted by invocation. `vi' users, for
+example, would need to delimit the boxed comment first, before executing
+the `!}rebox' command (is this correct? my `vi' recollection is far away).
+
+Batch usage is also slow, as internal structures have to be reinitialised
+at every call. Producing a box in a single style is fast, but recognising
+the previous style requires setting up for all possible styles.
+
+Emacs usage
+-----------
+
+For most Emacs language editing modes, refilling does not make sense
+outside comments, one may redefine the `M-q' command and link it to this
+Pymacs module. For example, I use this in my `.emacs' file:
+
+ (add-hook 'c-mode-hook 'fp-c-mode-routine)
+ (defun fp-c-mode-routine ()
+ (local-set-key "\M-q" 'rebox-comment))
+ (autoload 'rebox-comment "rebox" nil t)
+ (autoload 'rebox-region "rebox" nil t)
+
+with a "rebox.el" file having this single line:
+
+ (pymacs-load "Pymacs.rebox")
+
+Install Pymacs from `http://www.iro.umontreal.ca/~pinard/pymacs.tar.gz'.
+
+The Emacs function `rebox-comment' automatically discovers the extent of
+the boxed comment near the cursor, possibly refills the text, then adjusts
+the box style. When this command is executed, the cursor should be within
+a comment, or else it should be between two comments, in which case the
+command applies to the next comment. The function `rebox-region' does
+the same, except that it takes the current region as a boxed comment.
+Both commands obey numeric prefixes to add or remove a box, force a
+particular box style, or to prevent refilling of text. Without such
+prefixes, the commands may deduce the current box style from the comment
+itself so the style is preserved.
+
+The default style initial value is nil or 0. It may be preset to another
+value through calling `rebox-set-default-style' from Emacs LISP, or changed
+to anything else though using a negative value for a prefix, in which case
+the default style is set to the absolute value of the prefix.
+
+A `C-u' prefix avoids refilling the text, but forces using the default box
+style. `C-u -' lets the user interact to select one attribute at a time.
+
+Adding new styles
+-----------------
+
+Let's suppose you want to add your own boxed comment style, say:
+
+ //--------------------------------------------+
+ // This is the style mandated in our company.
+ //--------------------------------------------+
+
+You might modify `rebox.py' but then, you will have to edit it whenever you
+get a new release of `pybox.py'. Emacs users might modify their `.emacs'
+file or their `rebox.el' bootstrap, if they use one. In either cases,
+after the `(pymacs-load "Pymacs.rebox")' line, merely add:
+
+ (rebox-Template NNN MMM ["//-----+"
+ "// box "
+ "//-----+"])
+
+If you use the `rebox' script rather than Emacs, the simplest is to make
+your own. This is easy, as it is very small. For example, the above
+style could be implemented by using this script instead of `rebox':
+
+ #!/usr/bin/env python
+ import sys
+ from Pymacs import rebox
+ rebox.Template(226, 325, ('//-----+',
+ '// box ',
+ '//-----+'))
+ apply(rebox.main, tuple(sys.argv[1:]))
+
+In all cases, NNN is the style three-digit number, with no zero digit.
+Pick any free style number, you are safe with 911 and up. MMM is the
+recognition priority, only used to disambiguate the style of a given boxed
+comments, when it matches many styles at once. Try something like 400.
+Raise or lower that number as needed if you observe false matches.
+
+On average, the template uses three lines of equal length. Do not worry if
+this implies a few trailing spaces, they will be cleaned up automatically
+at box generation time. The first line or the third line may be omitted
+to create vertically opened boxes. But the middle line may not be omitted,
+it ought to include the word `box', which will get replaced by your actual
+comment. If the first line is shorter than the middle one, it gets merged
+at the start of the comment. If the last line is shorter than the middle
+one, it gets merged at the end of the comment and is refilled with it.
+
+History
+-------
+
+I first observed rounded corners, as in style 223 boxes, in code from
+Warren Tucker, a previous maintainer of the `shar' package, circa 1980.
+
+Except for very special files, I carefully avoided boxed comments for
+real work, as I found them much too hard to maintain. My friend Paul
+Provost was working at Taarna, a computer graphics place, which had boxes
+as part of their coding standards. He asked that we try something to get
+him out of his misery, and this how `rebox.el' was originally written.
+I did not plan to use it for myself, but Paul was so enthusiastic that I
+timidly started to use boxes in my things, very little at first, but more
+and more as time passed, still in doubt that it was a good move. Later,
+many friends spontaneously started to use this tool for real, some being very
+serious workers. This convinced me that boxes are acceptable, after all.
+
+I do not use boxes much with Python code. It is so legible that boxing
+is not that useful. Vertical white space is less necessary, too. I even
+avoid white lines within functions. Comments appear prominent enough when
+using highlighting editors like Emacs or nice printer tools like `enscript'.
+
+After Emacs could be extended with Python, in 2001, I translated `rebox.el'
+into `rebox.py', and added the facility to use it as a batch script.
+"""
+
+## Note: This code is currently compatible down to Python version 1.5.2.
+## It is probably worth keeping it that way for a good while, still.
+
+## Note: a double hash comment introduces a group of functions or methods.
+
+import re, string, sys
+
+def main(*arguments):
+ refill = 1
+ style = None
+ tabify = 0
+ verbose = 0
+ width = 79
+ import getopt
+ options, arguments = getopt.getopt(arguments, 'ns:tvw:', ['help'])
+ for option, value in options:
+ if option == '--help':
+ sys.stdout.write(__doc__)
+ sys.exit(0)
+ elif option == '-n':
+ refill = 0
+ elif option == '-s':
+ style = int(value)
+ elif option == '-t':
+ tabify = 1
+ elif option == '-v':
+ verbose = 1
+ elif option == '-w':
+ width = int(value)
+ if len(arguments) == 0:
+ text = sys.stdin.read()
+ elif len(arguments) == 1:
+ text = open(arguments[0]).read()
+ else:
+ sys.stderr.write("Invalid usage, try `rebox --help' for help.\n")
+ sys.exit(1)
+ old_style, new_style, text, position = engine(
+ text, style=style, width=width, refill=refill, tabify=tabify)
+ if text is None:
+ sys.stderr.write("* Cannot rebox to style %d.\n" % new_style)
+ sys.exit(1)
+ sys.stdout.write(text)
+ if verbose:
+ if old_style == new_style:
+ sys.stderr.write("Reboxed with style %d.\n" % old_style)
+ else:
+ sys.stderr.write("Reboxed from style %d to %d.\n"
+ % (old_style, new_style))
+
+def pymacs_load_hook():
+ global interactions, lisp, Let, region, comment, set_default_style
+ from Pymacs import lisp, Let
+ emacs_rebox = Emacs_Rebox()
+ # Declare functions for Emacs to import.
+ interactions = {}
+ region = emacs_rebox.region
+ interactions[region] = 'P'
+ comment = emacs_rebox.comment
+ interactions[comment] = 'P'
+ set_default_style = emacs_rebox.set_default_style
+
+class Emacs_Rebox:
+
+ def __init__(self):
+ self.default_style = None
+
+ def set_default_style(self, style):
+ """\
+Set the default style to STYLE.
+"""
+ self.default_style = style
+
+ def region(self, flag):
+ """\
+Rebox the boxed comment in the current region, obeying FLAG.
+"""
+ self.emacs_engine(flag, self.find_region)
+
+ def comment(self, flag):
+ """\
+Rebox the surrounding boxed comment, obeying FLAG.
+"""
+ self.emacs_engine(flag, self.find_comment)
+
+ def emacs_engine(self, flag, find_limits):
+ """\
+Rebox text while obeying FLAG. Call FIND_LIMITS to discover the extent
+of the boxed comment.
+"""
+ # `C-u -' means that box style is to be decided interactively.
+ if flag == lisp['-']:
+ flag = self.ask_for_style()
+ # If FLAG is zero or negative, only change default box style.
+ if type(flag) is type(0) and flag <= 0:
+ self.default_style = -flag
+ lisp.message("Default style set to %d" % -flag)
+ return
+ # Decide box style and refilling.
+ if flag is None:
+ style = self.default_style
+ refill = 1
+ elif type(flag) == type(0):
+ if self.default_style is None:
+ style = flag
+ else:
+ style = merge_styles(self.default_style, flag)
+ refill = 1
+ else:
+ flag = flag.copy()
+ if type(flag) == type([]):
+ style = self.default_style
+ refill = 0
+ else:
+ lisp.error("Unexpected flag value %s" % flag)
+ # Prepare for reboxing.
+ lisp.message("Reboxing...")
+ checkpoint = lisp.buffer_undo_list.value()
+ start, end = find_limits()
+ text = lisp.buffer_substring(start, end)
+ width = lisp.fill_column.value()
+ tabify = lisp.indent_tabs_mode.value() is not None
+ point = lisp.point()
+ if start <= point < end:
+ position = point - start
+ else:
+ position = None
+ # Rebox the text and replace it in Emacs buffer.
+ old_style, new_style, text, position = engine(
+ text, style=style, width=width,
+ refill=refill, tabify=tabify, position=position)
+ if text is None:
+ lisp.error("Cannot rebox to style %d" % new_style)
+ lisp.delete_region(start, end)
+ lisp.insert(text)
+ if position is not None:
+ lisp.goto_char(start + position)
+ # Collapse all operations into a single one, for Undo.
+ self.clean_undo_after(checkpoint)
+ # We are finished, tell the user.
+ if old_style == new_style:
+ lisp.message("Reboxed with style %d" % old_style)
+ else:
+ lisp.message("Reboxed from style %d to %d"
+ % (old_style, new_style))
+
+ def ask_for_style(self):
+ """\
+Request the style interactively, using the minibuffer.
+"""
+ language = quality = type = None
+ while language is None:
+ lisp.message("\
+Box language is 100-none, 200-/*, 300-//, 400-#, 500-;, 600-%%")
+ key = lisp.read_char()
+ if key >= ord('0') and key <= ord('6'):
+ language = key - ord('0')
+ while quality is None:
+ lisp.message("\
+Box quality/width is 10-simple/1, 20-rounded/2, 30-starred/3 or 40-starred/4")
+ key = lisp.read_char()
+ if key >= ord('0') and key <= ord('4'):
+ quality = key - ord('0')
+ while type is None:
+ lisp.message("\
+Box type is 1-opened, 2-half-single, 3-single, 4-half-double or 5-double")
+ key = lisp.read_char()
+ if key >= ord('0') and key <= ord('5'):
+ type = key - ord('0')
+ return 100*language + 10*quality + type
+
+ def find_region(self):
+ """\
+Return the limits of the region.
+"""
+ return lisp.point(), lisp.mark(lisp.t)
+
+ def find_comment(self):
+ """\
+Find and return the limits of the block of comments following or enclosing
+the cursor, or return an error if the cursor is not within such a block
+of comments. Extend it as far as possible in both directions.
+"""
+ let = Let()
+ let.push_excursion()
+ # Find the start of the current or immediately following comment.
+ lisp.beginning_of_line()
+ lisp.skip_chars_forward(' \t\n')
+ lisp.beginning_of_line()
+ if not language_matcher[0](self.remainder_of_line()):
+ temp = lisp.point()
+ if not lisp.re_search_forward('\\*/', None, lisp.t):
+ lisp.error("outside any comment block")
+ lisp.re_search_backward('/\\*')
+ if lisp.point() > temp:
+ lisp.error("outside any comment block")
+ temp = lisp.point()
+ lisp.beginning_of_line()
+ lisp.skip_chars_forward(' \t')
+ if lisp.point() != temp:
+ lisp.error("text before start of comment")
+ lisp.beginning_of_line()
+ start = lisp.point()
+ language = guess_language(self.remainder_of_line())
+ # Find the end of this comment.
+ if language == 2:
+ lisp.search_forward('*/')
+ if not lisp.looking_at('[ \t]*$'):
+ lisp.error("text after end of comment")
+ lisp.end_of_line()
+ if lisp.eobp():
+ lisp.insert('\n')
+ else:
+ lisp.forward_char(1)
+ end = lisp.point()
+ # Try to extend the comment block backwards.
+ lisp.goto_char(start)
+ while not lisp.bobp():
+ if language == 2:
+ lisp.skip_chars_backward(' \t\n')
+ if not lisp.looking_at('[ \t]*\n[ \t]*/\\*'):
+ break
+ if lisp.point() < 2:
+ break
+ lisp.backward_char(2)
+ if not lisp.looking_at('\\*/'):
+ break
+ lisp.re_search_backward('/\\*')
+ temp = lisp.point()
+ lisp.beginning_of_line()
+ lisp.skip_chars_forward(' \t')
+ if lisp.point() != temp:
+ break
+ lisp.beginning_of_line()
+ else:
+ lisp.previous_line(1)
+ if not language_matcher[language](self.remainder_of_line()):
+ break
+ start = lisp.point()
+ # Try to extend the comment block forward.
+ lisp.goto_char(end)
+ while language_matcher[language](self.remainder_of_line()):
+ if language == 2:
+ lisp.re_search_forward('[ \t]*/\\*')
+ lisp.re_search_forward('\\*/')
+ if lisp.looking_at('[ \t]*$'):
+ lisp.beginning_of_line()
+ lisp.forward_line(1)
+ end = lisp.point()
+ else:
+ lisp.forward_line(1)
+ end = lisp.point()
+ return start, end
+
+ def remainder_of_line(self):
+ """\
+Return all characters between point and end of line in Emacs buffer.
+"""
+ return lisp('''\
+(buffer-substring (point) (save-excursion (skip-chars-forward "^\n") (point)))
+''')
+
+ def clean_undo_after_old(self, checkpoint):
+ """\
+Remove all intermediate boundaries from the Undo list since CHECKPOINT.
+"""
+ # Declare some LISP functions.
+ car = lisp.car
+ cdr = lisp.cdr
+ eq = lisp.eq
+ setcdr = lisp.setcdr
+ # Remove any `nil' delimiter recently added to the Undo list.
+ cursor = lisp.buffer_undo_list.value()
+ if not eq(cursor, checkpoint):
+ tail = cdr(cursor)
+ while not eq(tail, checkpoint):
+ if car(tail):
+ cursor = tail
+ tail = cdr(cursor)
+ else:
+ tail = cdr(tail)
+ setcdr(cursor, tail)
+
+ def clean_undo_after(self, checkpoint):
+ """\
+Remove all intermediate boundaries from the Undo list since CHECKPOINT.
+"""
+ lisp("""
+(let ((undo-list %s))
+ (if (not (eq buffer-undo-list undo-list))
+ (let ((cursor buffer-undo-list))
+ (while (not (eq (cdr cursor) undo-list))
+ (if (car (cdr cursor))
+ (setq cursor (cdr cursor))
+ (setcdr cursor (cdr (cdr cursor)))))))
+ nil)
+"""
+ % (checkpoint or 'nil'))
+
+def engine(text, style=None, width=79, refill=1, tabify=0, position=None):
+ """\
+Add, delete or adjust a boxed comment held in TEXT, according to STYLE.
+STYLE values are explained at beginning of this file. Any zero attribute
+in STYLE indicates that the corresponding attribute should be recovered
+from the currently existing box. Produced lines will not go over WIDTH
+columns if possible, if refilling gets done. But if REFILL is false, WIDTH
+is ignored. If TABIFY is true, the beginning of produced lines will have
+spaces replace by TABs. POSITION is either None, or a character position
+within TEXT. Returns four values: the old box style, the new box style,
+the reformatted text, and either None or the adjusted value of POSITION in
+the new text. The reformatted text is returned as None if the requested
+style does not exist.
+"""
+ last_line_complete = text and text[-1] == '\n'
+ if last_line_complete:
+ text = text[:-1]
+ lines = string.split(string.expandtabs(text), '\n')
+ # Decide about refilling and the box style to use.
+ new_style = 111
+ old_template = guess_template(lines)
+ new_style = merge_styles(new_style, old_template.style)
+ if style is not None:
+ new_style = merge_styles(new_style, style)
+ new_template = template_registry.get(new_style)
+ # Interrupt processing if STYLE does not exist.
+ if not new_template:
+ return old_template.style, new_style, None, None
+ # Remove all previous comment marks, and left margin.
+ if position is not None:
+ marker = Marker()
+ marker.save_position(text, position, old_template.characters())
+ lines, margin = old_template.unbuild(lines)
+ # Ensure only one white line between paragraphs.
+ counter = 1
+ while counter < len(lines) - 1:
+ if lines[counter] == '' and lines[counter-1] == '':
+ del lines[counter]
+ else:
+ counter = counter + 1
+ # Rebuild the boxed comment.
+ lines = new_template.build(lines, width, refill, margin)
+ # Retabify to the left only.
+ if tabify:
+ for counter in range(len(lines)):
+ tabs = len(re.match(' *', lines[counter]).group()) / 8
+ lines[counter] = '\t' * tabs + lines[counter][8*tabs:]
+ # Restore the point position.
+ text = string.join(lines, '\n')
+ if last_line_complete:
+ text = text + '\n'
+ if position is not None:
+ position = marker.get_position(text, new_template.characters())
+ return old_template.style, new_style, text, position
+
+def guess_language(line):
+ """\
+Guess the language in use for LINE.
+"""
+ for language in range(len(language_matcher) - 1, 1, -1):
+ if language_matcher[language](line):
+ return language
+ return 1
+
+def guess_template(lines):
+ """\
+Find the heaviest box template matching LINES.
+"""
+ best_template = None
+ for template in template_registry.values():
+ if best_template is None or template > best_template:
+ if template.match(lines):
+ best_template = template
+ return best_template
+
+def left_margin_size(lines):
+ """\
+Return the width of the left margin for all LINES. Ignore white lines.
+"""
+ margin = None
+ for line in lines:
+ counter = len(re.match(' *', line).group())
+ if counter != len(line):
+ if margin is None or counter < margin:
+ margin = counter
+ if margin is None:
+ margin = 0
+ return margin
+
+def merge_styles(original, update):
+ """\
+Return style attributes as per ORIGINAL, in which attributes have been
+overridden by non-zero corresponding style attributes from UPDATE.
+"""
+ style = [original / 100, original / 10 % 10, original % 10]
+ merge = update / 100, update / 10 % 10, update % 10
+ for counter in range(3):
+ if merge[counter]:
+ style[counter] = merge[counter]
+ return 100*style[0] + 10*style[1] + style[2]
+
+def refill_lines(lines, width):
+ """\
+Refill LINES, trying to not produce lines having more than WIDTH columns.
+"""
+ # Try using GNU `fmt'.
+ import tempfile, os
+ name = tempfile.mktemp()
+ open(name, 'w').write(string.join(lines, '\n') + '\n')
+ process = os.popen('fmt -cuw %d %s' % (width, name))
+ text = process.read()
+ os.remove(name)
+ if process.close() is None:
+ return map(string.expandtabs, string.split(text, '\n')[:-1])
+ # If `fmt' failed, do refilling more naively, wihtout using the
+ # Knuth algorithm, nor protecting full stops at end of sentences.
+ lines.append(None)
+ new_lines = []
+ new_line = ''
+ start = 0
+ for end in range(len(lines)):
+ if not lines[end]:
+ margin = left_margin_size(lines[start:end])
+ for line in lines[start:end]:
+ counter = len(re.match(' *', line).group())
+ if counter > margin:
+ if new_line:
+ new_lines.append(' ' * margin + new_line)
+ new_line = ''
+ indent = counter - margin
+ else:
+ indent = 0
+ for word in string.split(line):
+ if new_line:
+ if len(new_line) + 1 + len(word) > width:
+ new_lines.append(' ' * margin + new_line)
+ new_line = word
+ else:
+ new_line = new_line + ' ' + word
+ else:
+ new_line = ' ' * indent + word
+ indent = 0
+ if new_line:
+ new_lines.append(' ' * margin + new_line)
+ new_line = ''
+ if lines[end] is not None:
+ new_lines.append('')
+ start = end + 1
+ return new_lines
+
+class Marker:
+
+ ## Heuristic to simulate a marker while reformatting boxes.
+
+ def save_position(self, text, position, ignorable):
+ """\
+Given a TEXT and a POSITION in that text, save the adjusted position
+by faking that all IGNORABLE characters before POSITION were removed.
+"""
+ ignore = {}
+ for character in ' \t\r\n' + ignorable:
+ ignore[character] = None
+ counter = 0
+ for character in text[:position]:
+ if ignore.has_key(character):
+ counter = counter + 1
+ self.position = position - counter
+
+ def get_position(self, text, ignorable, latest=0):
+ """\
+Given a TEXT, return the value that would yield the currently saved position,
+if it was saved by `save_position' with IGNORABLE. Unless the position lies
+within a series of ignorable characters, LATEST has no effect in practice.
+If LATEST is true, return the biggest possible value instead of the smallest.
+"""
+ ignore = {}
+ for character in ' \t\r\n' + ignorable:
+ ignore[character] = None
+ counter = 0
+ position = 0
+ if latest:
+ for character in text:
+ if ignore.has_key(character):
+ counter = counter + 1
+ else:
+ if position == self.position:
+ break
+ position = position + 1
+ elif self.position > 0:
+ for character in text:
+ if ignore.has_key(character):
+ counter = counter + 1
+ else:
+ position = position + 1
+ if position == self.position:
+ break
+ return position + counter
+
+## Template processing.
+
+class Template:
+
+ def __init__(self, style, weight, lines):
+ """\
+Digest and register a single template. The template is numbered STYLE,
+has a parsing WEIGHT, and is described by one to three LINES.
+STYLE should be used only once through all `declare_template' calls.
+
+One of the lines should contain the substring `box' to represent the comment
+to be boxed, and if three lines are given, `box' should appear in the middle
+one. Lines containing only spaces are implied as necessary before and after
+the the `box' line, so we have three lines.
+
+Normally, all three template lines should be of the same length. If the first
+line is shorter, it represents a start comment string to be bundled within the
+first line of the comment text. If the third line is shorter, it represents
+an end comment string to be bundled at the end of the comment text, and
+refilled with it.
+"""
+ assert not template_registry.has_key(style), \
+ "Style %d defined more than once" % style
+ self.style = style
+ self.weight = weight
+ # Make it exactly three lines, with `box' in the middle.
+ start = string.find(lines[0], 'box')
+ if start >= 0:
+ line1 = None
+ line2 = lines[0]
+ if len(lines) > 1:
+ line3 = lines[1]
+ else:
+ line3 = None
+ else:
+ start = string.find(lines[1], 'box')
+ if start >= 0:
+ line1 = lines[0]
+ line2 = lines[1]
+ if len(lines) > 2:
+ line3 = lines[2]
+ else:
+ line3 = None
+ else:
+ assert 0, "Erroneous template for %d style" % style
+ end = start + len('box')
+ # Define a few booleans.
+ self.merge_nw = line1 is not None and len(line1) < len(line2)
+ self.merge_se = line3 is not None and len(line3) < len(line2)
+ # Define strings at various cardinal directions.
+ if line1 is None:
+ self.nw = self.nn = self.ne = None
+ elif self.merge_nw:
+ self.nw = line1
+ self.nn = self.ne = None
+ else:
+ if start > 0:
+ self.nw = line1[:start]
+ else:
+ self.nw = None
+ if line1[start] != ' ':
+ self.nn = line1[start]
+ else:
+ self.nn = None
+ if end < len(line1):
+ self.ne = string.rstrip(line1[end:])
+ else:
+ self.ne = None
+ if start > 0:
+ self.ww = line2[:start]
+ else:
+ self.ww = None
+ if end < len(line2):
+ self.ee = line2[end:]
+ else:
+ self.ee = None
+ if line3 is None:
+ self.sw = self.ss = self.se = None
+ elif self.merge_se:
+ self.sw = self.ss = None
+ self.se = string.rstrip(line3)
+ else:
+ if start > 0:
+ self.sw = line3[:start]
+ else:
+ self.sw = None
+ if line3[start] != ' ':
+ self.ss = line3[start]
+ else:
+ self.ss = None
+ if end < len(line3):
+ self.se = string.rstrip(line3[end:])
+ else:
+ self.se = None
+ # Define parsing regexps.
+ if self.merge_nw:
+ self.regexp1 = re.compile(' *' + regexp_quote(self.nw) + '.*$')
+ elif self.nw and not self.nn and not self.ne:
+ self.regexp1 = re.compile(' *' + regexp_quote(self.nw) + '$')
+ elif self.nw or self.nn or self.ne:
+ self.regexp1 = re.compile(
+ ' *' + regexp_quote(self.nw) + regexp_ruler(self.nn)
+ + regexp_quote(self.ne) + '$')
+ else:
+ self.regexp1 = None
+ if self.ww or self.ee:
+ self.regexp2 = re.compile(
+ ' *' + regexp_quote(self.ww) + '.*'
+ + regexp_quote(self.ee) + '$')
+ else:
+ self.regexp2 = None
+ if self.merge_se:
+ self.regexp3 = re.compile('.*' + regexp_quote(self.se) + '$')
+ elif self.sw and not self.ss and not self.se:
+ self.regexp3 = re.compile(' *' + regexp_quote(self.sw) + '$')
+ elif self.sw or self.ss or self.se:
+ self.regexp3 = re.compile(
+ ' *' + regexp_quote(self.sw) + regexp_ruler(self.ss)
+ + regexp_quote(self.se) + '$')
+ else:
+ self.regexp3 = None
+ # Save results.
+ template_registry[style] = self
+
+ def __cmp__(self, other):
+ return cmp(self.weight, other.weight)
+
+ def characters(self):
+ """\
+Return a string of characters which may be used to draw the box.
+"""
+ characters = ''
+ for text in (self.nw, self.nn, self.ne,
+ self.ww, self.ee,
+ self.sw, self.ss, self.se):
+ if text:
+ for character in text:
+ if character not in characters:
+ characters = characters + character
+ return characters
+
+ def match(self, lines):
+ """\
+Returns true if LINES exactly match this template.
+"""
+ start = 0
+ end = len(lines)
+ if self.regexp1 is not None:
+ if start == end or not self.regexp1.match(lines[start]):
+ return 0
+ start = start + 1
+ if self.regexp3 is not None:
+ if end == 0 or not self.regexp3.match(lines[end-1]):
+ return 0
+ end = end - 1
+ if self.regexp2 is not None:
+ for line in lines[start:end]:
+ if not self.regexp2.match(line):
+ return 0
+ return 1
+
+ def unbuild(self, lines):
+ """\
+Remove all comment marks from LINES, as hinted by this template. Returns the
+cleaned up set of lines, and the size of the left margin.
+"""
+ margin = left_margin_size(lines)
+ # Remove box style marks.
+ start = 0
+ end = len(lines)
+ if self.regexp1 is not None:
+ lines[start] = unbuild_clean(lines[start], self.regexp1)
+ start = start + 1
+ if self.regexp3 is not None:
+ lines[end-1] = unbuild_clean(lines[end-1], self.regexp3)
+ end = end - 1
+ if self.regexp2 is not None:
+ for counter in range(start, end):
+ lines[counter] = unbuild_clean(lines[counter], self.regexp2)
+ # Remove the left side of the box after it turned into spaces.
+ delta = left_margin_size(lines) - margin
+ for counter in range(len(lines)):
+ lines[counter] = lines[counter][delta:]
+ # Remove leading and trailing white lines.
+ start = 0
+ end = len(lines)
+ while start < end and lines[start] == '':
+ start = start + 1
+ while end > start and lines[end-1] == '':
+ end = end - 1
+ return lines[start:end], margin
+
+ def build(self, lines, width, refill, margin):
+ """\
+Put LINES back into a boxed comment according to this template, after
+having refilled them if REFILL. The box should start at column MARGIN,
+and the total size of each line should ideally not go over WIDTH.
+"""
+ # Merge a short end delimiter now, so it gets refilled with text.
+ if self.merge_se:
+ if lines:
+ lines[-1] = lines[-1] + ' ' + self.se
+ else:
+ lines = [self.se]
+ # Reduce WIDTH according to left and right inserts, then refill.
+ if self.ww:
+ width = width - len(self.ww)
+ if self.ee:
+ width = width - len(self.ee)
+ if refill:
+ lines = refill_lines(lines, width)
+ # Reduce WIDTH further according to the current right margin,
+ # and excluding the left margin.
+ maximum = 0
+ for line in lines:
+ if line:
+ if line[-1] in '.!?':
+ length = len(line) + 1
+ else:
+ length = len(line)
+ if length > maximum:
+ maximum = length
+ width = maximum - margin
+ # Construct the top line.
+ if self.merge_nw:
+ lines[0] = ' ' * margin + self.nw + lines[0][margin:]
+ start = 1
+ elif self.nw or self.nn or self.ne:
+ if self.nn:
+ line = self.nn * width
+ else:
+ line = ' ' * width
+ if self.nw:
+ line = self.nw + line
+ if self.ne:
+ line = line + self.ne
+ lines.insert(0, string.rstrip(' ' * margin + line))
+ start = 1
+ else:
+ start = 0
+ # Construct all middle lines.
+ for counter in range(start, len(lines)):
+ line = lines[counter][margin:]
+ line = line + ' ' * (width - len(line))
+ if self.ww:
+ line = self.ww + line
+ if self.ee:
+ line = line + self.ee
+ lines[counter] = string.rstrip(' ' * margin + line)
+ # Construct the bottom line.
+ if self.sw or self.ss or self.se and not self.merge_se:
+ if self.ss:
+ line = self.ss * width
+ else:
+ line = ' ' * width
+ if self.sw:
+ line = self.sw + line
+ if self.se and not self.merge_se:
+ line = line + self.se
+ lines.append(string.rstrip(' ' * margin + line))
+ return lines
+
+def regexp_quote(text):
+ """\
+Return a regexp matching TEXT without its surrounding space, maybe
+followed by spaces. If STRING is nil, return the empty regexp.
+Unless spaces, the text is nested within a regexp parenthetical group.
+"""
+ if text is None:
+ return ''
+ if text == ' ' * len(text):
+ return ' *'
+ return '(' + re.escape(string.strip(text)) + ') *'
+
+def regexp_ruler(character):
+ """\
+Return a regexp matching two or more repetitions of CHARACTER, maybe
+followed by spaces. Is CHARACTER is nil, return the empty regexp.
+Unless spaces, the ruler is nested within a regexp parenthetical group.
+"""
+ if character is None:
+ return ''
+ if character == ' ':
+ return ' +'
+ return '(' + re.escape(character + character) + '+) *'
+
+def unbuild_clean(line, regexp):
+ """\
+Return LINE with all parenthetical groups in REGEXP erased and replaced by an
+equivalent number of spaces, except for trailing spaces, which get removed.
+"""
+ match = re.match(regexp, line)
+ groups = match.groups()
+ for counter in range(len(groups)):
+ if groups[counter] is not None:
+ start, end = match.span(1 + counter)
+ line = line[:start] + ' ' * (end - start) + line[end:]
+ return string.rstrip(line)
+
+## Template data.
+
+# Matcher functions for a comment start, indexed by numeric LANGUAGE.
+language_matcher = []
+for pattern in (r' *(/\*|//+|#+|;+|%+)',
+ r'', # 1
+ r' */\*', # 2
+ r' *//+', # 3
+ r' *#+', # 4
+ r' *;+', # 5
+ r' *%+'): # 6
+ language_matcher.append(re.compile(pattern).match)
+
+# Template objects, indexed by numeric style.
+template_registry = {}
+
+def make_generic(style, weight, lines):
+ """\
+Add various language digit to STYLE and generate one template per language,
+all using the same WEIGHT. Replace `?' in LINES accordingly.
+"""
+ for language, character in ((300, '/'), # C++ style comments
+ (400, '#'), # scripting languages
+ (500, ';'), # LISP and assembler
+ (600, '%')): # TeX and PostScript
+ new_style = language + style
+ if 310 < new_style <= 319:
+ # Disallow quality 10 with C++.
+ continue
+ new_lines = []
+ for line in lines:
+ new_lines.append(string.replace(line, '?', character))
+ Template(new_style, weight, new_lines)
+
+# Generic programming language templates.
+
+make_generic(11, 115, ('? box',))
+
+make_generic(12, 215, ('? box ?',
+ '? --- ?'))
+
+make_generic(13, 315, ('? --- ?',
+ '? box ?',
+ '? --- ?'))
+
+make_generic(14, 415, ('? box ?',
+ '???????'))
+
+make_generic(15, 515, ('???????',
+ '? box ?',
+ '???????'))
+
+make_generic(21, 125, ('?? box',))
+
+make_generic(22, 225, ('?? box ??',
+ '?? --- ??'))
+
+make_generic(23, 325, ('?? --- ??',
+ '?? box ??',
+ '?? --- ??'))
+
+make_generic(24, 425, ('?? box ??',
+ '?????????'))
+
+make_generic(25, 525, ('?????????',
+ '?? box ??',
+ '?????????'))
+
+make_generic(31, 135, ('??? box',))
+
+make_generic(32, 235, ('??? box ???',
+ '??? --- ???'))
+
+make_generic(33, 335, ('??? --- ???',
+ '??? box ???',
+ '??? --- ???'))
+
+make_generic(34, 435, ('??? box ???',
+ '???????????'))
+
+make_generic(35, 535, ('???????????',
+ '??? box ???',
+ '???????????'))
+
+make_generic(41, 145, ('???? box',))
+
+make_generic(42, 245, ('???? box ????',
+ '???? --- ????'))
+
+make_generic(43, 345, ('???? --- ????',
+ '???? box ????',
+ '???? --- ????'))
+
+make_generic(44, 445, ('???? box ????',
+ '?????????????'))
+
+make_generic(45, 545, ('?????????????',
+ '???? box ????',
+ '?????????????'))
+
+# Textual (non programming) templates.
+
+Template(111, 113, ('box',))
+
+Template(112, 213, ('| box |',
+ '+-----+'))
+
+Template(113, 313, ('+-----+',
+ '| box |',
+ '+-----+'))
+
+Template(114, 413, ('| box |',
+ '*=====*'))
+
+Template(115, 513, ('*=====*',
+ '| box |',
+ '*=====*'))
+
+Template(121, 123, ('| box |',))
+
+Template(122, 223, ('| box |',
+ '`-----\''))
+
+Template(123, 323, ('.-----.',
+ '| box |',
+ '`-----\''))
+
+Template(124, 423, ('| box |',
+ '\\=====/'))
+
+Template(125, 523, ('/=====\\',
+ '| box |',
+ '\\=====/'))
+
+Template(141, 143, ('| box ',))
+
+Template(142, 243, ('* box *',
+ '*******'))
+
+Template(143, 343, ('*******',
+ '* box *',
+ '*******'))
+
+Template(144, 443, ('X box X',
+ 'XXXXXXX'))
+
+Template(145, 543, ('XXXXXXX',
+ 'X box X',
+ 'XXXXXXX'))
+# C language templates.
+
+Template(211, 118, ('/* box */',))
+
+Template(212, 218, ('/* box */',
+ '/* --- */'))
+
+Template(213, 318, ('/* --- */',
+ '/* box */',
+ '/* --- */'))
+
+Template(214, 418, ('/* box */',
+ '/* === */'))
+
+Template(215, 518, ('/* === */',
+ '/* box */',
+ '/* === */'))
+
+Template(221, 128, ('/* ',
+ ' box',
+ '*/'))
+
+Template(222, 228, ('/* .',
+ '| box |',
+ '`----*/'))
+
+Template(223, 328, ('/*----.',
+ '| box |',
+ '`----*/'))
+
+Template(224, 428, ('/* \\',
+ '| box |',
+ '\\====*/'))
+
+Template(225, 528, ('/*====\\',
+ '| box |',
+ '\\====*/'))
+
+Template(231, 138, ('/* ',
+ ' | box',
+ ' */ '))
+
+Template(232, 238, ('/* ',
+ ' | box | ',
+ ' *-----*/'))
+
+Template(233, 338, ('/*-----* ',
+ ' | box | ',
+ ' *-----*/'))
+
+Template(234, 438, ('/* box */',
+ '/*-----*/'))
+
+Template(235, 538, ('/*-----*/',
+ '/* box */',
+ '/*-----*/'))
+
+Template(241, 148, ('/* ',
+ ' * box',
+ ' */ '))
+
+Template(242, 248, ('/* * ',
+ ' * box * ',
+ ' *******/'))
+
+Template(243, 348, ('/******* ',
+ ' * box * ',
+ ' *******/'))
+
+Template(244, 448, ('/* box */',
+ '/*******/'))
+
+Template(245, 548, ('/*******/',
+ '/* box */',
+ '/*******/'))
+
+Template(251, 158, ('/* ',
+ ' * box',
+ ' */ '))
+
+if __name__ == '__main__':
+ apply(main, sys.argv[1:])
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/ide-integration/Pymacs-0.20/README Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,1030 @@
+* README for `Pymacs' allout -*- outline -*-
+
+ `http://www.iro.umontreal.ca/~pinard/pymacs/' contains a copy of this
+ `README' file in HTML form. The canonical Pymacs distribution is
+ available as `http://www.iro.umontreal.ca/~pinard/pymacs/Pymacs.tar.gz'.
+ Report problems and suggestions to `mailto:pinard@iro.umontreal.ca'.
+
+.. Presentation.
+
+. : What is Pymacs?
+
+ Pymacs is a powerful tool which, once started from Emacs, allows both-way
+ communication between Emacs Lisp and Python. Yet, Pymacs aims Python as
+ an extension language for Emacs rather than the other way around; this
+ assymetry is reflected in some design choices. Within Emacs Lisp code,
+ one may load and use Python modules. Python functions may themselves use
+ Emacs services, and handle Emacs Lisp objects kept in Emacs Lisp space.
+
+ The goals are to write "naturally" in both languages, debug with ease,
+ fall back gracefully on errors, and allow full cross-recursivity.
+
+ It is very easy to install Pymacs, as neither Emacs nor Python need to
+ be compiled nor relinked. Emacs merely starts Python as a subprocess,
+ and Pymacs implements a communication protocol between both processes.
+
+. : Warning to Pymacs users.
+
+ I expect average Pymacs users to have a deeper knowledge of Python
+ than Emacs Lisp. Some examples at the end of this file are meant
+ for Python users having a limited experience with the Emacs API.
+ Currently, there are only contains two examples, one is too small,
+ the other is too big :-). As there is no dedicated mailing list nor
+ discussion group for Pymacs, let's use `python-list@python.org' for
+ asking questions or discussing Pymacs related matters.
+
+ This is beta status software: specifications are slightly frozen, yet
+ changes may still happen that would require small adaptations in your
+ code. Report problems to François Pinard at `pinard@iro.umontreal.ca'.
+ For discussing specifications or making suggestions, please also copy
+ the `python-list@python.org' mailing list, to help brain-storming! :-)
+
+. : History and references.
+
+ I once starved for a Python-extensible editor, and pondered the idea of
+ dropping Emacs for other avenues, but found nothing much convincing.
+ Moreover, looking at all LISP extensions I wrote for myself, and
+ considering all those superb tools written by others and that became
+ part of my computer life, it would have been a huge undertaking for
+ me to reprogram these all in Python. So, when I began to see that
+ something like Pymacs was possible, I felt strongly motivated! :-)
+
+ Pymacs revisits previous Cedric Adjih's works about running Python as a
+ process separate from Emacs. See `http://www.crepuscule.com/pyemacs/',
+ or write Cedric at `adjih-pam@crepuscule.com'. Cedric presented
+ `pyemacs' to me as a proof of concept. As I simplified that concept
+ a bit, I dropped the `e' in `pyemacs' :-). Cedric also told me that
+ there exist some older patches for linking Python right into XEmacs.
+
+ Brian McErlean independently and simultaneously wrote a tool similar
+ to this one, we decided to join our projects. Amusing coincidence, he
+ even chose `pymacs' as a name. Brian paid good attention to complex
+ details that escaped my courage, so his help and collaboration have
+ been beneficial. You may reach Brian at `brianmce@crosswinds.net'.
+
+ One other reference of interest is Doug Bagley shoot out project,
+ which compares the relative speed of many popular languages.
+ See `http://www.bagley.org/~doug/shootout/' for more information.
+
+.. Installation.
+
+. : Install the Pymacs proper.
+
+ Currently, there are two installation scripts, and both should be run.
+ If you prefer, you may use `make install lispdir=LISPDIR', where
+ LISPDIR is some directory along the list kept in your Emacs `load-path'.
+
+ The first installation script installs the Python package, including the
+ Pymacs examples, using the Python standard Distutils tool. Merely `cd'
+ into the Pymacs distribution, then execute `python setup.py install'.
+ To get an option reminder, do `python setup.py install --help'. Check
+ the Distutils documentation if you need more information about this.
+
+ The second installation script installs the Emacs Lisp part only.
+ (It used to do everything, but is now doomed to disappear completely.)
+ Merely `cd' into the Pymacs distribution, then run `python setup -ie'.
+ This will invite you to interactively confirm the Lisp installation
+ directory. Without `-ie', the Lisp part of Pymacs will be installed
+ in some automatically guessed place. Use `-n' to known about the guess
+ without proceeding to the actual installation. `./setup -E xemacs ...'
+ may be useful to XEmacs lovers. See `./setup -H' for all options.
+
+ About Win32 systems, Syver Enstad says: "For Pymacs to operate correctly,
+ one should create a batch file with `pymacs-services.bat' as a name,
+ which runs the `pymacs-services' script. The `.bat' file could be
+ placed along with `pymacs-services', wherever that maybe.".
+
+ To check that `pymacs.el' is properly installed, start Emacs and give
+ it the command `M-x load-library RET pymacs': you should not receive
+ any error. To check that `pymacs.py' is properly installed, start
+ an interactive Python session and type `from Pymacs import lisp':
+ you should not receive any error. To check that `pymacs-services'
+ is properly installed, type `pymacs-services </dev/null' in a shell;
+ you should then get a line ending with "(pymacs-version VERSION)",
+ and another saying: "Protocol error: `>' expected.".
+
+ Currently, there is only one installed Pymacs example, which comes
+ in two parts: a batch script `rebox' and a `Pymacs.rebox' module.
+ To check that both are properly installed, type `rebox </dev/null'
+ in a shell; you should not receive any output nor see any error.
+
+. : Prepare your `.emacs' file.
+
+ The ".emacs" file is not given in the distribution, you likely have
+ one already in your home directory. You need to add these lines:
+
+ (autoload 'pymacs-load "pymacs" nil t)
+ (autoload 'pymacs-eval "pymacs" nil t)
+ (autoload 'pymacs-apply "pymacs")
+ (autoload 'pymacs-call "pymacs")
+ ;;(eval-after-load "pymacs"
+ ;; '(add-to-list 'pymacs-load-path "YOUR-PYMACS-DIRECTORY"))
+
+ If you plan to use a special directory to hold your own Pymacs code in
+ Python, which should be searched prior to the usual Python import search
+ path, then uncomment the last two lines (by removing the semi-colons)
+ and replace YOUR-PYMACS-DIRECTORY by the name of your special directory.
+ If the file "$HOME/.emacs" does not exist, merely create it with the
+ above lines. You are now all set to use Pymacs.
+
+ To check this, start a fresh Emacs session, and type `M-x pymacs-eval'.
+ Emacs should prompt you for a Python expression. Try "`2L**111`" (type
+ the backquotes, but not the external double-quotes). The minibuffer
+ should display `2596148429267413814265248164610048L'. `M-x pymacs-load'
+ should prompt you for a Python module name. Reply `os'. After Emacs
+ prompts you for a prefix, merely hit Enter to accept the default prefix.
+ This should have the effect of importing the Python "os" module within
+ Emacs. Typing `M-: (os-getcwd)' should echo the current directory in
+ the message buffer, as returned by the `os.getcwd' Python function.
+
+. : Porting Pymacs.
+
+ Pymacs has been developped on Linux and Emacs (20 and 21), it is expected
+ to work out of the box on most other Unices, and also with XEmacs.
+
+ Syver Enstad reports that Pymacs could be made to work on Windows-2000
+ (win2k), he suspects it should equally work with NT and XP. However,
+ little shell stunts may be required, I hope to later document them here.
+
+. : Caveats.
+
+ Some later versions of Emacs 20 silently ignore the request for
+ creating weak hash tables, they create an ordinary table instead.
+ Older Emacses just do not have hash tables. Pymacs should run on
+ all, yet for these, memory will leak on the Python side whenever
+ complex objects get transmitted to Emacs, as these objects will not
+ be reclaimed on the Python side once Emacs is finished with them.
+ It should not be a practical problem in most simple cases.
+
+.. Emacs Lisp structures and Python objects.
+
+. : Conversions.
+
+ Whenever Emacs Lisp calls Python functions giving them arguments,
+ these arguments are Emacs Lisp structures that should be converted
+ into Python objects in some way. Conversely, whenever Python calls
+ Emacs Lisp functions, the arguments are Python objects that should
+ be received as Emacs Lisp structures. We need some conventions for
+ doing such conversions.
+
+ Conversions generally transmit mutable Emacs Lisp structures as mutable
+ objects on the Python side, in such a way that transforming the object in
+ Python will effectively transform the structure on the Emacs Lisp side
+ (strings are handled a bit specially however, see below). The other
+ way around, Python objects transmitted to Emacs Lisp often loose their
+ mutability, so transforming the Emacs Lisp structure is not reflected
+ on the Python side.
+
+. : Simple objects.
+
+ Emacs Lisp `nil' and the equivalent Emacs Lisp `()' yield Python `None'.
+ Python `None' and the Python empty list `[]' are returned as `nil'
+ in Emacs Lisp.
+
+ Emacs Lisp numbers, either integer or floating, are converted in
+ equivalent Python numbers. Emacs Lisp characters are really numbers
+ and yield Python numbers. In the other direction, Python numbers are
+ converted into Emacs Lisp numbers, with the exception of long Python
+ integers and complex numbers.
+
+ Emacs Lisp strings are usually converted into equivalent Python narrow
+ strings. As Python strings do not have text properties, these are not
+ reflected. This may be changed by setting the `pymacs-mutable-strings'
+ option: if this variable is not `nil', Emacs Lisp strings are then
+ transmitted opaquely. Python strings, except Unicode, are always
+ converted into Emacs Lisp strings.
+
+ Emacs Lisp symbols yield the special `lisp.SYMBOL' or `lisp[STRING]'
+ notations on the Python side. The first notation is used when the
+ Emacs Lisp symbol starts with a letter, and contains only letters,
+ digits and hyphens, in which case Emacs Lisp hyphens get replaced
+ by Python underscores. This convention is welcome, as Emacs Lisp
+ programmers commonly prefer using dashes, where Python programmers
+ use underlines. Otherwise, the second notation is used. Conversely,
+ `lisp.SYMBOL' on the Python side yields an Emacs Lisp symbol with
+ underscores replaced with hyphens, while `lisp[STRING]' corresponds
+ to an Emacs Lisp symbol printed with that STRING which, of course,
+ should then be a valid Emacs Lisp symbol name.
+
+. : Sequences.
+
+ The case of strings has been discussed in the previous section.
+
+ Proper Emacs Lisp lists, those for which the `cdr' of last
+ cell is `nil', are normally transmitted opaquely to Python.
+ If `pymacs-forget-mutability' is set, or if Python later asks for these
+ to be expanded, proper Emacs Lisp lists get converted into Python lists,
+ if we except the empty list, which is always converted as Python `None'.
+ In the other direction, Python lists are always converted into proper
+ Emacs Lisp lists.
+
+ Emacs Lisp vectors are normally transmitted opaquely to Python. However,
+ if `pymacs-forget-mutability' is set, or if Python later asks for these
+ to be expanded, Emacs Lisp vectors get converted into Python tuples.
+ In the other direction, Python tuples are always converted into Emacs
+ Lisp vectors.
+
+ Remember the rule: "Round parentheses correspond to square brackets!".
+ It works for lists, vectors, tuples, seen from either Emacs Lisp or Python.
+
+ The above choices were debatable. Since Emacs Lisp proper lists and
+ Python lists are the bread-and-butter of algorithms modifying structures,
+ at least in my experience, I guess they are more naturally mapped into
+ one another, this spares many casts in practice. While in Python,
+ the most usual idiom for growing lists is appending to their end,
+ the most usual idiom in Emacs Lisp to grow a list is by cons'ing new
+ items at its beginning:
+
+ (setq accumulator (cons 'new-item accumulator))
+
+ or more simply:
+
+ (push accumulator 'new-item)
+
+ So, in case speed is especially important and many modifications happen
+ in a row on the same side, while order of elements ought to be preserved,
+ some (nreverse ...) on the Emacs Lisp side or .reverse() on the Python
+ side side might be needed. Surely, proper lists in Emacs Lisp and lists
+ in Python are the normal structure for which length is easily modified.
+
+ We cannot so easily change the size of a vector, the same as it is a
+ bit more of a stunt to "modify" a tuple. The shape of these objects is
+ fixed. Mapping vectors to tuples, which is admittedly strange, will only
+ be done if the Python side requests an expanded copy, otherwise an opaque
+ Emacs Lisp object is seen in Python. In the other direction, whenever
+ an Emacs Lisp vector is needed, one has to write `tuple(python_list)'
+ while transmitting the object. Such transmissions are most probably
+ to be unusual, as people are not going to blindly transmit whole big
+ structures back and forth between Emacs and Python, they would rather do
+ it once in a while only, and do only local modifications afterwards.
+ The infrequent casting to `tuple' for getting an Emacs Lisp vector
+ seems to suggest that we did a reasonable compromise.
+
+ In Python, both tuples and lists have O(1) access, so there is no real
+ speed consideration there. Emacs Lisp is different: vectors have O(1)
+ access while lists have O(N) access. The rigidity of Emacs Lisp
+ vectors is such that people do not resort to vectors unless there
+ is a speed issue, so in real Emacs Lisp practice, vectors are used
+ rather parsimoniously. So much, in fact, that Emacs Lisp vectors are
+ overloaded for what they are not meant: for example, very small vectors
+ are used to represent X events in key-maps, programmers only want
+ to test vectors for their type, or users just like bracketed syntax.
+ The speed of access is hardly an issue then.
+
+. : Opaque objects.
+
+. , Emacs Lisp handles.
+
+ When a Python function is called from Emacs Lisp, the function arguments
+ have already been converted to Python types from Emacs Lisp types
+ and the function result is going to be converted back to Emacs Lisp.
+
+ Several Emacs Lisp objects do not have Python equivalents, like for
+ Emacs windows, buffers, markers, overlays, etc. It is nevertheless
+ useful to pass them to Python functions, hoping that these Python
+ functions will "operate" on these Emacs Lisp objects. Of course,
+ the Python side may not itself modify such objects, it has to call
+ for Emacs services to do so. Emacs Lisp handles are a mean to ease
+ this communication.
+
+ Whenever an Emacs Lisp object may not be converted to a Python object, an
+ Emacs Lisp handle is created and used instead. Whenever that Emacs Lisp
+ handle is returned into Emacs Lisp from a Python function, or is used
+ as an argument to an Emacs Lisp function from Python, the original Emacs
+ Lisp object behind the Emacs Lisp handle is automatically retrieved.
+
+ Emacs Lisp handles are either instances of the internal `Lisp' class,
+ or of one of its subclasses. If `object' is an Emacs Lisp handle, and
+ if the underlying Emacs Lisp object is an Emacs Lisp sequence, then
+ whenever `object[index]', `object[index] = value' and `len(object)'
+ are meaningful, these may be used to fetch or alter an element of the
+ sequence directly in Emacs Lisp space. Also, if `object' corresponds
+ to an Emacs Lisp function, `object(ARGUMENTS)' may be used to apply
+ the Emacs Lisp function over the given arguments. Since arguments
+ have been evaluated the Python way on the Python side, it would be
+ conceptual overkill evaluating them again the Emacs Lisp way on the
+ Emacs Lisp side, so Pymacs manage to quotes arguments for defeating
+ Emacs Lisp evaluation. The same logic applies the other way around.
+
+ Emacs Lisp handles have a `value()' method, which merely returns self.
+ They also have a `copy()' method, which tries to "open the box"
+ if possible. Emacs Lisp proper lists are turned into Python lists,
+ Emacs Lisp vectors are turned into Python tuples. Then, modifying
+ the structure of the copy on the Python side has no effect on the
+ Emacs Lisp side.
+
+ For Emacs Lisp handles, `str()' returns an Emacs Lisp representation
+ of the handle which should be `eq' to the original object if read back
+ and evaluated in Emacs Lisp. `repr()' returns a Python representation
+ of the expanded Emacs Lisp object. If that Emacs Lisp object has
+ an Emacs Lisp representation which Emacs Lisp could read back, then
+ `repr()' value is such that it could be read back and evaluated in
+ Python as well, this would result in another object which is `equal'
+ to the original, but not neccessarily `eq'.
+
+. , Python handles.
+
+ The same as Emacs Lisp handles are useful to handle Emacs Lisp objects
+ on the Python side, Python handles are useful to handle Python objects
+ on the Emacs Lisp side.
+
+ Many Python objects do not have direct Emacs Lisp equivalents,
+ including long integers, complex numbers, Unicode strings, modules,
+ classes, instances and surely a lot of others. When such are being
+ transmitted to the Emacs Lisp side, Pymacs use Python handles.
+ These are automatically recovered into the original Python objects
+ whenever transmitted back to Python, either as arguments to a Python
+ function, as the Python function itself, or as the return value of
+ an Emacs Lisp function called from Python.
+
+ The objects represented by these Python handles may be inspected or
+ modified using the basic library of Python functions. For example, in:
+
+ (setq matcher (pymacs-eval "re.compile('PATTERN').match"))
+ (pymacs-call matcher ARGUMENT)
+
+ the initial `setq' above could be decomposed into:
+
+ (setq compiled (pymacs-eval "re.compile('PATTERN')")
+ matcher (pymacs-call "getattr" compiled "match"))
+
+ This example shows that one may use `pymacs-call' with "getattr"
+ as the function, to get a wanted attribute for a Python object.
+
+.. Usage on the Emacs Lisp side.
+
+. : `pymacs-eval'.
+
+ Function `(pymacs-eval TEXT)' gets TEXT evaluated as a Python expression,
+ and returns the value of that expression converted back to Emacs Lisp.
+
+. : `pymacs-call'.
+
+ Function `(pymacs-call FUNCTION ARGUMENT...)' will get Python to apply
+ the given FUNCTION over zero or more ARGUMENT. FUNCTION is either a
+ string holding Python source code for a function (like a mere name, or
+ even an expression), or else, a Python handle previously received from
+ Python, and hopefully holding a callable Python object. Each ARGUMENT
+ gets separately converted to Python before the function is called.
+ `pymacs-call' returns the resulting value of the function call,
+ converted back to Emacs Lisp.
+
+. : `pymacs-apply'.
+
+ Function `(pymacs-apply FUNCTION ARGUMENTS)' will get Python to apply
+ the given FUNCTION over the given ARGUMENTS. ARGUMENTS is a list
+ containing all arguments, or `nil' if there is none. Besides arguments
+ being bundled together instead of given separately, the function acts
+ pretty much like `pymacs-call'.
+
+. : `pymacs-load'.
+
+ Function `(pymacs-load MODULE PREFIX)' imports the Python MODULE into
+ Emacs Lisp space. MODULE is the name of the file containing the module,
+ without any `.py' or `.pyc' extension. If the directory part is omitted
+ in MODULE, the module will be looked into the current Python search
+ path. Dot notation may be used when the module is part of a package.
+ Each top-level function in the module produces a trampoline function in
+ Emacs Lisp having the same name, except that underlines in Python names
+ are turned into dashes in Emacs Lisp, and that PREFIX is uniformly
+ added before the Emacs Lisp name (as a way to avoid name clashes).
+ PREFIX may be omitted, in which case it defaults to base name of MODULE
+ with underlines turned into dashes, and followed by a dash.
+
+ Whenever `pymacs_load_hook' is defined in the loaded Python module,
+ `pymacs-load' calls it without arguments, but before creating the Emacs
+ view for that module. So, the `pymacs_load_hook' function may create
+ new definitions or even add `interaction' attributes to functions.
+
+ The return value of a successful `pymacs-load' is the module object.
+ An optional third argument, NOERROR, when given and not `nil', will
+ have `pymacs-load' to return `nil' instead of raising an error, if
+ the Python module could not be found.
+
+ When later calling one of these trampoline functions, all provided
+ arguments are converted to Python and transmitted, and the function
+ return value is later converted back to Emacs Lisp. It is left to
+ the Python side to check for argument consistency. However, for
+ an interactive function, the interaction specification drives some
+ checking on the Emacs Lisp side. Currently, there is no provision
+ for collecting keyword arguments in Emacs Lisp.
+
+. : Expected usage.
+
+ We do not expect that `pymacs-eval', `pymacs-call' or `pymacs-apply'
+ will be much used, if ever. In practice, the Emacs Lisp side
+ of a Pymacs application might call `pymacs-load' a few times for
+ linking into the Python modules, with the indirect effect of defining
+ trampoline functions for these modules on the Emacs Lisp side, which
+ can later be called like usual Emacs Lisp functions.
+
+ These imported functions are usually those which are of interest for
+ the user, and the preferred way to call Python services with Pymacs.
+
+. : Special Emacs Lisp variables.
+
+ Users could alter the inner working of Pymacs through a few variables,
+ these are all documented here. Except for `pymacs-load-path', which
+ should be set before calling any Pymacs function, the value of these
+ variables can be changed at any time.
+
+. , pymacs-load-path
+
+ Users might want to use special directories for holding their Python
+ modules, when these modules are meant to be used from Emacs. Best is
+ to preset `pymacs-load-path, `nil' by default, to a list of these
+ directory names. (Tilde expansions and such occur automatically.)
+
+ Here is how it works. The first time Pymacs is needed from Emacs,
+ `pymacs-services' is called, and given as arguments all strings in the
+ `pymacs-load-path' list. These arguments are added at the beginning
+ of `sys.path', or moved at the beginning if they were already on
+ `sys.path'. So in practice, nothing is removed from `sys.path'.
+
+. , pymacs-trace-transit
+
+ The `*Pymacs*' buffer, within Emacs, holds a trace of transactions
+ between Emacs and Python. When `pymacs-trace-transit' is `nil',
+ and this is the default setting, the buffer only holds the last
+ bi-directional transaction (a request and a reply). If that variable
+ is not `nil', all transactions are kept. This could be useful for
+ debugging, but the drawback is that this buffer could grow big over
+ time, to the point of diminishing Emacs performance.
+
+. , pymacs-forget-mutability
+
+ The default behaviour of Pymacs is to transmit Emacs Lisp objects to
+ Python in such a way thay they are fully modifiable from the Python
+ side, would it mean triggering Emacs Lisp functions to act on them.
+ When `pymacs-forget-mutability' is not `nil', the behaviour is changed,
+ and the flexibility is lost. Pymacs then tries to expand proper lists
+ and vectors as full copies when transmitting them on the Python side.
+ This variable, seen as a user setting, is best left to `nil'. It may
+ be temporarily overriden within some functions, when deemed useful.
+
+ There is no corresponding variable from objects transmitted to Emacs
+ from Python. Pymacs automatically expands what gets transmitted.
+ Mutability is preserved only as a side-effect of not having a natural
+ Emacs Lisp representation for the Python object. This assymetry is on
+ purpose, yet debatable. Maybe Pymacs could have a variable telling
+ that mutability _is_ important for Python objects? That would give
+ Pymacs users the capability of restoring the symmetry somewhat,
+ yet so far, in our experience, this has never been needed.
+
+. , pymacs-mutable-strings
+
+ Strictly speaking, Emacs Lisp strings are mutable. Yet, it does not
+ come naturally to a Python programmer to modify a string "in-place",
+ as Python strings are never mutable. When `pymacs-mutable-strings' is
+ `nil', which is the default setting, Emacs Lisp strings are transmitted
+ to Python as Python strings, and so, loose their mutability.
+ Moreover, text properties are not reflected on the Python side.
+ But if that variable is not `nil', Emacs Lisp strings are rather
+ passed as Emacs Lisp handles. This variable is ignored whenever
+ `pymacs-forget-mutability' is set.
+
+. , pymacs-timeout-at-start
+. , pymacs-timeout-at-reply
+. , pymacs-timeout-at-line
+
+ Emacs needs to protect itself a bit, in case the Pymacs service program,
+ which handles the Python side of requests, would not start correctly,
+ or maybe later die unexpectedly. So, whenever Emacs reads data coming
+ from that program, it sets a time limit, and take some action whenever
+ that time limit expires. All times are expressed in seconds.
+
+ The timeout at start defaults to 30 seconds, this time should only
+ be increased if a given machine is so heavily loaded that the Pymacs
+ service program has not enough of 30 seconds to start, in which case
+ Pymacs refuses to work, with an appropriate message in the minibuffer.
+
+ The two other variables almost never need to be changed in practice.
+ When Emacs is expecting a reply from Python, it might repeatedly
+ check the status of the Pymacs service program when that reply is not
+ received fast enough, just to make sure that this program did not die.
+ The timeout at reply, which defaults to 5, says how many seconds
+ to wait without checking, while expecting the first line of a reply.
+ The timeout at line, which defaults to 2, says how many seconds to wait
+ without checking, while expecting a line of the reply after the first.
+
+.. Usage on the Python side.
+
+. : Python setup.
+
+ Pymacs requires little or no setup in the Python modules which are
+ meant to be used from Emacs, for the simple situations where these
+ modules receive nothing but Emacs nil, numbers or strings, or return
+ nothing but Python `None', numbers or strings.
+
+ Otherwise, use `from Pymacs import lisp'. If you need more Pymacs
+ features, like the `Let' class, write `from Pymacs import lisp, Let'.
+
+. : Response mode.
+
+ When Python receives a request from Emacs in the context of Pymacs,
+ and until it returns the reply, Emacs keeps listening to serve Python
+ requests. Emacs is not listening otherwise. Other Python threads,
+ if any, may not call Emacs without _very_ careful synchronisation.
+
+. : Emacs Lisp symbols.
+
+ `lisp' is a special object which has useful built-in magic.
+ Its attributes do nothing but represent Emacs Lisp symbols, created
+ on the fly as needed (symbols also have their built-in magic).
+
+ Except for `lisp.nil' or `lisp["nil"]', which are the same as `None',
+ both `lisp.SYMBOL' and `lisp[STRING]' yield objects of the internal
+ `Symbol' type. These are genuine Python objects, that could be referred
+ to by simple Python variables. One may write `quote = lisp.quote',
+ for example, and use `quote' afterwards to mean that Emacs Lisp symbol.
+ If a Python function received an Emacs Lisp symbol as an argument, it
+ can check with `==' if that argument is `lisp.never' or `lisp.ask', say.
+ A Python function may well choose to return `lisp.t'.
+
+ In Python, writing `lisp.SYMBOL = VALUE' or `lisp[STRING] = VALUE'
+ does assign VALUE to the corresponding symbol in Emacs Lisp space.
+ Beware that in such cases, the `lisp.' prefix may not be spared.
+ After `result = lisp.result', one cannot hope that a later `result = 3'
+ will have any effect in the Emacs Lisp space: this would merely change
+ the Python variable `result', which was a reference to a `Symbol'
+ instance, so it is now a reference to the number 3.
+
+ The `Symbol' class has `value()' and `copy()' methods. One can use
+ either `lisp.SYMBOL.value()' or `lisp.SYMBOL.copy()' to access the
+ Emacs Lisp value of a symbol, after conversion to some Python object,
+ of course. However, if `value()' would have given an Emacs Lisp handle,
+ `lisp.SYMBOL.copy()' has the effect of `lisp.SYMBOL.value().copy()',
+ that is, it returns the value of the symbol as opened as possible.
+
+ A symbol may also be used as if it was a Python function, in which case
+ it really names an Emacs Lisp function that should be applied over the
+ following function arguments. The result of the Emacs Lisp function
+ becomes the value of the call, with all due conversions of course.
+
+. : Dynamic bindings.
+
+ As Emacs Lisp uses dynamic bindings, it is common that Emacs Lisp
+ programs use `let' for temporarily setting new values for some Emacs
+ Lisp variables having global scope. These variables recover their
+ previous value automatically when the `let' gets completed, even if
+ an error occurs which interrupts the normal flow of execution.
+
+ Pymacs has a `Let' class to represent such temporary settings. Suppose
+ for example that you want to recover the value of `lisp.mark()' when the
+ transient mark mode is active on the Emacs Lisp side. One could surely
+ use `lisp.mark(lisp.t)' to "force" reading the mark in such cases,
+ but for the sake of illustration, let's ignore that, and temporarily
+ deactivate transient mark mode instead. This could be done this way:
+
+ try:
+ let = Let()
+ let.push(transient_mark_mode=None)
+ ... USER CODE ...
+ finally:
+ let.pop()
+
+ `let.push()' accepts any number of keywords arguments. Each keyword
+ name is interpreted as an Emacs Lisp symbol written the Pymacs way,
+ with underlines. The value of that Emacs Lisp symbol is saved on the
+ Python side, and the value of the keyword becomes the new temporary value
+ for this Emacs Lisp symbol. A later `let.pop()' restores the previous
+ value for all symbols which were saved together at the time of the
+ corresponding `let.push()'. There may be more than one `let.push()'
+ call for a single `Let' instance, they stack within that instance.
+ Each `let.pop()' will undo one and only one `let.push()' from the stack,
+ in the reverse order or the pushes.
+
+ When the `Let' instance disappears, either because the programmer does
+ `del let' or `let = None', or just because the Python `let' variable
+ goes out of scope, all remaining `let.pop()' get automatically executed,
+ so the `try'/`finally' statement may be omitted in practice. For this
+ omission to work flawlessly, the programmer should be careful at not
+ keeping extra references to the `Let' instance.
+
+ The constructor call `let = Let()' also has an implied initial `.push()'
+ over all given arguments, so the explicit `let.push()' may be omitted
+ as well. In practice, this sums up and the above code could be reduced
+ to a mere:
+
+ let = Let(transient_mark_mode=None)
+ ... USER CODE ...
+
+ Be careful at assigning the result of the constructor to some Python
+ variable. Otherwise, the instance would disappear immediately after
+ having been created, restoring the Emacs Lisp variable much too soon.
+
+ Any variable to be bound with `Let' should have been bound in advance
+ on the Emacs Lisp side. This restriction usually does no kind of harm.
+ Yet, it will likely be lifted in some later version of Pymacs.
+
+ The `Let' class has other methods meant for some macros which are
+ common in Emacs Lisp programming, in the spirit of `let' bindings.
+ These method names look like `push_*' or `pop_*', where Emacs
+ Lisp macros are `save-*'. One has to use the matching `pop_*' for
+ undoing the effect of a given `push_*' rather than a mere `.pop()':
+ the Python code is clearer, this also ensures that things are undone
+ in the proper order. The same `Let' instance may use many `push_*'
+ methods, their effects nest.
+
+ `push_excursion()' and `pop_excursion()' save and restore the current
+ buffer, point and mark. `push_match_data()' and `pop_match_data()'
+ save and restore the state of the last regular expression match.
+ `push_restriction()' and `pop_restriction()' save and restore
+ the current narrowing limits. `push_selected_window()' and
+ `pop_selected_window()' save and restore the fact that a window holds
+ the cursor. `push_window_excursion()' and `pop_window_excursion()'
+ save and restore the current window configuration in the Emacs display.
+
+ As a convenience, `let.push()' and all other `push_*' methods return
+ the `Let' instance. This helps chaining various `push_*' right after
+ the instance generation. For example, one may write:
+
+ let = Let().push_excursion()
+ if True:
+ ... USER CODE ...
+ del let
+
+ The `if True:' (use `if 1:' with older Python releases, some people
+ might prefer writing `if let:' anyway), has the only goal of indenting
+ USER CODE, so the scope of the `let' variable is made very explicit.
+ This is purely stylistic, and not at all necessary. The last `del let'
+ might be omitted in a few circumstances, for example if the excursion
+ lasts until the end of the Python function.
+
+. : Raw Emacs Lisp expressions.
+
+ Pymacs offers a device for evaluating a raw Emacs Lisp expression,
+ or a sequence of such, expressed as a string. One merely uses `lisp'
+ as a function, like this:
+
+ lisp("""
+ ...
+ POSSIBLY-LONG-SEQUENCE-OF-LISP-EXPRESSIONS
+ ...
+ """)
+
+ The Emacs Lisp value of the last or only expression in the sequence
+ becomes the value of the `lisp' call, after conversion back to Python.
+
+. : User interaction.
+
+ Emacs functions have the concept of user interaction for completing
+ the specification of their arguments while being called. This happens
+ only when a function is interactively called by the user, it does
+ not happen when a function is programmatically called by another.
+ As Python does not have a corresponding facility, a bit of trickery
+ was needed to retrofit that facility on the Python side.
+
+ After loading a Python module but prior to creating an Emacs view
+ for this module, Pymacs decides whether loaded functions will be
+ interactively callable from Emacs, or not. Whenever a function has an
+ `interaction' attribute, this attribute holds the Emacs interaction
+ specification for this function. The specification is either another
+ Python function or a string. In the former case, that other function
+ is called without arguments and should, maybe after having consulted
+ the user, return a list of the actual arguments to be used for the
+ original function. In the latter case, the specification string is
+ used verbatim as the argument to the `(interactive ...)' function
+ on the Emacs side. To get a short reminder about how this string is
+ interpreted on the Emacs side, try `C-h f interactive' within Emacs.
+ Here is an example where an empty string is used to specify that an
+ interactive has no arguments:
+
+ from Pymacs import lisp
+
+ def hello_world():
+ "`Hello world' from Python."
+ lisp.insert("Hello from Python!")
+ hello_world.interaction = ''
+
+ Versions of Python released before the integration of PEP 232 do not
+ allow users to add attributes to functions, so there is a fallback
+ mechanism. Let's presume that a given function does not have an
+ `interaction' attribute as explained above. If the Python module
+ contains an `interactions' global variable which is a dictionary,
+ if that dictionary has an entry for the given function with a value
+ other than `None', that function is going to be interactive on the
+ Emacs side. Here is how the preceeding example should be written
+ for an older version of Python, or when portability is at premium:
+
+ from Pymacs import lisp
+ interactions = {}
+
+ def hello_world():
+ "`Hello world' from Python."
+ lisp.insert("Hello from Python!")
+ interactions[hello_world] = ''
+
+ One might wonder why we do not merely use `lisp.interactive(...)' from
+ within Python. There is some magic in the Emacs Lisp interpreter
+ itself, looking for that call _before_ the function is actually
+ entered, this explains why "(interactive ...)" has to appear first in
+ an Emacs Lisp `defun'. Pymacs could try to scan the already compiled
+ form of the Python code, seeking for `lisp.interactive', but as the
+ evaluation of `lisp.interactive' arguments could get arbitrarily
+ complex, it would a real challenge un-compiling that evaluation into
+ Emacs Lisp.
+
+. : Keybindings.
+
+ An interactive function may be bound to a key sequence.
+
+ To translate bindings like `C-x w', say, one might have to know a bit
+ more how Emacs Lisp processes string escapes like `\C-x' or `\M-\C-x' in
+ Emacs Lisp, and emulate it within Python strings, since Python does not
+ have such escapes. `\C-L', where L is an upper case letter, produces a
+ character which ordinal is the result of subtracting 0x40 from ordinal
+ of `L'. `\M-' has the ordinal one gets by adding 0x80 to the ordinal
+ of following described character. So people can use self-inserting
+ non-ASCII characters, `\M-' is given another representation, which is
+ to replace the addition of 0x80 by prefixing with `ESC', that is 0x1b.
+
+ So "\C-x" in Emacs is '\x18' in Python. This is easily found, using an
+ interactive Python session, by givin it: chr(ord('X') - ord('A') + 1).
+ An easier way would be using the `kbd' function on the Emacs Lisp side,
+ like with lisp.kbd('C-x w') or lisp.kbd('M-<f2>').
+
+ To bind the F1 key to the `helper' function in some `module':
+
+ lisp.global_set_key((lisp.f1,), lisp.module_helper)
+
+ (item,) is a Python tuple yielding an Emacs Lisp vector. `lisp.f1'
+ translates to the Emacs Lisp symbol `f1'. So, Python `(lisp.f1,)' is
+ Emacs Lisp `[f1]'. Keys like `[M-f2]' might require some more ingenuity,
+ one may write either (lisp['M-f2'],) or (lisp.M_f2,) on the Python side.
+
+.. Debugging.
+
+. : The `*Pymacs*' buffer.
+
+ Emacs and Python are two separate processes (well, each may use more
+ than one process). Pymacs implements a simple communication protocol
+ between both, and does whatever needed so the programmers do not have
+ to worry about details. The main debugging tool is the communication
+ buffer between Emacs and Python, which is named `*Pymacs*'. To make
+ good use of it, first set `pymacs-trace-transit' to `t', so all
+ exchanges are accumulated in that buffer. As it is sometimes helpful
+ to understand the communication protocol, it is briefly explained here,
+ using an artificially complex example to do so. Consider:
+
+ ---------------------------------------------------------------------->
+ (pymacs-eval "lisp('(pymacs-eval \"`2L**111`\")')")
+ "2596148429267413814265248164610048L"
+ ----------------------------------------------------------------------<
+
+ Here, Emacs asks Python to ask Emacs to ask Python for a simple bignum
+ computation. Note that Emacs does not natively know how to handle big
+ integers, nor has an internal representation for them. This is why I
+ use backticks, so Python returns a string representation of the result,
+ instead of the result itself. Here is a trace for this example. The `<'
+ character flags a message going from Python to Emacs and is followed
+ by an expression written in Emacs Lisp. The '>' character flags a
+ message going from Emacs to Python and is followed by a expression
+ written in Python. The number gives the length of the message.
+
+ ---------------------------------------------------------------------->
+ <22 (pymacs-version "0.3")
+ >49 eval("lisp('(pymacs-eval \"`2L**111`\")')")
+ <25 (pymacs-eval "`2L**111`")
+ >18 eval("`2L**111`")
+ <47 (pymacs-reply "2596148429267413814265248164610048L")
+ >45 reply("2596148429267413814265248164610048L")
+ <47 (pymacs-reply "2596148429267413814265248164610048L")
+ ----------------------------------------------------------------------<
+
+ Python evaluation is done in the context of the `Pymacs.pymacs' module,
+ so for example a mere `reply' really means `Pymacs.pymacs.reply'. On the
+ Emacs Lisp side, there is no concept of module namespaces, so we use
+ the `pymacs-' prefix as an attempt to stay clean. Users should ideally
+ refrain from naming their Emacs Lisp objects with a `pymacs-' prefix.
+
+ `reply' and `pymacs-reply' are special functions meant to indicate that
+ an expected result is finally transmitted. `error' and `pymacs-error'
+ are special functions that introduce a string which explains an exception
+ which recently occurred. `pymacs-expand' is a special function
+ implementing the `copy()' methods of Emacs Lisp handles or symbols.
+ In all other cases, the expression is a request for the other side,
+ that request stacks until a corresponding reply is received.
+
+ Part of the protocol manages memory, and this management generates
+ some extra-noise in the `*Pymacs*' buffer. Whenever Emacs passes a
+ structure to Python, an extra pointer is generated on the Emacs side to
+ inhibit garbage collection by Emacs. Python garbage collector detects
+ when the received structure is no longer needed on the Python side,
+ at which time the next communication will tell Emacs to remove the
+ extra pointer. It works symmetrically as well, that is, whenever Python
+ passes a structure to Emacs, an extra Python reference is generated to
+ inhibit garbage collection on the Python side. Emacs garbage collector
+ detects when the received structure is no longer needed on the Emacs
+ side, after which Python will be told to remove the extra reference.
+ For efficiency, those allocation-related messages are delayed, merged and
+ batched together within the next communication having another purpose.
+
+. : Emacs usual debugging.
+
+ If cross-calls between Emacs Lisp and Python nest deeply, an error will
+ raise successive exceptions alternatively on both sides as requests
+ unstack, and the diagnostic gets transmitted back and forth, slightly
+ growing as we go. So, errors will eventually be reported by Emacs.
+ I made no kind of effort to transmit the Emacs Lisp backtrace on the
+ Python side, as I do not see a purpose for it: all debugging is done
+ within Emacs windows anyway.
+
+ On recent Emacses, the Python backtrace gets displayed in the
+ mini-buffer, and the Emacs Lisp backtrace is simultaneously shown in the
+ `*Backtrace*' window. One useful thing is to allow to mini-buffer to
+ grow big, so it has more chance to fully contain the Python backtrace,
+ the last lines of which are often especially useful. Here, I use:
+
+ (setq resize-mini-windows t
+ max-mini-window-height .85)
+
+ in my `.emacs' file, so the mini-buffer may use 85% of the screen,
+ and quickly shrinks when fewer lines are needed. The mini-buffer
+ contents disappear at the next keystroke, but you can recover the
+ Python backtrace by looking at the end of the `*Messages*' buffer.
+ In which case the `ffap' package in Emacs may be yet another friend!
+ From the `*Messages*' buffer, once `ffap' activated, merely put the
+ cursor on the file name of a Python module from the backtrace, and
+ `C-x C-f RET' will quickly open that source for you.
+
+. : Auto-reloading on save.
+
+ I found useful to automatically `pymacs-load' some Python files whenever
+ they get saved from Emacs. Here is how I do it. The code below assumes
+ that Python files meant for Pymacs are kept in `~/share/emacs/python'.
+
+ (defun fp-maybe-pymacs-reload ()
+ (let ((pymacsdir (expand-file-name "~/share/emacs/python/")))
+ (when (and (string-equal (file-name-directory buffer-file-name)
+ pymacsdir)
+ (string-match "\\.py\\'" buffer-file-name))
+ (pymacs-load (substring buffer-file-name 0 -3)))))
+ (add-hook 'after-save-hook 'fp-maybe-pymacs-reload)
+
+.. Exemples.
+
+. : Paul Winkler's.
+
+. , The problem.
+
+ Let's say I have a a module, call it `manglers.py', containing this
+ simple python function:
+
+ def break_on_whitespace(some_string):
+ words = some_string.split()
+ return '\n'.join(words)
+
+ The goal is telling Emacs about this function so that I can call it on
+ a region of text and replace the region with the result of the call.
+ And bind this action to a key, of course, let's say `[f7]'.
+
+ The Emacs buffer ought to be handled in some way. If this is not on the
+ Emacs Lisp side, it has to be on the Python side, but we cannot escape
+ handling the buffer. So, there is an equilibrium in the work to do for
+ the user, that could be displaced towards Emacs Lisp or towards Python.
+
+. , Python side.
+
+ Here is a first draft for the Python side of the problem:
+
+ from Pymacs import lisp
+
+ def break_on_whitespace():
+ start = lisp.point()
+ end = lisp.mark(lisp.t)
+ if start > end:
+ start, end = end, start
+ text = lisp.buffer_substring(start, end)
+ words = text.split()
+ replacement = '\n'.join(words)
+ lisp.delete_region(start, end)
+ lisp.insert(replacement)
+
+ interactions = {break_on_whitespace: ''}
+
+ For various stylistic reasons, this could be rewritten into:
+
+ from Pymacs import lisp
+ interactions = {}
+
+ def break_on_whitespace():
+ start, end = lisp.point(), lisp.mark(lisp.t)
+ words = lisp.buffer_substring(start, end).split()
+ lisp.delete_region(start, end)
+ lisp.insert('\n'.join(words))
+
+ interactions[break_on_whitespace] = ''
+
+ The above relies, in particular, on the fact that for those Emacs
+ Lisp functions used here, `start' and `end' may be given in any order.
+
+. , Emacs side.
+
+ On the Emacs side, one would do:
+
+ (pymacs-load "manglers")
+ (global-set-key [f7] 'manglers-break-on-whitespace)
+
+. : The `rebox' tool.
+
+. , The problem.
+
+ For comments held within boxes, it is painful to fill paragraphs, while
+ stretching or shrinking the surrounding box "by hand", as needed.
+ This piece of Python code eases my life on this. It may be used
+ interactively from within Emacs through the Pymacs interface, or in
+ batch as a script which filters a single region to be reformatted.
+
+ In batch, the reboxing is driven by command options and arguments
+ and expects a complete, self-contained boxed comment from a file.
+ Emacs function `rebox-region' also presumes that the region encloses
+ a single boxed comment. Emacs `rebox-comment' is different, as it
+ has to chase itself the extent of the surrounding boxed comment.
+
+. , Python side.
+
+ The Python code is too big to be inserted in this documentation: see
+ file `Pymacs/rebox.py' in the Pymacs distribution. You will observe
+ in the code that Pymacs specific features are used exclusively from
+ within the `pymacs_load_hook' function and the `Emacs_Rebox' class.
+ In batch mode, `Pymacs' is not even imported. Here, we mean to
+ discuss some of the design choices in the context of Pymacs.
+
+ In batch mode, as well as with `rebox-region', the text to handle is
+ turned over to Python, and fully processed in Python, with practically
+ no Pymacs interaction while the work gets done. On the other hand,
+ `rebox-comment' is rather Pymacs intensive: the comment boundaries
+ are chased right from the Emacs buffer, as directed by the function
+ `Emacs_Rebox.find_comment'. Once the boundaries are found, the
+ remainder of the work is essentially done on the Python side.
+
+ Once the boxed comment has been reformatted in Python, the old comment
+ is removed in a single delete operation, the new comment is inserted in
+ a second operation, this occurs in `Emacs_Rebox.process_emacs_region'.
+ But by doing so, if point was within the boxed comment before the
+ reformatting, its precise position is lost. To well preserve point,
+ Python might have driven all reformatting details directly in the
+ Emacs buffer. We really preferred doing it all on the Python side:
+ as we gain legibility by expressing the algorithms in pure Python,
+ the same Python code may be used in batch or interactively, and we
+ avoid the slowdown that would result from heavy use of Emacs services.
+
+ To avoid completely loosing point, I kludged a `Marker' class,
+ which goal is to estimate the new value of point from the old.
+ Reformatting may change the amount of white space, and either delete
+ or insert an arbitrary number characters meant to draw the box.
+ The idea is to initially count the number of characters between the
+ beginning of the region and point, while ignoring any problematic
+ character. Once the comment has been reboxed, point is advanced from
+ the beginning of the region until we get the same count of characters,
+ skipping all problematic characters. This `Marker' class works fully
+ on the Python side, it does not involve Pymacs at all, but it does
+ solve a problem that resulted from my choice of keeping the data on
+ the Python side instead of handling it directly in the Emacs buffer.
+
+ We want a comment reformatting to appear as a single operation, in
+ the context of Emacs Undo. The method `Emacs_Rebox.clean_undo_after'
+ handles the general case for this. Not that we do so much in practice:
+ a reformatting implies one `delete-region' and one `insert', and maybe
+ some other little adjustements at `Emacs_Rebox.find_comment' time.
+ Even if this method scans and mofifies an Emacs Lisp list directly in
+ the Emacs memory, the code doing this stays neat and legible. However,
+ I found out that the undo list may grow quickly when the Emacs buffer
+ use markers, with the consequence of making this routine so Pymacs
+ intensive that most of the CPU is spent there. I rewrote that
+ routine in Emacs Lisp so it executes in a single Pymacs interaction.
+
+ Function `Emacs_Rebox.remainder_of_line' could have been written in
+ Python, but it was probably not worth going away from this one-liner in
+ Emacs Lisp. Also, given this routine is often called by `find_comment',
+ a few Pymacs protocol interactions are spared this way. This function
+ is useful when there is a need to apply a regexp already compiled on
+ the Python side, it is probably better fetching the line from Emacs
+ and do the pattern match on the Python side, than transmitting the
+ source of the regexp to Emacs for it to compile and apply it.
+
+ For refilling, I could have either used the refill algorithm built
+ within in Emacs, programmed a new one in Python, or relied on Ross
+ Paterson's `fmt', distributed by GNU and available on most Linuxes.
+ In fact, `refill_lines' prefers the latter. My own Emacs setup is
+ such that the built-in refill algorithm is _already_ overridden by GNU
+ `fmt', and it really does a much better job. Experience taught me
+ that calling an external program is fast enough to be very bearable,
+ even interactively. If Python called Emacs to do the refilling, Emacs
+ would itself call GNU `fmt' in my case, I preferred that Python calls
+ GNU `fmt' directly. I could have reprogrammed GNU `fmt' in Python.
+ Despite interesting, this is an uneasy project: `fmt' implements
+ the Knuth refilling algorithm, which depends on dynamic programming
+ techniques; Ross fine tuned them, and took care of many details.
+ If GNU `fmt' fails, for not being available, say, `refill_lines'
+ falls back on a dumb refilling algorithm, which is better than none.
+
+. , Emacs side.
+
+ The Emacs recipe appears under the `Emacs usage' section, near the
+ beginning of `Pymacs/rebox.py', so I do not repeat it here.
+
+..
+ François Pinard,
+ pinard@iro.umontreal.ca
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/ide-integration/Pymacs-0.20/README.html Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,11 @@
+<!doctype HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<!--: Faire
+ from Webert import transform
+ title, text = transform.transform('README')
+:-->
+<html>
+ <head><title>%(title)s</title></head>
+ <body>
+%(text)s
+ </body>
+</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/ide-integration/Pymacs-0.20/THANKS Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,24 @@
+Pymacs has been written by François Pinard after an idea from Cedric Adjih,
+and much influenced by Brian McErlean. Here is the list of contributors.
+
+Brian McErlean b.mcerlean@kainos.com
+Carel Fellinger cfelling@iae.nl
+Carey Evans careye@spamcop.net
+Cedric Adjih adjih-pam@crepuscule.com
+ http://www.crepuscule.com/pyemacs/
+Christian Tanzer tanzer@swing.co.at
+Dave Sellars dsellars@windriver.com
+Dirk Vleugels dvl@2scale.net
+Eli Zaretskii eliz@is.elta.co.il
+François Pinard pinard@iro.umontreal.ca
+ http://www.iro.umontreal.ca/~pinard
+Gerd Möllmann gerd@gnu.org
+John Wiegley johnw@gnu.org
+Marcin Qrczak Kowalczyk qrczak@knm.org.pl
+Paul Foley mycroft@actrix.gen.nz
+ http://users.actrix.co.nz/mycroft/
+Paul Winkler slinkp23@yahoo.com
+Richard Stallman rms@gnu.org
+Stefan Reichör xsteve@riic.at
+Steffen Ries steffen.ries@sympatico.ca
+Syver Enstad syver.enstad@asker.online.no
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/ide-integration/Pymacs-0.20/THANKS-rebox Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,8 @@
+`rebox' has been written by François Pinard. Here is the list of
+contributors. Also see `ChangeLog-rebox' for a more details.
+
+Akim Demaille demaille@inf.enst.fr
+Marc Feeley feeley@iro.umontreal.ca
+Paul Eggert eggert@twinsun.com
+Paul Provost provost@virtualprototypes.ca
+Ulrich Drepper drepper@gnu.org
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/ide-integration/Pymacs-0.20/TODO Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,34 @@
+* TODO for `Pymacs' allout -*- outline -*-
+
+.. Internal cleanup.
+
+. : `(pymacs-eval "dir()")' shows too many things.
+
+.. Type conversions.
+
+. : Convert LISP hash tables into Python dicts and vice-versa.
+
+.. Iterator protocol.
+
+. : Allow iterating over vectors.
+
+.. Rebox.
+
+. : Debug the Undo list cleanup!
+
+. : Try the fall back refiller.
+
+. : Unicode boxes (suggested by Bruno).
+. , U+231C..U+231F
+. , U+25xx
+
+.. Debugging facilities.
+
+. : *Pymacs*
+. , Indent.
+. , Interpret numbers.
+. , Highlight.
+
+. : Test suite. (Brian has one.)
+
+. : Python shell link to helper.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/ide-integration/Pymacs-0.20/pymacs-services Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,27 @@
+#!/usr/bin/env python
+# Copyright İ 2001, 2002 Progiciels Bourbeau-Pinard inc.
+# François Pinard <pinard@iro.umontreal.ca>, 2001.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+"""\
+Interface between Emacs LISP and Python - Python process starter.
+
+This small bootstrap is so Pymacs modules can be kept in compiled form.
+"""
+
+import sys
+from Pymacs import pymacs
+apply(pymacs.main, sys.argv[1:])
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/ide-integration/Pymacs-0.20/pymacs-services.bat Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,1 @@
+@python "%~dpn0" %*
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/ide-integration/Pymacs-0.20/pymacs.el Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,631 @@
+;;; Interface between Emacs Lisp and Python - Lisp part.
+;;; Copyright İ 2001, 2002 Progiciels Bourbeau-Pinard inc.
+;;; François Pinard <pinard@iro.umontreal.ca>, 2001.
+
+;;; This program is free software; you can redistribute it and/or modify
+;;; it under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 2, or (at your option)
+;;; any later version.
+;;;
+;;; This program is distributed in the hope that it will be useful,
+;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with this program; if not, write to the Free Software Foundation,
+;;; Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+;;; See the Pymacs documentation (in `README') for more information.
+
+;;; Published functions.
+
+(defvar pymacs-load-path nil
+ "List of additional directories to search for Python modules.
+The directories listed will be searched first, in the order given.")
+
+(defvar pymacs-trace-transit nil
+ "Keep the communication buffer growing, for debugging.
+When this variable is nil, the `*Pymacs*' communication buffer gets erased
+before each communication round-trip. Setting it to `t' guarantees that
+the full communication is saved, which is useful for debugging.")
+
+(defvar pymacs-forget-mutability nil
+ "Transmit copies to Python instead of Lisp handles, as much as possible.
+When this variable is nil, most mutable objects are transmitted as handles.
+This variable is meant to be temporarily rebound to force copies.")
+
+(defvar pymacs-mutable-strings nil
+ "Prefer transmitting Lisp strings to Python as handles.
+When this variable is nil, strings are transmitted as copies, and the
+Python side thus has no way for modifying the original Lisp strings.
+This variable is ignored whenever `forget-mutability' is set.")
+
+(defvar pymacs-timeout-at-start 30
+ "Maximum reasonable time, in seconds, for starting `pymacs-services'.
+A machine should be pretty loaded before one needs to increment this.")
+
+(defvar pymacs-timeout-at-reply 5
+ "Expected maximum time, in seconds, to get the first line of a reply.
+The status of `pymacs-services' is checked at every such timeout.")
+
+(defvar pymacs-timeout-at-line 2
+ "Expected maximum time, in seconds, to get another line of a reply.
+The status of `pymacs-services' is checked at every such timeout.")
+
+(defun pymacs-load (module &optional prefix noerror)
+ "Import the Python module named MODULE into Emacs.
+Each function in the Python module is made available as an Emacs function.
+The Lisp name of each function is the concatenation of PREFIX with
+the Python name, in which underlines are replaced by dashes. If PREFIX is
+not given, it defaults to MODULE followed by a dash.
+If NOERROR is not nil, do not raise error when the module is not found."
+ (interactive
+ (let* ((module (read-string "Python module? "))
+ (default (concat (car (last (split-string module "\\."))) "-"))
+ (prefix (read-string (format "Prefix? [%s] " default)
+ nil nil default)))
+ (list module prefix)))
+ (message "Pymacs loading %s..." module)
+ (let ((lisp-code (pymacs-call "pymacs_load_helper" module prefix)))
+ (cond (lisp-code (let ((result (eval lisp-code)))
+ (message "Pymacs loading %s...done" module)
+ result))
+ (noerror (message "Pymacs loading %s...failed" module) nil)
+ (t (error "Pymacs loading %s...failed" module)))))
+
+(defun pymacs-eval (text)
+ "Compile TEXT as a Python expression, and return its value."
+ (interactive "sPython expression? ")
+ (let ((value (pymacs-call "eval" text)))
+ (when (interactive-p)
+ (message "%S" value))
+ value))
+
+(defun pymacs-exec (text)
+ "Compile and execute TEXT as a sequence of Python statements.
+This functionality is experimental, and does not appear to be useful."
+ (interactive "sPython statements? ")
+ (let ((value (pymacs-serve-until-reply
+ `(progn (princ "exec ") (prin1 ,text)))))
+ (when (interactive-p)
+ (message "%S" value))
+ value))
+
+(defun pymacs-call (function &rest arguments)
+ "Return the result of calling a Python function FUNCTION over ARGUMENTS.
+FUNCTION is a string denoting the Python function, ARGUMENTS are separate
+Lisp expressions, one per argument. Immutable Lisp constants are converted
+to Python equivalents, other structures are converted into Lisp handles."
+ (pymacs-apply function arguments))
+
+(defun pymacs-apply (function arguments)
+ "Return the result of calling a Python function FUNCTION over ARGUMENTS.
+FUNCTION is a string denoting the Python function, ARGUMENTS is a list of
+Lisp expressions. Immutable Lisp constants are converted to Python
+equivalents, other structures are converted into Lisp handles."
+ (pymacs-serve-until-reply `(pymacs-print-for-apply ',function ',arguments)))
+
+;;; Integration details.
+
+;; Python functions and modules should ideally look like Lisp functions and
+;; modules. This page tries to increase the integration seamlessness.
+
+(defadvice documentation (around pymacs-ad-documentation activate)
+ ;; Integration of doc-strings.
+ (let* ((reference (pymacs-python-reference function))
+ (python-doc (when reference
+ (pymacs-eval (format "doc_string(%s)" reference)))))
+ (if (or reference python-doc)
+ (setq ad-return-value
+ (concat
+ "It interfaces to a Python function.\n\n"
+ (when python-doc
+ (if raw python-doc (substitute-command-keys python-doc)))))
+ ad-do-it)))
+
+(defun pymacs-python-reference (object)
+ ;; Return the text reference of a Python object if possible, else nil.
+ (when (functionp object)
+ (let* ((definition (indirect-function object))
+ (body (and (pymacs-proper-list-p definition)
+ (> (length definition) 2)
+ (eq (car definition) 'lambda)
+ (cddr definition))))
+ (when (and body (listp (car body)) (eq (caar body) 'interactive))
+ ;; Skip the interactive specification of a function.
+ (setq body (cdr body)))
+ (when (and body
+ ;; Advised functions start with a string.
+ (not (stringp (car body)))
+ ;; Python trampolines hold exactly one expression.
+ (= (length body) 1))
+ (let ((expression (car body)))
+ ;; EXPRESSION might now hold something like:
+ ;; (pymacs-apply (quote (pymacs-python . N)) ARGUMENT-LIST)
+ (when (and (pymacs-proper-list-p expression)
+ (= (length expression) 3)
+ (eq (car expression) 'pymacs-apply)
+ (eq (car (cadr expression)) 'quote))
+ (setq object (cadr (cadr expression))))))))
+ (when (eq (car-safe object) 'pymacs-python)
+ (format "python[%d]" (cdr object))))
+
+;; The following functions are experimental -- they are not satisfactory yet.
+
+(defun pymacs-file-handler (operation &rest arguments)
+ ;; Integration of load-file, autoload, etc.
+ ;; Emacs might want the contents of some `MODULE.el' which does not exist,
+ ;; while there is a `MODULE.py' or `MODULE.pyc' file in the same directory.
+ ;; The goal is to generate a virtual contents for this `MODULE.el' file, as
+ ;; a set of Lisp trampoline functions to the Python module functions.
+ ;; Python modules can then be loaded or autoloaded as if they were Lisp.
+ ;(message "** %S %S" operation arguments)
+ (cond ((and (eq operation 'file-readable-p)
+ (let ((module (substring (car arguments) 0 -3)))
+ (or (pymacs-file-force operation arguments)
+ (file-readable-p (concat module ".py"))
+ (file-readable-p (concat module ".pyc"))))))
+ ((and (eq operation 'load)
+ (not (pymacs-file-force
+ 'file-readable-p (list (car arguments))))
+ (file-readable-p (car arguments)))
+ (let ((lisp-code (pymacs-call "pymacs_load_helper"
+ (substring (car arguments) 0 -3)
+ nil)))
+ (unless lisp-code
+ (error "Python import error"))
+ (eval lisp-code)))
+ ((and (eq operation 'insert-file-contents)
+ (not (pymacs-file-force
+ 'file-readable-p (list (car arguments))))
+ (file-readable-p (car arguments)))
+ (let ((lisp-code (pymacs-call "pymacs_load_helper"
+ (substring (car arguments) 0 -3)
+ nil)))
+ (unless lisp-code
+ (error "Python import error"))
+ (insert (prin1-to-string lisp-code))))
+ (t (pymacs-file-force operation arguments))))
+
+(defun pymacs-file-force (operation arguments)
+ ;; Bypass the file handler.
+ (let ((inhibit-file-name-handlers
+ (cons 'pymacs-file-handler
+ (and (eq inhibit-file-name-operation operation)
+ inhibit-file-name-handlers)))
+ (inhibit-file-name-operation operation))
+ (apply operation arguments)))
+
+;(add-to-list 'file-name-handler-alist '("\\.el\\'" . pymacs-file-handler))
+
+;;; Gargabe collection of Python IDs.
+
+;; Python objects which have no Lisp representation are allocated on the
+;; Python side as `python[INDEX]', and INDEX is transmitted to Emacs, with
+;; the value to use on the Lisp side for it. Whenever Lisp does not need a
+;; Python object anymore, it should be freed on the Python side. The
+;; following variables and functions are meant to fill this duty.
+
+(defvar pymacs-use-hash-tables nil
+ "Automatically set to t if hash tables are available.")
+
+(defvar pymacs-used-ids nil
+ "List of received IDs, currently allocated on the Python side.")
+
+(defvar pymacs-weak-hash nil
+ "Weak hash table, meant to find out which IDs are still needed.")
+
+(defvar pymacs-gc-wanted nil
+ "Flag if it is time to clean up unused IDs on the Python side.")
+
+(defvar pymacs-gc-running nil
+ "Flag telling that a Pymacs garbage collection is in progress.")
+
+(defvar pymacs-gc-timer nil
+ "Timer to trigger Pymacs garbage collection at regular time intervals.
+The timer is used only if `post-gc-hook' is not available.")
+
+(defun pymacs-schedule-gc ()
+ (unless pymacs-gc-running
+ (setq pymacs-gc-wanted t)))
+
+(defun pymacs-garbage-collect ()
+ ;; Clean up unused IDs on the Python side.
+ (when pymacs-use-hash-tables
+ (let ((pymacs-gc-running t)
+ (pymacs-forget-mutability t)
+ (ids pymacs-used-ids)
+ used-ids unused-ids)
+ (while ids
+ (let ((id (car ids)))
+ (setq ids (cdr ids))
+ (if (gethash id pymacs-weak-hash)
+ (setq used-ids (cons id used-ids))
+ (setq unused-ids (cons id unused-ids)))))
+ ;;(message "** pymacs-garbage-collect %d %d"
+ ;; (length used-ids) (length unused-ids))
+ (setq pymacs-used-ids used-ids
+ pymacs-gc-wanted nil)
+ (when unused-ids
+ (pymacs-apply "free_python" unused-ids)))))
+
+(defun pymacs-defuns (arguments)
+ ;; Take one argument, a list holding a number of items divisible by 3. The
+ ;; first argument is an INDEX, the second is a NAME, the third is the
+ ;; INTERACTION specification, and so forth. Register Python INDEX with a
+ ;; function with that NAME and INTERACTION on the Lisp side. The strange
+ ;; calling convention is to minimise quoting at call time.
+ (while (>= (length arguments) 3)
+ (let ((index (nth 0 arguments))
+ (name (nth 1 arguments))
+ (interaction (nth 2 arguments)))
+ (fset name (pymacs-defun index interaction))
+ (setq arguments (nthcdr 3 arguments)))))
+
+(defun pymacs-defun (index interaction)
+ ;; Register INDEX on the Lisp side with a Python object that is a function,
+ ;; and return a lambda form calling that function. If the INTERACTION
+ ;; specification is nil, the function is not interactive. Otherwise, the
+ ;; function is interactive, INTERACTION is then either a string, or the
+ ;; index of an argument-less Python function returning the argument list.
+ (let ((object (pymacs-python index)))
+ (cond ((null interaction)
+ `(lambda (&rest arguments)
+ (pymacs-apply ',object arguments)))
+ ((stringp interaction)
+ `(lambda (&rest arguments)
+ (interactive ,interaction)
+ (pymacs-apply ',object arguments)))
+ (t `(lambda (&rest arguments)
+ (interactive (pymacs-call ',(pymacs-python interaction)))
+ (pymacs-apply ',object arguments))))))
+
+(defun pymacs-python (index)
+ ;; Register on the Lisp side a Python object having INDEX, and return it.
+ ;; The result is meant to be recognised specially by `print-for-eval', and
+ ;; in the function position by `print-for-apply'.
+ (let ((object (cons 'pymacs-python index)))
+ (when pymacs-use-hash-tables
+ (puthash index object pymacs-weak-hash)
+ (setq pymacs-used-ids (cons index pymacs-used-ids)))
+ object))
+
+;;; Generating Python code.
+
+;; Many Lisp expressions cannot fully be represented in Python, at least
+;; because the object is mutable on the Lisp side. Such objects are allocated
+;; somewhere into a vector of handles, and the handle index is used for
+;; communication instead of the expression itself.
+
+(defvar pymacs-lisp nil
+ "Vector of handles to hold transmitted expressions.")
+
+(defvar pymacs-freed-list nil
+ "List of unallocated indices in Lisp.")
+
+;; When the Python CG is done with a Lisp object, a communication occurs so to
+;; free the object on the Lisp side as well.
+
+(defun pymacs-allocate-lisp (expression)
+ ;; This function allocates some handle for an EXPRESSION, and return its
+ ;; index.
+ (unless pymacs-freed-list
+ (let* ((previous pymacs-lisp)
+ (old-size (length previous))
+ (new-size (if (zerop old-size) 100 (+ old-size (/ old-size 2))))
+ (counter new-size))
+ (setq pymacs-lisp (make-vector new-size nil))
+ (while (> counter 0)
+ (setq counter (1- counter))
+ (if (< counter old-size)
+ (aset pymacs-lisp counter (aref previous counter))
+ (setq pymacs-freed-list (cons counter pymacs-freed-list))))))
+ (let ((index (car pymacs-freed-list)))
+ (setq pymacs-freed-list (cdr pymacs-freed-list))
+ (aset pymacs-lisp index expression)
+ index))
+
+(defun pymacs-free-lisp (&rest indices)
+ ;; This function is triggered from Python side for Lisp handles which lost
+ ;; their last reference. These references should be cut on the Lisp side as
+ ;; well, or else, the objects will never be garbage-collected.
+ (while indices
+ (let ((index (car indices)))
+ (aset pymacs-lisp index nil)
+ (setq pymacs-freed-list (cons index pymacs-freed-list)
+ indices (cdr indices)))))
+
+(defun pymacs-print-for-apply-expanded (function arguments)
+ ;; This function acts like `print-for-apply', but produce arguments which
+ ;; are expanded copies whenever possible, instead of handles. Proper lists
+ ;; are turned into Python lists, vectors are turned into Python tuples.
+ (let ((pymacs-forget-mutability t))
+ (pymacs-print-for-apply function arguments)))
+
+(defun pymacs-print-for-apply (function arguments)
+ ;; This function prints a Python expression calling FUNCTION, which is a
+ ;; string naming a Python function, or a Python reference, over all its
+ ;; ARGUMENTS, which are Lisp expressions.
+ (let ((separator "")
+ argument)
+ (if (eq (car-safe function) 'pymacs-python)
+ (princ (format "python[%d]" (cdr function)))
+ (princ function))
+ (princ "(")
+ (while arguments
+ (setq argument (car arguments)
+ arguments (cdr arguments))
+ (princ separator)
+ (setq separator ", ")
+ (pymacs-print-for-eval argument))
+ (princ ")")))
+
+(defun pymacs-print-for-eval (expression)
+ ;; This function prints a Python expression out of a Lisp EXPRESSION.
+ (let (done)
+ (cond ((not expression)
+ (princ "None")
+ (setq done t))
+ ((numberp expression)
+ (princ expression)
+ (setq done t))
+ ((stringp expression)
+ (when (or pymacs-forget-mutability
+ (not pymacs-mutable-strings))
+ (let ((text (copy-sequence expression)))
+ (set-text-properties 0 (length text) nil text)
+ (princ (mapconcat 'identity
+ (split-string (prin1-to-string text) "\n")
+ "\\n")))
+ (setq done t)))
+ ((symbolp expression)
+ (let ((name (symbol-name expression)))
+ ;; The symbol can only be transmitted when in the main oblist.
+ (when (eq expression (intern-soft name))
+ (cond
+ ((save-match-data
+ (string-match "^[A-Za-z][-A-Za-z0-9]*$" name))
+ (princ "lisp.")
+ (princ (mapconcat 'identity (split-string name "-") "_")))
+ (t (princ "lisp[")
+ (prin1 name)
+ (princ "]")))
+ (setq done t))))
+ ((vectorp expression)
+ (when pymacs-forget-mutability
+ (let ((limit (length expression))
+ (counter 0))
+ (princ "(")
+ (while (< counter limit)
+ (unless (zerop counter)
+ (princ ", "))
+ (pymacs-print-for-eval (aref expression counter)))
+ (when (= limit 1)
+ (princ ","))
+ (princ ")")
+ (setq done t))))
+ ((eq (car-safe expression) 'pymacs-python)
+ (princ "python[")
+ (princ (cdr expression))
+ (princ "]"))
+ ((pymacs-proper-list-p expression)
+ (when pymacs-forget-mutability
+ (princ "[")
+ (pymacs-print-for-eval (car expression))
+ (while (setq expression (cdr expression))
+ (princ ", ")
+ (pymacs-print-for-eval (car expression)))
+ (princ "]")
+ (setq done t))))
+ (unless done
+ (let ((class (cond ((vectorp expression) "Vector")
+ ((and pymacs-use-hash-tables
+ (hash-table-p expression))
+ "Table")
+ ((bufferp expression) "Buffer")
+ ((pymacs-proper-list-p expression) "List")
+ (t "Lisp"))))
+ (princ class)
+ (princ "(")
+ (princ (pymacs-allocate-lisp expression))
+ (princ ")")))))
+
+;;; Communication protocol.
+
+(defvar pymacs-transit-buffer nil
+ "Communication buffer between Emacs and Python.")
+
+;; The principle behind the communication protocol is that it is easier to
+;; generate than parse, and that each language already has its own parser.
+;; So, the Emacs side generates Python text for the Python side to interpret,
+;; while the Python side generates Lisp text for the Lisp side to interpret.
+;; About nothing but expressions are transmitted, which are evaluated on
+;; arrival. The pseudo `reply' function is meant to signal the final result
+;; of a series of exchanges following a request, while the pseudo `error'
+;; function is meant to explain why an exchange could not have been completed.
+
+;; The protocol itself is rather simple, and contains human readable text
+;; only. A message starts at the beginning of a line in the communication
+;; buffer, either with `>' for the Lisp to Python direction, or `<' for the
+;; Python to Lisp direction. This is followed by a decimal number giving the
+;; length of the message text, a TAB character, and the message text itself.
+;; Message direction alternates systematically between messages, it never
+;; occurs that two successive messages are sent in the same direction. The
+;; first message is received from the Python side, it is `(version VERSION)'.
+
+(defun pymacs-start-services ()
+ ;; This function gets called automatically, as needed.
+ (let ((buffer (get-buffer-create "*Pymacs*")))
+ (with-current-buffer buffer
+ (buffer-disable-undo)
+ (save-match-data
+ ;; Launch the Python helper.
+ (let ((process (apply 'start-process "pymacs" buffer "pymacs-services"
+ (mapcar 'expand-file-name pymacs-load-path))))
+ (process-kill-without-query process)
+ ;; Receive the synchronising reply.
+ (while (progn
+ (goto-char (point-min))
+ (not (re-search-forward "<\\([0-9]+\\)\t" nil t)))
+ (unless (accept-process-output process pymacs-timeout-at-start)
+ (error "Pymacs helper did not start within %d seconds."
+ pymacs-timeout-at-start)))
+ (let ((marker (process-mark process))
+ (limit-position (+ (match-end 0)
+ (string-to-number (match-string 1)))))
+ (while (< (marker-position marker) limit-position)
+ (unless (accept-process-output process pymacs-timeout-at-start)
+ (error "Pymacs helper probably was interrupted at start.")))))
+ ;; Check that synchronisation occurred.
+ (goto-char (match-end 0))
+ (let ((reply (read (current-buffer))))
+ (if (and (pymacs-proper-list-p reply)
+ (= (length reply) 2)
+ (eq (car reply) 'pymacs-version))
+ (unless (string-equal (cadr reply) "@VERSION@")
+ (error "Pymacs Lisp version is @VERSION@, Python is %s."
+ (cadr reply)))
+ (error "Pymacs got an invalid initial reply.")))))
+ (setq pymacs-use-hash-tables (and (fboundp 'make-hash-table)
+ (fboundp 'gethash)
+ (fboundp 'puthash)))
+ (when pymacs-use-hash-tables
+ (if pymacs-weak-hash
+ ;; A previous Pymacs session occurred in *this* Emacs session. Some
+ ;; IDs may hang around, which do not correspond to anything on the
+ ;; Python side. Python should not recycle such IDs for new objects.
+ (when pymacs-used-ids
+ (let ((pymacs-transit-buffer buffer)
+ (pymacs-forget-mutability t))
+ (pymacs-apply "zombie_python" pymacs-used-ids)))
+ (setq pymacs-weak-hash (make-hash-table :weakness 'value)))
+ (if (boundp 'post-gc-hook)
+ (add-hook 'post-gc-hook 'pymacs-schedule-gc)
+ (setq pymacs-gc-timer (run-at-time 20 20 'pymacs-schedule-gc))))
+ ;; If nothing failed, only then declare the Pymacs has started!
+ (setq pymacs-transit-buffer buffer)))
+
+(defun pymacs-terminate-services ()
+ ;; This function is mainly provided for documentation purposes.
+ (interactive)
+ (garbage-collect)
+ (pymacs-garbage-collect)
+ (when (or (not pymacs-used-ids)
+ (yes-or-no-p "\
+Killing the helper might create zombie objects. Kill? "))
+ (cond ((boundp 'post-gc-hook)
+ (remove-hook 'post-gc-hook 'pymacs-schedule-gc))
+ ((timerp pymacs-gc-timer)
+ (cancel-timer pymacs-gc-timer)))
+ (when pymacs-transit-buffer
+ (kill-buffer pymacs-transit-buffer))
+ (setq pymacs-gc-running nil
+ pymacs-gc-timer nil
+ pymacs-transit-buffer nil
+ pymacs-lisp nil
+ pymacs-freed-list nil)))
+
+(defun pymacs-serve-until-reply (inserter)
+ ;; This function evals INSERTER to print a Python request. It sends it to
+ ;; the Python helper, and serves all sub-requests coming from the
+ ;; Python side, until either a reply or an error is finally received.
+ (unless (and pymacs-transit-buffer
+ (buffer-name pymacs-transit-buffer)
+ (get-buffer-process pymacs-transit-buffer))
+ (pymacs-start-services))
+ (when pymacs-gc-wanted
+ (pymacs-garbage-collect))
+ (let (done value)
+ (while (not done)
+ (let* ((text (pymacs-round-trip inserter))
+ (reply (condition-case info
+ (eval text)
+ (error (cons 'pymacs-oops (prin1-to-string info))))))
+ (cond ((not (consp reply))
+ (setq inserter
+ `(pymacs-print-for-apply 'reply '(,reply))))
+ ((eq 'pymacs-reply (car reply))
+ (setq done t value (cdr reply)))
+ ((eq 'pymacs-error (car reply))
+ (error "Python: %s" (cdr reply)))
+ ((eq 'pymacs-oops (car reply))
+ (setq inserter
+ `(pymacs-print-for-apply 'error '(,(cdr reply)))))
+ ((eq 'pymacs-expand (car reply))
+ (setq inserter
+ `(pymacs-print-for-apply-expanded 'reply
+ '(,(cdr reply)))))
+ (t (setq inserter
+ `(pymacs-print-for-apply 'reply '(,reply)))))))
+ value))
+
+(defun pymacs-reply (expression)
+ ;; This pseudo-function returns `(pymacs-reply . EXPRESSION)'.
+ ;; `serve-until-reply' later recognises this form.
+ (cons 'pymacs-reply expression))
+
+(defun pymacs-error (expression)
+ ;; This pseudo-function returns `(pymacs-error . EXPRESSION)'.
+ ;; `serve-until-reply' later recognises this form.
+ (cons 'pymacs-error expression))
+
+(defun pymacs-expand (expression)
+ ;; This pseudo-function returns `(pymacs-expand . EXPRESSION)'.
+ ;; `serve-until-reply' later recognises this form.
+ (cons 'pymacs-expand expression))
+
+(defun pymacs-round-trip (inserter)
+ ;; This function evals INSERTER to print a Python request. It sends it to
+ ;; the Python helper, awaits for any kind of reply, and returns it.
+ (with-current-buffer pymacs-transit-buffer
+ (unless pymacs-trace-transit
+ (erase-buffer))
+ (let* ((process (get-buffer-process pymacs-transit-buffer))
+ (status (process-status process))
+ (marker (process-mark process))
+ (moving (= (point) marker))
+ send-position reply-position reply)
+ (save-excursion
+ (save-match-data
+ ;; Encode request.
+ (setq send-position (marker-position marker))
+ (let ((standard-output marker))
+ (eval inserter))
+ (goto-char marker)
+ (unless (= (preceding-char) ?\n)
+ (princ "\n" marker))
+ ;; Send request text.
+ (goto-char send-position)
+ (insert (format ">%d\t" (- marker send-position)))
+ (setq reply-position (marker-position marker))
+ (process-send-region process send-position marker)
+ ;; Receive reply text.
+ (while (and (eq status 'run)
+ (progn
+ (goto-char reply-position)
+ (not (re-search-forward "<\\([0-9]+\\)\t" nil t))))
+ (unless (accept-process-output process pymacs-timeout-at-reply)
+ (setq status (process-status process))))
+ (when (eq status 'run)
+ (let ((limit-position (+ (match-end 0)
+ (string-to-number (match-string 1)))))
+ (while (and (eq status 'run)
+ (< (marker-position marker) limit-position))
+ (unless (accept-process-output process pymacs-timeout-at-line)
+ (setq status (process-status process))))))
+ ;; Decode reply.
+ (if (not (eq status 'run))
+ (error "Pymacs helper status is `%S'." status)
+ (goto-char (match-end 0))
+ (setq reply (read (current-buffer))))))
+ (when (and moving (not pymacs-trace-transit))
+ (goto-char marker))
+ reply)))
+
+(defun pymacs-proper-list-p (expression)
+ ;; Tell if a list is proper, id est, that it is `nil, or ends with `nil'.
+ (cond ((not expression))
+ ((consp expression) (not (cdr (last expression))))))
+
+(provide 'pymacs)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/ide-integration/Pymacs-0.20/rebox Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+# Copyright İ 2002 Progiciels Bourbeau-Pinard inc.
+# François Pinard <pinard@iro.umontreal.ca>, 2002.
+
+"""\
+Handling of boxed comments in various box styles.
+"""
+
+import sys
+from Pymacs import rebox
+apply(rebox.main, tuple(sys.argv[1:]))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/ide-integration/Pymacs-0.20/setup-emacs.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,235 @@
+#!/usr/bin/env python
+# Copyright İ 2001, 2002 Progiciels Bourbeau-Pinard inc.
+# François Pinard <pinard@iro.umontreal.ca>, 2001.
+
+"""\
+Installer tool for Pymacs `pymacs.el'.
+
+Usage: setup [OPTION]
+
+ -H Display this help, then exit.
+ -V Display package name and version, then exit.
+
+ -i Interactively check selected options with user.
+ -n Dry run: merely display selected options.
+ -g GROUP Install with write permissions for that user GROUP.
+ -e Load `.emacs' before checking Emacs `load-path'.
+
+ -l LISPDIR Install `pymacs.el' in LISPDIR.
+ -E EMACS Use that executable for EMACS, if not `emacs'.
+"""
+
+import os, string, sys
+
+sys.path.insert(0, '.')
+from Pymacs import package, version
+del sys.path[0]
+
+AUTOCONF = () # neither a string nor None
+
+class run:
+ interactive = 0
+ dry = 0
+ group = None
+ dot_emacs = 0
+ lispdir = AUTOCONF
+ emacs = 'emacs'
+
+def main(*arguments):
+ import getopt
+ options, arguments = getopt.getopt(arguments, 'E:HVeg:il:n')
+ for option, value in options:
+ if option == '-E' and value:
+ run.emacs = value
+ elif option == '-H':
+ sys.stdout.write(__doc__)
+ sys.exit(0)
+ elif option == '-V':
+ sys.stdout.write('%s-%s' % (package, version))
+ sys.exit(0)
+ elif option == '-e':
+ run.dot_emacs = 1
+ elif option == '-g' and value:
+ run.group = value
+ elif option == '-i':
+ run.interactive = 1
+ elif option == '-l' and value:
+ if value in ('none', 'None'):
+ run.lispdir = None
+ else:
+ run.lispdir = [value]
+ auto_configure()
+ if run.interactive:
+ check_with_user()
+ check_choices()
+ if not run.dry:
+ complete_install()
+
+def auto_configure():
+ if run.lispdir is AUTOCONF:
+ run.lispdir = []
+ import tempfile
+ script = tempfile.mktemp()
+ if sys.platform == 'win32':
+ # Win32 names starting with tilde and Emacs are unhappy together.
+ path, file = os.path.split(script)
+ script = os.path.join(path, 'a' + file)
+ try:
+ open(script, 'w').write('(message "%S" load-path)')
+ load_config = ''
+ if run.dot_emacs:
+ config = os.path.join(os.environ['HOME'], '.emacs')
+ for name in config, config + '.el', config + '.elc':
+ if os.path.isfile(name):
+ # Quote! Spaces are common in Win32 file names.
+ load_config = ' -l "%s"' % name
+ break
+ # Quote! Spaces are common in Win32 file names.
+ text = os.popen('%s -batch%s -l "%s" 2>&1'
+ % (run.emacs, load_config, script)).read()
+ finally:
+ os.remove(script)
+ position = string.find(text, '("')
+ if position >= 0:
+ text = text[position:]
+ if text[-1] == '\n':
+ text = text[:-1]
+ assert text[0] == '(' and text[-1] == ')', text
+ for path in string.split(text[1:-1]):
+ assert path[0] == '"' and path[-1] == '"', path
+ path = path[1:-1]
+ if os.access(path, 7):
+ run.lispdir.append(path)
+
+def check_with_user():
+ sys.stderr.write("""\
+Install tool for %s version %s.
+"""
+ % (package, version))
+ run.lispdir = user_select('lispdir', run.lispdir, """\
+This is where `pymacs.el', the Emacs side code of Pymacs, should go:
+somewhere on your Emacs `load-path'.
+""")
+
+def user_select(name, values, message):
+ write = sys.stderr.write
+ readline = sys.stdin.readline
+ if values is None:
+ write("""\
+
+Enter a value for `%s', or merely type `Enter' if you do not want any.
+"""
+ % name)
+ write(message)
+ while 1:
+ write('%s? ' % name)
+ text = string.strip(readline())
+ if not text:
+ return None
+ if os.access(os.path.expanduser(text), 7):
+ return [text]
+ write("""\
+
+This directory does not exist, or is not writable. Please reenter it.
+""")
+ if len(values) == 1:
+ return values
+ if values == []:
+ write("""\
+
+Pymacs is not likely to install properly, as the installer may not currently
+write in any directory for `%s'. Running as `root' might help you.
+Or else, you will most probably have to revise a bit your work setup.
+"""
+ % name)
+ write(message)
+ return values
+ write("""\
+
+There are many possibilities for `%s', please select one of them by
+typing its number followed by `Enter'. A mere `Enter' selects the first.
+"""
+ % name)
+ write(message)
+ write('\n')
+ for counter in range(len(values)):
+ write('%d. %s\n' % (counter + 1, values[counter]))
+ while 1:
+ write('[1-%d]? ' % len(values))
+ text = string.strip(readline())
+ if not text:
+ return [values[0]]
+ try:
+ counter = int(text)
+ except ValueError:
+ pass
+ else:
+ if 1 <= counter <= len(values):
+ return [values[counter-1]]
+ write("""\
+This is not a valid choice. Please retry.
+""")
+
+def check_choices():
+ write = sys.stderr.write
+ error = 0
+ if run.lispdir is not None:
+ if run.lispdir and os.access(os.path.expanduser(run.lispdir[0]), 7):
+ run.lispdir = run.lispdir[0]
+ else:
+ write("\
+Use `-l LISPDIR' to select where `pymacs.el' should go.\n")
+ error = 1
+ if error:
+ write("ERROR: Installation aborted!\n"
+ " Try `%s -i'.\n" % sys.argv[0])
+ sys.exit(1)
+ write(
+ '\n'
+ "Directory selection for installing Pymacs:\n"
+ " lispdir = %(lispdir)s\n"
+ '\n'
+ % run.__dict__)
+
+def complete_install():
+ run.substitute = {'PACKAGE': package, 'VERSION': version}
+ if run.lispdir:
+ goal = os.path.join(run.lispdir, 'pymacs.el')
+ install('pymacs.el', goal, 0644)
+ compile_lisp(goal)
+
+def install(source, destination, permissions):
+ sys.stderr.write('Installing %s\n' % destination)
+ write = open(destination, 'w').write
+ produce_at = 0
+ #print '*', run.substitute
+ for fragment in string.split(open(source).read(), '@'):
+ #print '**', produce_at, `fragment`
+ if produce_at:
+ replacement = run.substitute.get(fragment)
+ #print '***', replacement
+ if replacement is None:
+ write('@')
+ write(fragment)
+ else:
+ write(replacement)
+ produce_at = 0
+ else:
+ write(fragment)
+ produce_at = 1
+ write = None
+ set_attributes(destination, permissions)
+
+def compile_lisp(name):
+ sys.stderr.write('Compiling %s\n' % name)
+ os.system('%s -batch -f batch-byte-compile %s' % (run.emacs, name))
+ set_attributes(name + 'c', 0644)
+
+def set_attributes(name, permissions):
+ if run.group:
+ os.chown(name, run.group)
+ permissions = permissions | 0020
+ os.chmod(name, permissions)
+
+if __name__ == '__main__':
+ apply(main, sys.argv[1:])
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/ide-integration/Pymacs-0.20/setup.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,17 @@
+#!/usr/bin/env python
+
+import sys
+sys.path.insert(0, '.')
+from Pymacs import package, version
+del sys.path[0]
+
+from distutils.core import setup
+
+setup(name=package,
+ version=version,
+ description='Interface between Emacs LISP and Python.',
+ author='François Pinard',
+ author_email='pinard@iro.umontreal.ca',
+ url='http://www.iro.umontreal.ca/~pinard',
+ scripts=['pymacs-services', 'pymacs-services.bat', 'rebox'],
+ packages=['Pymacs'])
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/ide-integration/bike.vim Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,430 @@
+" Bicycle Repair Man integration for Vim
+" Version 0.3
+" Copyright (c) 2003 Marius Gedminas <mgedmin@delfi.lt>
+"
+" Needs Vim 6.x with python interpreter (Python 2.2 or newer)
+" Installation instructions: just drop it into $HOME/.vim/plugin and you
+" should see the Bicycle Repair Menu appear in GVim (or you can check if
+" any of the BikeXxx commands are defined in console mode Vim). If bike.vim
+" fails to load and you want to see the reason, try
+" let g:bike_exceptions = 1
+" source ~/.vim/plugin/bike.vim
+"
+" Configuration options you can add to your .vimrc:
+" let g:bike_exceptions = 1 " show tracebacks on exceptions
+" let g:bike_progress = 1 " show import progress
+"
+" Commands defined:
+"
+" BikeShowScope
+" Show current scope. Sample of usage:
+" autocmd CursorHold *.py BikeShowScope
+"
+" <range> BikeShowType
+" Show selected expression type. The range is ignored, '<,'> is always
+" used. Use this in visual block mode.
+"
+" BikeFindRefs
+" Finds all references of the function/method/class defined in current
+" line.
+"
+" BikeFindDef
+" Finds the definition of the function/method/class under cursor.
+"
+" BikeRename
+" Renames the function/method/class defined in current line. Updates
+" all references in all files known (i.e. imported) to Bicycle Repair
+" Man.
+"
+" <range> BikeExtract
+" Extracts a part of the function/method into a new function/method.
+" The range is ignored, '<,'> is always used. Use this in visual mode.
+"
+" BikeUndo
+" Globally undoes the previous refactoring.
+"
+" Issues:
+" - Saves all modified buffers to disk without asking the user -- not nice
+" - Does not reimport files that were modified outside of Vim
+" - Uses mktemp() -- I think that produces deprecation warnings in Python 2.3
+" - BikeShowType, BikeExtract ignores the specified range, that's confusing.
+" At least BikeExtract ought to work...
+" - BikeShowType/BikeExtract or their GUI counterparts work when not in
+" visual mode by using the previous values of '<,'>. Might be confusing.
+" - Would be nice if :BikeImport<CR> asked to enter package
+" - Would be nice if :BikeRename myNewName<CR> worked
+" - The code is not very robust (grep for XXX)
+
+"
+" Default settings for global configuration variables {{{1
+"
+
+" Set to 1 to see full tracebacks
+if !exists("g:bike_exceptions")
+ let g:bike_exceptions = 0
+endif
+
+" Set to 1 to see import progress
+if !exists("g:bike_progress")
+ let g:bike_progress = 0
+endif
+
+"
+" Initialization {{{1
+"
+
+" First check that Vim is sufficiently recent, that we have python interpreted
+" support, and that Bicycle Repair Man is available.
+"
+" If something is wrong, fail silently, as error messages every time vim
+" starts are very annoying. But if the user wants to see why bike.vim failed
+" to load, she can let g:bike_exceptions = 1
+if version < 600
+ if g:bike_exceptions
+ echo 'Bicycle Repair Man needs Vim 6.0'
+ endif
+ finish
+endif
+
+if !has("python")
+ if g:bike_exceptions
+ echo 'Bicycle Repair Man needs Vim with Python interpreter support (2.2 or newer)'
+ endif
+ finish
+endif
+
+let s:has_bike=1
+python << END
+import vim
+import sys
+try:
+ if sys.version_info < (2, 2):
+ raise ImportError, 'Bicycle Repair Man needs Python 2.2 or newer'
+ import bike
+ bikectx = bike.init()
+ bikectx.isLoaded # make sure bike package is recent enough
+except ImportError:
+ vim.command("let s:has_bike=0")
+ if vim.eval('g:bike_exceptions') not in (None, '', '0'):
+ raise
+END
+if !s:has_bike
+ finish
+endif
+
+" Use sane cpoptions
+let s:cpo_save = &cpo
+set cpo&vim
+
+"
+" Menu {{{1
+"
+silent! aunmenu Bicycle\ Repair\ Man
+
+" Shortcuts available: ab-----h-jk--nopq----vw--z
+amenu <silent> Bicycle\ &Repair\ Man.-SEP1-
+\ :
+amenu <silent> Bicycle\ &Repair\ Man.&Find\ References
+\ :call <SID>BikeFindRefs()<CR>
+amenu <silent> Bicycle\ &Repair\ Man.Find\ &Definition
+\ :call <SID>BikeFindDef()<CR>
+amenu <silent> Bicycle\ &Repair\ Man.Resu<s.&List<tab>:cl
+\ :cl<CR>
+amenu <silent> Bicycle\ &Repair\ Man.Resu<s.&Current<tab>:cc
+\ :cc<CR>
+amenu <silent> Bicycle\ &Repair\ Man.Resu<s.&Next<tab>:cn
+\ :cn<CR>
+amenu <silent> Bicycle\ &Repair\ Man.Resu<s.&Previous<tab>:cp
+\ :cp<CR>
+amenu <silent> Bicycle\ &Repair\ Man.Resu<s.&First<tab>:cfirst
+\ :cfirst<CR>
+amenu <silent> Bicycle\ &Repair\ Man.Resu<s.Las&t<tab>:clast
+\ :clast<CR>
+amenu <silent> Bicycle\ &Repair\ Man.Resu<s.&Older\ List<tab>:colder
+\ :colder<CR>
+amenu <silent> Bicycle\ &Repair\ Man.Resu<s.N&ewer\ List<tab>:cnewer
+\ :cnewer<CR>
+amenu <silent> Bicycle\ &Repair\ Man.Resu<s.&Window.&Update<tab>:cw
+\ :cw<CR>
+amenu <silent> Bicycle\ &Repair\ Man.Resu<s.&Window.&Open<tab>:copen
+\ :copen<CR>
+amenu <silent> Bicycle\ &Repair\ Man.Resu<s.&Window.&Close<tab>:cclose
+\ :cclose<CR>
+amenu <silent> Bicycle\ &Repair\ Man.-SEP2-
+\ :
+amenu <silent> Bicycle\ &Repair\ Man.&Rename
+\ :call <SID>BikeRename()<CR>
+amenu <silent> Bicycle\ &Repair\ Man.E&xtract\ Method
+\ :call <SID>BikeExtract('method')<CR>
+amenu <silent> Bicycle\ &Repair\ Man.&Extract\ Function
+\ :call <SID>BikeExtract('function')<CR>
+amenu <silent> Bicycle\ &Repair\ Man.&Undo
+\ :call <SID>BikeUndo()<CR>
+amenu <silent> Bicycle\ &Repair\ Man.-SEP3-
+\ :
+amenu <silent> Bicycle\ &Repair\ Man.Settin&gs.Import\ &Progress.&Enable
+\ :let g:bike_progress = 1<CR>
+amenu <silent> Bicycle\ &Repair\ Man.Settin&gs.Import\ &Progress.&Disable
+\ :let g:bike_progress = 0<CR>
+amenu <silent> Bicycle\ &Repair\ Man.Settin&gs.Full\ &Exceptions.&Enable
+\ :let g:bike_exceptions = 1<CR>
+amenu <silent> Bicycle\ &Repair\ Man.Settin&gs.Full\ &Exceptions.&Disable
+\ :let g:bike_exceptions = 0<CR>
+
+" Note: The three rename commands are basically identical. The two extract
+" commands are also identical behind the scenes.
+
+"
+" Commands {{{1
+"
+
+command! BikeShowScope call <SID>BikeShowScope()
+command! -range BikeShowType call <SID>BikeShowType()
+command! BikeFindRefs call <SID>BikeFindRefs()
+command! BikeFindDef call <SID>BikeFindDef()
+
+command! BikeRename call <SID>BikeRename()
+command! -range BikeExtract call <SID>BikeExtract('function')
+command! BikeUndo call <SID>BikeUndo()
+
+"
+" Implementation {{{1
+"
+
+" Query functions {{{2
+
+function! s:BikeShowScope()
+" Shows the scope under cursor
+ python << END
+fn = vim.current.buffer.name
+row, col = vim.current.window.cursor
+try:
+ print bikectx.getFullyQualifiedNameOfScope(fn, row)
+except:
+ show_exc()
+END
+endf
+
+function! s:BikeShowType()
+" Shows the inferred type of the selected expression
+ if col("'<") == 0 " mark not set
+ echo "Select a region first!"
+ return
+ endif
+ if line("'<") != line("'>") " multiline selection
+ echo "Multi-line regions not supported"
+ " XXX deficiency of bikefacade interface, expressions can easily span
+ " several lines in Python
+ return
+ endif
+ python << END
+fn = vim.current.buffer.name
+row1, col1 = vim.current.buffer.mark('<')
+row2, col2 = vim.current.buffer.mark('>')
+try:
+ print bikectx.getTypeOfExpression(fn, row1, col1, col2)
+except:
+ show_exc()
+END
+endf
+
+function! s:BikeFindRefs()
+" Find all references to the item defined on current line
+ wall
+ python << END
+fn = vim.current.buffer.name
+row, col = vim.current.window.cursor
+try:
+ refs = bikectx.findReferencesByCoordinates(fn, row, col)
+ quickfixdefs(refs)
+except:
+ show_exc()
+END
+endf
+
+function! s:BikeFindDef()
+" Find all definitions of the item under cursor. Ideally there should be only
+" one.
+ wall
+ python << END
+fn = vim.current.buffer.name
+row, col = vim.current.window.cursor
+try:
+ defs = bikectx.findDefinitionByCoordinates(fn, row, col)
+ quickfixdefs(defs)
+except:
+ show_exc()
+END
+endf
+
+" Refactoring commands {{{2
+
+function! s:BikeRename()
+" Rename a function/method/class.
+
+ let newname = inputdialog('Rename to: ')
+ wall
+ python << END
+fn = vim.current.buffer.name
+row, col = vim.current.window.cursor
+newname = vim.eval("newname")
+if newname:
+ try:
+ bikectx.setRenameMethodPromptCallback(renameMethodPromptCallback)
+ bikectx.renameByCoordinates(fn, row, col, newname)
+ except:
+ show_exc()
+ saveChanges()
+else:
+ print "Aborted"
+END
+endf
+
+function! s:BikeExtract(what)
+" Extract a piece of code into a separate function/method. The argument
+" a:what can be 'method' or 'function'.
+ if col("'<") == 0 " mark not set
+ echo "Select a region first!"
+ return
+ endif
+ let newname = inputdialog('New function name: ')
+ wall
+ python << END
+fn = vim.current.buffer.name
+row1, col1 = vim.current.buffer.mark('<')
+row2, col2 = vim.current.buffer.mark('>')
+newname = vim.eval("newname")
+if newname:
+ try:
+ bikectx.extractMethod(fn, row1, col1, row2, col2, newname)
+ except:
+ show_exc()
+ saveChanges()
+else:
+ print "Aborted"
+END
+endf
+
+function! s:BikeUndo()
+" Undoes the last refactoring
+ wall
+python << END
+try:
+ bikectx.undo()
+ saveChanges()
+except bike.UndoStackEmptyException, e:
+ print "Nothing to undo"
+END
+endf
+
+"
+" Helper functions {{{1
+"
+
+" Python helpers
+
+python << END
+
+import tempfile
+import os
+import linecache
+
+modified = {} # a dictionary whose keys are the names of files modifed
+ # in Vim since the last import
+
+def show_exc():
+ """Print exception according to bike settings."""
+ if vim.eval('g:bike_exceptions') not in (None, '', '0'):
+ import traceback
+ traceback.print_exc()
+ else:
+ type, value = sys.exc_info()[:2]
+ if value is not None:
+ print "%s: %s" % (type, value)
+ else:
+ print type
+
+def writedefs(defs, filename):
+ """Write a list of file locations to a file."""
+ ef = None
+ curdir = os.getcwd()
+ if not curdir.endswith(os.sep):
+ curdir = curdir + os.sep
+ for d in defs:
+ if not ef:
+ ef = open(filename, "w")
+ fn = d.filename
+ if fn.startswith(curdir):
+ fn = fn[len(curdir):]
+ line = linecache.getline(fn, d.lineno).strip()
+ res ="%s:%d: %3d%%: %s" % (fn, d.lineno, d.confidence, line)
+ print res
+ print >> ef, res
+ if ef:
+ ef.close()
+ return ef is not None
+
+def quickfixdefs(defs):
+ """Import a list of file locations into vim error list."""
+ fn = tempfile.mktemp() # XXX unsafe
+ if writedefs(defs, fn):
+ vim.command('let old_errorfile = &errorfile')
+ vim.command('let old_errorformat = &errorformat')
+ vim.command(r'set errorformat=\%f:\%l:\ \%m')
+ vim.command("cfile %s" % fn)
+ vim.command('let &errorformat = old_errorformat')
+ vim.command('let &errorfile = old_errorfile')
+ os.unlink(fn)
+ else:
+ print "Not found"
+
+def renameMethodPromptCallback(filename, line, col1, col2):
+ """Verify that the call in a given file position should be renamed."""
+ vim.command('e +%d %s' % (line, filename))
+ vim.command('normal %d|' % col1)
+ vim.command('match Search /\%%%dl\%%>%dc\%%<%dc' % (line, col1, col2+1))
+ vim.command('redraw')
+ ans = vim.eval('confirm("Cannot deduce instance type. Rename this call?",'
+ ' "&Yes\n&No", 1, "Question")')
+ vim.command('match none')
+ if ans == '1':
+ return 1
+ else:
+ return 0
+
+
+def saveChanges():
+ """Save refactoring changes to the file system and reload the modified
+ files in Vim."""
+ # bikectx.save() returns a list of modified file names. We should make
+ # sure that all those files are reloaded iff they were open in vim.
+ files = map(os.path.abspath, bikectx.save())
+
+ opened_files = {}
+ for b in vim.buffers:
+ if b.name is not None:
+ opened_files[os.path.abspath(b.name)] = b.name
+
+ for f in files:
+ try:
+ # XXX might fail when file name contains funny characters
+ vim.command("sp %s | q" % opened_files[f])
+ except KeyError:
+ pass
+
+ # Just in case:
+ vim.command("checktime")
+ vim.command("e")
+
+END
+
+" Make sure modified files are reimported into BRM
+autocmd BufWrite * python modified[os.path.abspath(vim.current.buffer.name)] = 1
+
+"
+" Cleanup {{{1
+"
+
+" Restore cpoptions
+let &cpo = s:cpo_save
+unlet s:cpo_save
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/ide-integration/bikeemacs.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,333 @@
+# Bicycle Repair Man integration with (X)Emacs
+# By Phil Dawes (2002)
+# Uses the fabulous Pymacs package by François Pinard
+
+from Pymacs import lisp, Let
+import bike
+reload(bike)
+from bike import bikefacade
+import StringIO, traceback
+import sys
+
+class EmacsLogger:
+ def __init__(self):
+ self.msg = ""
+
+ def write(self,output):
+ self.msg +=output
+ if output.endswith("\n"):
+ lisp.message(self.msg)
+ self.msg = ""
+
+class NullLogger:
+ def write(self,output):
+ pass
+
+logger = EmacsLogger()
+
+
+class ExceptionCatcherWrapper:
+
+ def __init__(self,myobj):
+ self.myobj = myobj
+
+ def __getattr__(self,name):
+ return ExceptionInterceptor(self.myobj,name)
+
+
+class ExceptionInterceptor:
+ def __init__(self,targetobj,name):
+ self.targetobj = targetobj
+ self.name = name
+
+ def __call__(self, *params, **kwparams ):
+ try:
+ return self.makeCall(params)
+ except:
+ traceback.print_exc()
+ lisp.error(str(sys.exc_info()[1]))
+
+ def makeCall(self,params):
+ argsstr="(self.targetobj"
+ for i in range(len(params)):
+ argsstr += ",params["+`i`+"]"
+ argsstr+=")"
+ return eval("self.targetobj.__class__."+self.name+argsstr)
+
+
+class BRMEmacs(object):
+
+ def __init__(self,brmctx):
+ self.ctx = brmctx
+
+ lisp.require(lisp["python-mode"])
+ lisp("""
+ (defvar brm-menu nil "Menu for Bicycle Repair Man")
+ (easy-menu-define
+ brm-menu py-mode-map "Bicycle Repair Man"
+ '("BicycleRepairMan"
+ "Queries"
+ ["Find-References" brm-find-references]
+ ["Find-Definition" brm-find-definition]
+ "---"
+ "Refactoring"
+ ["Rename" brm-rename t]
+ ["Extract-Method" brm-extract-method t]
+ ["Extract-Local-Variable" brm-extract-local-variable t]
+ ["Inline-Local-Variable" brm-inline-local-variable t]
+ ["Undo Last Refactoring" brm-undo t]
+
+ ))
+ (add-hook 'python-mode-hook (lambda () (easy-menu-add brm-menu)))
+ """)
+ # ["Move-Class-To-New-Module" brm-move-class t]
+ # ["Move-Function-To-New-Module" brm-move-class t]
+
+ self.ctx.setProgressLogger(logger)
+
+
+ def rename(self,newname):
+ lisp.save_some_buffers()
+ filename = lisp.buffer_file_name()
+ line,col = _getCoords()
+ brmctx.setRenameMethodPromptCallback(promptCallback)
+ try:
+ self.ctx.renameByCoordinates(filename,line,col,newname)
+ savedFiles = brmctx.save()
+ _revertSavedFiles(savedFiles)
+ lisp.set_marker(lisp.mark_marker(),None)
+ except bikefacade.CouldntLocateASTNodeFromCoordinatesException:
+ print >>logger,"Couldn't find AST Node. Are you renaming the declaration?"
+
+ def kill(self):
+ self.ctx = None
+ self.ctx = bike.init()
+ self.ctx.setProgressLogger(logger)
+
+ def undo(self):
+ brmctx.undo()
+ savedFiles = brmctx.save()
+ _revertSavedFiles(savedFiles)
+
+ def find_references(self):
+ lisp.save_some_buffers()
+ filename = lisp.buffer_file_name()
+ line,col = _getCoords()
+ refs = brmctx.findReferencesByCoordinates(filename,line,col)
+ _switchToConsole()
+ numRefs = 0
+ for ref in refs:
+ _insertRefLineIntoConsole(ref)
+ numRefs +=1
+ lisp.insert("Done - %d refs found\n"%numRefs)
+
+
+ def find_definition(self):
+ lisp.save_some_buffers()
+ filename = lisp.buffer_file_name()
+ line,col = _getCoords()
+ defns = brmctx.findDefinitionByCoordinates(filename,line,col)
+
+ try:
+ firstdefn = defns.next()
+ lisp.find_file_other_window(firstdefn.filename)
+ lisp.goto_line(firstdefn.lineno)
+ lisp.forward_char(firstdefn.colno)
+ except StopIteration:
+ pass
+ else:
+ numRefs = 1
+ for defn in defns:
+ if numRefs == 1:
+ _switchToConsole()
+ _insertRefLineIntoConsole(firstdefn)
+ _insertRefLineIntoConsole(defn)
+ numRefs += 1
+
+
+ def inline_local_variable(self):
+ lisp.save_some_buffers()
+ filename = lisp.buffer_file_name()
+ line,col = _getCoords()
+ brmctx.inlineLocalVariable(filename,line,col)
+ lisp.set_marker(lisp.mark_marker(),None)
+ _revertSavedFiles(brmctx.save())
+
+ def extract_local_variable(self,name):
+ lisp.save_some_buffers()
+ filename = lisp.buffer_file_name()
+
+ bline,bcol = _getPointCoords()
+ lisp.exchange_point_and_mark()
+ eline,ecol = _getPointCoords()
+ lisp.exchange_point_and_mark()
+
+ brmctx.extractLocalVariable(filename,bline,bcol,eline,ecol,name)
+ lisp.set_marker(lisp.mark_marker(),None)
+ _revertSavedFiles(brmctx.save())
+
+ def extract_method(self,name):
+ lisp.save_some_buffers()
+ filename = lisp.buffer_file_name()
+
+ bline,bcol = _getPointCoords()
+ lisp.exchange_point_and_mark()
+ eline,ecol = _getPointCoords()
+ lisp.exchange_point_and_mark()
+
+ brmctx.extract(filename,bline,bcol,eline,ecol,name)
+ lisp.set_marker(lisp.mark_marker(),None)
+ _revertSavedFiles(brmctx.save())
+
+
+ def move_class(self,newfilename):
+ lisp.save_some_buffers()
+ filename = lisp.buffer_file_name()
+ line,col = _getCoords()
+ brmctx.moveClassToNewModule(filename,line,newfilename)
+ _revertSavedFiles(brmctx.save())
+
+
+
+brmctx = bike.init()
+
+brmemacs = None
+
+is_xemacs = (lisp.emacs_version().find("GNU") == -1)
+currently_saving=0
+
+# fix pop_excursion to work with xemacs
+class Let(Let):
+ def pop_excursion(self):
+ method, (buffer, point_marker, mark_marker) = self.stack[-1]
+ assert method == 'excursion', self.stack[-1]
+ del self.stack[-1]
+ lisp.set_buffer(buffer)
+ lisp.goto_char(point_marker)
+ lisp.set_mark(mark_marker)
+ lisp.set_marker(point_marker, None)
+ if mark_marker is not None: # needed for xemacs
+ lisp.set_marker(mark_marker, None)
+
+
+
+def init():
+ global brmemacs
+ brmemacs = ExceptionCatcherWrapper(BRMEmacs(brmctx))
+
+
+def kill():
+ """
+ Removes the bicyclerepairman context (freeing the state),
+ and reinitialises it.
+ """
+ brmemacs.kill()
+kill.interaction=""
+
+
+def promptCallback(filename,line,colbegin,colend):
+ let = Let().push_excursion() # gets popped when let goes out of scope
+ buffer = lisp.current_buffer()
+ if let:
+ ans = 0
+ lisp.find_file(filename)
+ lisp.goto_line(line)
+ lisp.move_to_column(colbegin)
+ lisp.set_mark(lisp.point() + (colend - colbegin))
+ if is_xemacs:
+ lisp.activate_region()
+ ans = lisp.y_or_n_p("Couldn't deduce object type - rename this method reference? ")
+ del let
+ lisp.switch_to_buffer(buffer)
+ return ans
+
+def rename(newname):
+ return brmemacs.rename(newname)
+rename.interaction="sNew name: "
+
+
+def undo():
+ return brmemacs.undo()
+undo.interaction=""
+
+
+def _revertSavedFiles(savedFiles):
+ global currently_saving
+ currently_saving = 1
+ for file in savedFiles:
+ buf = lisp.find_buffer_visiting(file)
+ if buf:
+ lisp.set_buffer(buf)
+ lisp.revert_buffer(None,1)
+ currently_saving = 0
+
+def find_references():
+ brmemacs.find_references()
+find_references.interaction=""
+
+def _getCoords():
+ line = lisp.count_lines(1,lisp.point())
+ col = lisp.current_column()
+ if col == 0:
+ line += 1 # get round 'if col == 0, then line is 1 out' problem
+
+ if mark_exists() and lisp.point() > lisp.mark():
+ lisp.exchange_point_and_mark()
+ col = lisp.current_column()
+ lisp.exchange_point_and_mark()
+ return line,col
+
+def mark_exists():
+ if is_xemacs:
+ return lisp.mark()
+ else:
+ return lisp("mark-active") and lisp.mark()
+
+
+
+def _switchToConsole():
+ consolebuf = lisp.get_buffer_create("BicycleRepairManConsole")
+ lisp.switch_to_buffer_other_window(consolebuf)
+ lisp.compilation_mode("BicycleRepairMan")
+ lisp.erase_buffer()
+ lisp.insert("Bicycle Repair Man\n")
+ lisp.insert("(Hint: Press Return a Link)\n")
+
+def find_definition():
+ brmemacs.find_definition()
+find_definition.interaction=""
+
+def _insertRefLineIntoConsole(ref):
+ lisp.insert(ref.filename+":"+str(ref.lineno)+": "+str(ref.confidence)+"% confidence\n")
+ _redisplayFrame()
+
+def _redisplayFrame():
+ lisp.sit_for(0)
+
+def inline_local_variable():
+ brmemacs.inline_local_variable()
+inline_local_variable.interaction=""
+
+def extract_local_variable(name):
+ brmemacs.extract_local_variable(name)
+extract_local_variable.interaction="sVariable name: "
+
+
+def extract_method(name):
+ brmemacs.extract_method(name)
+extract_method.interaction="sName of function: "
+
+
+def move_class(newfilename):
+ return brmemacs.move_class(newfilename)
+move_class.interaction="fTarget file: "
+
+
+
+def _getPointCoords():
+ bline = lisp.count_lines(1,lisp.point())
+ bcol = lisp.current_column()
+ if bcol == 0: # get round line is one less if col is 0 problem
+ bline += 1
+ return bline,bcol
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/ide-integration/test/README Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,89 @@
+Test Steps for testing idle integration
+---------------------------------------
+
+N.B. this test script is not expected to test all of bicyclerepairman
+- the pyunit tests do that. It is merely there to test the integration
+with idle (which doesnt get tested by pyunit).
+
+
+------------- Rename * -----------------------------------------
+
+- Load scrap.py
+
+- Rename Class to MyRenamedClass
+ (Check it renames the class)
+
+- Undo the rename
+
+- Create scrap3.py, containing the following:
+
+---------------------------
+import scrap
+
+a = scrap.MyClass()
+a.myMethod()
+--------------------------
+
+- Go back to scrap.py and rename the method to MyRenamedMethod
+ (Check that it prompts for rename in scrap2.py - rename the
+ first, but not the second)
+ (Check that it renames all the methods in scrap2.py and scrap3.py
+ except for the one you said no to)
+
+- Undo the rename
+ (Check that it undid all the renamings in all the files)
+
+------------- Find References ----------------------------------
+
+
+- Goto scrap.py, select 'find references'. Check that it tells you to
+highlight a class/function/method.
+
+- Highlight 'myMethod' and try again Check that it displays a list of
+references to this method.
+
+
+------------- Find Definition ----------------------------------
+
+- Goto scrap2.py, click 'myMethod' on d.myMethod(), then select 'find
+definition'. Check that it displays both the myMethod in MyClass and
+in AnotherClass.
+
+
+- Goto scrap2.py, click 'myMethod' on e.myMethod(), then select 'find
+definition'. Check that it takes you to the definition, and doesn't
+display a list of myMethod() references.
+
+
+------------- Extract Method / Function ------------------------
+
+- Load extractMethod.py into idle
+
+- select 'Extract Method' without first selecting a region
+ (Check that it tells you to select the region)
+
+- Use extractMethod to extract the marked line from the function
+
+- Use extractMethod to extract the lines of code from the method
+
+- Undo the extract Method
+
+- Undo the extract Function
+
+- Undo again - confirm that a dialog box pops up telling you the stack
+is empty.
+
+
+------------- Extract / Inline local variable ------------------
+
+- Load extractMethod.py into the ide
+
+- In the function 'inlineVariableTest', extract the marked code into a
+variable
+
+- Inline the variable back into the code
+
+----------------------------------------------------------------
+
+- exit from the ide, and delete scrap3.py from the directory
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/ide-integration/test/extractmethod.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,16 @@
+def myFunction():
+ a = 3
+ print "hello"+a # extract me
+
+class MyClass:
+ def myMethod(self):
+ b = 12 # extract me
+ c = 3 # and me
+ d = 2 # and me
+ print b, c
+
+
+def inlineVariableTest():
+ a = b + 3 - 5
+ # --^^^^^ - Extract this into variable
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/ide-integration/test/scrap.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,19 @@
+class MyClass:
+ def myMethod(self, foo):
+ pass
+
+a = MyClass()
+
+a.myMethod()
+
+
+class AnotherClass:
+ def myMethod(self,foo):
+ pass
+
+def testFunction():
+ print "hello"
+
+
+print "hello"
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/ide-integration/test/scrap2.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,20 @@
+from scrap import MyClass, testFunction
+
+b = MyClass()
+
+b.myMethod()
+
+
+c = abcde()
+
+c.myMethod()
+
+
+d = defgh()
+
+d.myMethod()
+
+
+e = MyClass()
+
+e.myMethod()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/setup.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+
+import sys
+import os,glob
+from distutils.core import setup
+
+# check version
+if sys.version_info[0] < 2 or sys.version_info[0] == 2 and sys.version_info[1] < 2:
+ print "Python versions below 2.2 not supported"
+ sys.exit(0)
+
+
+setup(name="bicyclerepair",
+ version="0.9",
+ description="Bicycle Repair Man, the Python refactoring tool",
+ maintainer="Phil Dawes",
+ maintainer_email="pdawes@users.sourceforge.net",
+ url="http://bicyclerepair.sourceforge.net",
+ packages=['','bike','bike.refactor','bike.parsing','bike.query','bike.transformer'],
+ package_dir = {'bike':'bike','':'ide-integration'}
+ )
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/testall.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,27 @@
+#!/usr/bin/env python
+
+import sys,os
+
+# check version
+if sys.version_info[0] < 2 or sys.version_info[0] == 2 and sys.version_info[1] < 2:
+ print "Python versions below 2.2 not supported"
+ sys.exit(0)
+
+if not os.path.abspath(".") in sys.path:
+ sys.path.append(os.path.abspath("."))
+
+
+from bike import logging
+
+from bike.test_testutils import *
+from bike.parsing.testall import *
+from bike.query.testall import *
+from bike.refactor.testall import *
+from bike.testall import *
+
+if __name__ == "__main__":
+ from bike import logging
+ logging.init()
+ log = logging.getLogger("bike")
+ log.setLevel(logging.WARN)
+ unittest.main()
--- a/vim/sadness/ropevim/rope.vim Tue Nov 16 17:53:55 2010 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,2 +0,0 @@
-let $PYTHONPATH = "/Users/sjl/lib/dotfiles/vim/sadness/ropevim/pylibs:".$PYTHONPATH
-source /Users/sjl/lib/dotfiles/vim/sadness/ropevim/src/ropevim/ropevim.vim
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/sadness.vim Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,7 @@
+let $rope_pypath = $HOME."/.vim/sadness/ropevim/pylibs"
+let $bike_pypath = $HOME."/.vim/sadness/bike"
+
+let $PYTHONPATH = $rope_pypath.":".$bike_pypath.":".$PYTHONPATH
+source $HOME/.vim/sadness/ropevim/src/ropevim/ropevim.vim
+
+source $HOME/.vim/sadness/bike/ide-integration/bike.vim