Commits

Anonymous committed e7d9688

Adding items to help menu

Comments (0)

Files changed (17)

docs/dev/issues.txt

 Metaphor: Enhance features and make rope extensible
 
 
+Release Goals
+-------------
+
+* Rope library
+
+
 Possible Plans
 --------------
 
 To Be Discussed
 ===============
 
-* `Undo Unification`_
+* `Library Movement`_
+* `Function Tools`_
 * `Python's Implicit Interfaces`_
 * Rope's preference saving system: format, location
 * Indexing source files for faster occurrence finding
 * Having a persistant format for PyNames and PyObjects for saving
   hard to compute information like class hierarchies
 * Using a modification of `compiler` AST for simplifying refactorings
+* Undo Unification
 
 
 Supporting Built-in Types
 Inline Method
 =============
 
-For each occurrence we can check whether its result has been used or
-not.  If it has been used we can insert the function body in its
-previous line and assign the return statement to a local variable.
-Otherwise we can remove the function call altogether and insert the
-function definition there.
-
 * Which of the variables should be renamed?
 * What to do about parameter assignments?
 * What to do about staticmethod an classmethods decorators
 and it throws an exception in those cases.
 
 
-Holding Resources Based On Their Path
-=====================================
+Library Movement
+================
 
-:Motive:
-    Supporting opening files before opening a project for help items.
+* Updating files that have changed on disk
+* Removing 4-space indentation assumption
+* Removing tabs
+* Changing `rope.base.fscommands` for projects
+* Adding `read` and `write` to `rope.base.fscommands`
+* Should we expect files to be changed inside or outside the project?
 
-Currently resources are devided into two groups; Those that are
-inside the projects and those that are outside.  We might be able to
-collapse these these two hierarchies by using a path based resource.
-Before doing that we need to handle these:
 
-* Using as reference/value object problems?
-* Updating the path of changed files in project; can we use
-  `Resource.add_change_visitor`
-* Providing old `Resource.get_path`
-* We don't need to check '', anymore?
-* Should we mix files that are inside and outside projects?
-* Can we change out of project files?
-* Should we expect files to be changed inside or outside the project?
+Updating Changed Files
+----------------------
+
+We can add a `Project.update` method that should be called each
+time we use rope classes.  But this seems inefficient if the
+number of updates are high.  We might be able to pass the
+changing file or folder to this method.  We can use this update in
+`fscommands` for moving and removing files, too.
+
+The other issue that might occur when using rope is that a file
+might detect that it has been changed or its path does not exist.
+What should we do in these situations?
+
+
+Function Tools
+==============
+
+* Adding parameters
+* Removing parameters
+* Renaming parameters
+* Change the order of parameters
+* Inline parameter default value
+* Sort and normalize passed call parameters;
+  a_func(p2=v2, p1=v1, p3=v3) -> a_func(v1, v2, p3=v3)
 
 
 Using ASTs For Transformations
 in the formatter, too but it seems a lot of work.
 
 
-Using Rope As A Library
-=======================
+What Rope Assumes...
+====================
 
-Here is a sample session.::
+In order to simplify problems a bit, rope makes some assumptions about
+the source code.  In futures some of this restrictions might be removed.
 
-  # Creating a project
-  >>> from rope.base.project import Project
-  >>> project = Project('.')
-
-  >>> pycore = project.get_pycore()
-  >>> refactoring = project.get_pycore().get_refactoring()
-
-  # Working with files to create and write a module
-  >>> root = project.get_root_folder()
-  >>> mod1 = root.create_file('mod1.py')
-  >>> mod1.write('a_var = 10\n')
-
-  # Creating modules and packages using `PyCore` objects
-  >>> pkg = pycore.create_package(root, 'pkg')
-  >>> mod2 = pycore.create_module(pkg, 'mod2')
-  >>> mod2.write('import mod1\nprint mod1.a_var\n')
-
-  # We can use `PyCore.find_module` for finding modules, too
-  >>> assert mod2 == pycore.find_module('pkg.mod2')
-
-  # Performing rename refactoring on `mod1.a_var`
-  >>> refactoring.rename(mod1, 1, 'new_var')
-  >>> mod1.read()
-  u'new_var = 10\n'
-  >>> mod2.read()
-  u'import mod1\nprint mod1.new_var\n'
-  
-  # Undoing rename refactoring
-  >>> refactoring.undo()
-  >>> mod1.read()
-  u'a_var = 10\n'
-  >>> mod2.read()
-  u'import mod1\nprint mod1.a_var\n'
-
-  >>> pkg.remove()
-  >>> mod1.remove()
-
-The main problem for using rope is that rope should perform the
-changes to the files, since it has to update its stored information.
+* All of the modules should use 4 spaces for indenting and no hard tabs.
+* All files that end with ``.py`` are considered to be python files and
+  all others not.
+* Either all files should be under version control or none.
+* All ``*.txt`` files are considered in reST formats.
+* XXX
 
 
 Designing Refactorings to Be Used in IDEs
   renames but we have to call their constructors separately.
 
 
-Undo Unification
-================
-
-Currently there are separate undo actions for editing text and
-refactorings and in future there might be undos for other changes.  We
-need to mix them together.  This seems hard to do mainly because we
-don't have much access to the undo mechanism of Tkinter.  I think an
-approximation will work.
-
-
 Moving Fields/Methods
 =====================
 
 logical line is very useful in many places for instance.
 
 
-What Rope Assumes...
-====================
-
-In order to simplify problems a bit, rope makes some assumptions about
-the source code.  In futures some of this restrictions might be removed.
-
-* All of the modules should use 4 spaces for indenting and no hard tabs.
-* All files that end with ``.py`` are considered to be python files and
-  all others not.
-* Either all files should be under version control or none.
-* All ``*.txt`` files are considered in reST formats.
-* XXX
-
-
 The GUI Mess; Working More on the UI Parts
 ==========================================
 

docs/dev/library.txt

+=========================
+ Using Rope As A Library
+=========================
+
+
+Useful Classes
+--------------
+
+* `rope.base.project.Project`
+* `rope.base.pycore.PyCore`
+* `rope.refactor.PythonRefactoring`
+* `rope.refactor` sub-modules
+* `rope.ide.codeassist.PythonCodeAssist`
+
+
+A Sample Session
+----------------
+
+Here is a sample session.::
+
+  # Creating a project
+  >>> from rope.base.project import Project
+  >>> project = Project('.')
+
+  >>> pycore = project.get_pycore()
+  >>> refactoring = project.get_pycore().get_refactoring()
+
+  # Working with files to create and write a module
+  >>> root = project.get_root_folder()
+  >>> mod1 = root.create_file('mod1.py')
+  >>> mod1.write('a_var = 10\n')
+
+  # Creating modules and packages using `PyCore` objects
+  >>> pkg = pycore.create_package(root, 'pkg')
+  >>> mod2 = pycore.create_module(pkg, 'mod2')
+  >>> mod2.write('import mod1\nprint mod1.a_var\n')
+
+  # We can use `PyCore.find_module` for finding modules, too
+  >>> assert mod2 == pycore.find_module('pkg.mod2')
+
+  # Performing rename refactoring on `mod1.a_var`
+  >>> refactoring.rename(mod1, 1, 'new_var')
+  >>> mod1.read()
+  u'new_var = 10\n'
+  >>> mod2.read()
+  u'import mod1\nprint mod1.new_var\n'
+  
+  # Undoing rename refactoring
+  >>> refactoring.undo()
+  >>> mod1.read()
+  u'a_var = 10\n'
+  >>> mod2.read()
+  u'import mod1\nprint mod1.a_var\n'
+
+  >>> pkg.remove()
+  >>> mod1.remove()
+
+The main problem for using rope is that rope should perform the
+changes to the files, since it has to update its stored information.

docs/dev/stories.txt

 * Variable indentation and tab size
 
 
-* Find occurances
-
-
 * Configuring keys
 
 
   in codeassist
 
 
-* Local history
-
-
 * Zip imports
 
 
 * Formating Code
 
 
+* Local history
+
+
 * What to do when the program has syntax errors for code assists
 
 
   Do not show submodules that are not imported yet as attributes.
 
 
-* Introduce parameter
-  Change global accesses or attributes accesses to parameters
-
-
 * Pull up method
 
 
-* View type hierarchy
-
-
 * Push down method
 
 
   * Running last run
 
 
-* Open Type; C-T
-
-
 * Replace method with method object refactoring
 
 
 * Moving fields/methods to attribute classes
 
 
+* Handling builtin functions and types
+
+
 * Change method signature
 
   * Add parameter
   * Reorder
 
 
-* Handling builtin functions and types
+* View type hierarchy
+
+
+* Find occurances
+
+
+* Introduce parameter
+  Change global accesses or attributes accesses to parameters
+
+
+* Sorting imports; standard, third party, project
 
 
 > Public Release 0.4m1 : November 19, 2006
 
 
-* Sorting imports; standard, third party, project
+* Open Type; C-T

docs/dev/workingon.txt

-Basic Inline Method
-===================
-
-- Occurances in defining module
-- Raising exceptions for function calls in lines that contain other
-  statements
-- Goto definition for '(a_var.\nattr)\n' for attr
-- Extract variable for selections starting from the start of the line
-- Renaming parameters to match parameters passed in call context
-- Default parameters
-- Testing badly formatted text
-- Handling class methods
-- Handling ``return`` statements
-- Lonely ``return``
-- Multiple ``return``\s
-- Keyword and list parameters
-- Caching calculated definitions
-- Test other calls as parameters
-- References to functions other than calls
-- Recursive calls
-
-Have a look at the ``Inline Method`` section of ``issues.txt``
-
+- Adding ``overview.txt``, ``README.txt``, ``tutorial.txt`` and
+  ``COPYING`` to help menu; Adding readonly mode?
+* Testing on windows
 
 Remaining Stories
 =================
 
-* Adding ``overview.txt``, ``README.txt``, ``tutorial.txt`` and
-  ``COPYING`` to help menu; Adding readonly mode?
+* `PyClass.superclasses` should be concluded data
 * Problems for renaming vars in `__init__.py` of packages!
 * ``How to use rope as a library`` doc
 * Better `ropetest` package structure

rope/base/codeanalyze.py

         last_parens = offset
         current_offset = self._find_last_non_space_char(offset)
         while current_offset > 0 and self.source_code[current_offset] in ')]}':
-            last_parens = current_offset = self._find_parens_start(current_offset)
-            current_offset = self._find_last_non_space_char(current_offset - 1)
+            last_parens = self._find_parens_start(current_offset)
+            current_offset = self._find_last_non_space_char(last_parens - 1)
+        if self.source_code[last_parens] == '(' and self._is_id_char(current_offset):
+           return self._find_primary_without_dot_start(current_offset)
+            
 
         if current_offset > 0 and self.source_code[current_offset] in '\'"':
             return self._find_string_start(current_offset)

rope/base/fscommands.py

     pass
 
 
-def create_fscommands(project):
-    root = project.get_root_folder()
-    if 'pysvn' in globals() and root.has_child('.svn'):
+def create_fscommands(root):
+    if 'pysvn' in globals() and '.svn' in os.listdir(root):
         return SubversionCommands()
     return FileSystemCommands()
 

rope/base/oi/dynamicoi.py

         env = dict(os.environ)
         source_folders = []
         file_path = self.file._get_real_path()
-        for folder in self.file.get_project().get_pycore().get_source_folders():
+        for folder in self.pycore.get_source_folders():
             source_folders.append(os.path.abspath(folder._get_real_path()))
         env['PYTHONPATH'] = env.get('PYTHONPATH', '') + os.pathsep + \
                             os.pathsep.join(source_folders)

rope/base/project.py

 from rope.base.exceptions import RopeException
 
 
-class Project(object):
-    """A Project containing files and folders"""
-
-    def __init__(self, projectRootAddress):
-        self.root = projectRootAddress
-        if not os.path.exists(self.root):
-            os.mkdir(self.root)
-        elif not os.path.isdir(self.root):
-            raise RopeException('Project root exists and is not a directory')
-        self.pycore = rope.base.pycore.PyCore(self)
+class _Project(object):
+    
+    def __init__(self, fscommands):
         self.resources = {}
-        self.resources[''] = RootFolder(self)
-        self.out_of_project_resources = {}
-        self.fscommands = rope.base.fscommands.create_fscommands(self)
-
-    def get_root_folder(self):
-        return self.get_resource('')
-
-    def get_root_address(self):
-        return self.root
+        self.fscommands = fscommands
 
     def get_resource(self, resource_name):
         if resource_name not in self.resources:
                 raise RopeException('Unknown resource ' + resource_name)
         return self.resources[resource_name]
 
-    def get_files(self):
-        return self._get_files_recursively(self.get_root_folder())
-
     def _create_file(self, file_name):
         file_path = self._get_resource_path(file_name)
         if os.path.exists(file_path):
         self.fscommands.create_folder(folder_path)
 
     def _get_resource_path(self, name):
+        pass
+
+    def _update_resource_location(self, resource, new_location=None):
+        del self.resources[resource.get_path()]
+        if new_location is not None:
+            self.resources[new_location] = resource
+
+    def remove_recursively(self, path):
+        self.fscommands.remove(path)
+
+
+class Project(_Project):
+    """A Project containing files and folders"""
+
+    def __init__(self, project_root):
+        self.root = project_root
+        if not os.path.exists(self.root):
+            os.mkdir(self.root)
+        elif not os.path.isdir(self.root):
+            raise RopeException('Project root exists and is not a directory')
+        fscommands = rope.base.fscommands.create_fscommands(self.root)
+        super(Project, self).__init__(fscommands)
+        self.pycore = rope.base.pycore.PyCore(self)
+        self.resources[''] = Folder(self, '')
+        self.no_project = NoProject()
+
+    def get_root_folder(self):
+        return self.get_resource('')
+
+    def get_root_address(self):
+        return self.root
+
+    def get_files(self):
+        return self._get_files_recursively(self.get_root_folder())
+
+    def _get_resource_path(self, name):
         return os.path.join(self.root, *name.split('/'))
 
     def _get_files_recursively(self, folder):
         return self.pycore
 
     def get_out_of_project_resource(self, path):
-        path = os.path.abspath(path)
-        if path not in self.out_of_project_resources:
-            if not os.path.exists(path):
-                raise RopeException('Resource %s does not exist' % path)
-            elif os.path.isfile(path):
-                self.out_of_project_resources[path] = OutOfProjectFile(self, path)
-            elif os.path.isdir(path):
-                self.out_of_project_resources[path] = OutOfProjectFolder(self, path)
-            else:
-                raise RopeException('Unknown resource ' + path)
-        return self.out_of_project_resources[path]
+        return self.no_project.get_resource(path)
     
-    def _update_resource_location(self, resource, new_location=None):
-        del self.resources[resource.get_path()]
-        if new_location is not None:
-            self.resources[new_location] = resource
 
-    def remove_recursively(self, path):
-        self.fscommands.remove(path)
+class NoProject(_Project):
+    """A null object for holding out of project files"""
+    
+    def __init__(self):
+        fscommands = rope.base.fscommands.FileSystemCommands()
+        super(NoProject, self).__init__(fscommands)
+    
+    def _get_resource_path(self, name):
+        return os.path.abspath(name)
+    
+    def get_resource(self, name):
+        return super(NoProject, self).get_resource(os.path.abspath(name))
 
 
 class Resource(object):
         self.observers = []
 
     def get_path(self):
-        """Returns the path of this resource relative to the project root
+        """Return the path of this resource relative to the project root
         
         The path is the list of parent directories separated by '/' followed
         by the resource name.
         return self.name
 
     def get_name(self):
-        """Returns the name of this resource"""
+        """Return the name of this resource"""
         return self.name.split('/')[-1]
     
     def remove(self):
-        """Removes resource from the project"""
+        """Remove resource from the project"""
     
     def move(self, new_location):
-        """Moves resource to new_lcation"""
+        """Move resource to new_lcation"""
 
     def is_folder(self):
-        """Returns true if the resource is a folder"""
-
-    def get_project(self):
-        """Returns the project this resource belongs to"""
-        return self.project
+        """Return true if the resource is a folder"""
 
     def add_change_observer(self, observer):
         self.observers.append(observer)
         return self.project.get_resource(parent)
 
     def _get_real_path(self):
-        """Returns the file system path of this resource"""
+        """Return the file system path of this resource"""
         return self.project._get_resource_path(self.name)
     
     def _get_destination_for_move(self, destination):
         return destination
 
 
-class _File(Resource):
-    """Represents a file in a project"""
+class File(Resource):
+    """Represents a file"""
 
     def __init__(self, project, name):
-        super(_File, self).__init__(project, name)
+        super(File, self).__init__(project, name)
     
     def read(self):
-        source_bytes = self._read_file_data()
+        source_bytes = open(self._get_real_path()).read()
         return self._file_data_to_unicode(source_bytes)
-    
-    def _read_file_data(self):
-        """Should be implemented in subclasses"""
-    
+        
     def _file_data_to_unicode(self, data):
         encoding = self._conclude_file_encoding(data)
         if encoding is not None:
     
     def _conclude_file_encoding(self, source_bytes):
         first_two_lines = source_bytes[:self._get_second_line_end(source_bytes)]
-        match = _File.encoding_pattern.search(first_two_lines)
+        match = File.encoding_pattern.search(first_two_lines)
         if match is not None:
             return match.group(1)
 
     def write(self, contents):
-        file_ = open(self.project._get_resource_path(self.name), 'w')
+        file_ = open(self._get_real_path(), 'w')
         encoding = self._conclude_file_encoding(contents)
         if encoding is not None and isinstance(contents, unicode):
             contents = contents.encode(encoding)
         return False
 
     def remove(self):
-        self.project.remove_recursively(self.project._get_resource_path(self.name))
+        self.project.remove_recursively(self._get_real_path())
         self.project._update_resource_location(self)
         for observer in list(self.observers):
             observer(self)
 
     def move(self, new_location):
         destination = self._get_destination_for_move(new_location)
-        self.project.fscommands.move(self.project._get_resource_path(self.name),
+        self.project.fscommands.move(self._get_real_path(),
                                      self.project._get_resource_path(destination))
         self.project._update_resource_location(self, destination)
         self.get_parent()._child_changed(self)
             observer(self)
     
 
-class File(_File):
-    """Represents a file in a project"""
-
-    def _read_file_data(self):
-        return open(self.project._get_resource_path(self.name)).read()
-
-
-class OutOfProjectFile(_File):
-    """Represents a file outside a project"""
-
-    def __init__(self, project, path):
-        super(OutOfProjectFile, self).__init__(project, path)
-        self.path = path
-        
-    def _read_file_data(self):
-        return open(self.path).read()
-
-    def _get_real_path(self):
-        """Returns the file system path of this resource"""
-        return self.path
-
-    def get_parent(self):
-        parent = '/'.join(self.path.split('/')[0:-1])
-        return self.project.get_out_of_project_resource(parent)
-
-
-class _Folder(Resource):
-    """Represents a folder in a project"""
+class Folder(Resource):
+    """Represents a folder"""
 
     def __init__(self, project, name):
-        super(_Folder, self).__init__(project, name)
+        super(Folder, self).__init__(project, name)
 
 
     def is_folder(self):
     def remove(self):
         for child in self.get_children():
             child.remove()
-        self.project.remove_recursively(self.project._get_resource_path(self.name))
+        self.project.remove_recursively(self._get_real_path())
         self.project._update_resource_location(self)
         self.get_parent()._child_changed(self)
 
         for child in self.get_children():
             if not (child.is_folder() and child.get_name() == '.svn'):
                 child.move(destination + '/' + child.get_name())
-        self.project.fscommands.remove(self.project._get_resource_path(self.get_path()))
+        self.project.fscommands.remove(self._get_real_path())
         self.project._update_resource_location(self, destination)
         self.get_parent()._child_changed(self)
         self.name = destination
         if child != self:
             for observer in list(self.observers):
                 observer(self)
-
-
-class Folder(_Folder):
-    """Represents a non root folder in a project"""
-
-    def __init__(self, project, folderName):
-        super(Folder, self).__init__(project, folderName)
-
-
-class RootFolder(_Folder):
-    """Represents the root folder of a project"""
-
-    def __init__(self, project):
-        super(RootFolder, self).__init__(project, '')
-
-
-class OutOfProjectFolder(_Folder):
-    """Represents a folder outside the project"""
-
-    def __init__(self, project, path):
-        super(OutOfProjectFolder, self).__init__(project, path)
-        self.path = path
-    
-    def get_children(self):
-        result = []
-        content = os.listdir(self.path)
-        for name in content:
-            resource_path = os.path.join(self.path, name)
-            result.append(self.project.get_out_of_project_resource(resource_path))
-        return result
-
-    def get_child(self, name):
-        child_path = os.path.join(self.path, name)
-        return self.project.get_out_of_project_resource(child_path)
-    
-    def _get_real_path(self):
-        """Returns the file system path of this resource"""
-        return self.path
-
-    def get_parent(self):
-        parent = '/'.join(self.path.split('/')[0:-1])
-        return self.project.get_out_of_project_resource(parent)
         import rope.ui.editactions
         import rope.ui.codeassist
         import rope.ui.refactor
-        for context in editingcontexts.contexts.values():
-            context.menu_manager.add_menu_cascade(MenuAddress(['Help'], 'p'))
-            context.menu_manager.add_menu_command(MenuAddress(['Help', 'About'], 'a'),
-                                                  self._show_about_dialog)
+        import rope.ui.helpactions
 
     def _add_menu_cascade(self, menu_address, active_contexts):
         active_contexts = self._get_matching_contexts(active_contexts)
     def _close_project_and_exit(self):
         self._close_project_dialog(exit_=True)
 
-    def _show_about_dialog(self):
-        toplevel = Toplevel()
-        toplevel.title('About Rope')
-        text = 'rope, a python refactoring IDE ...\n' + \
-               'version ' + rope.VERSION + '\n\n' + \
-               'Copyright (C) 2006 Ali Gholami Rudi\n\n' + \
-               'This program is free software; you can redistribute it and/or modify it\n' + \
-               'under the terms of GNU General Public License as published by the \n' + \
-               'Free Software Foundation; either version 2 of the license, or (at your \n' + \
-               'opinion) any later version.\n\n' + \
-               'This program is distributed in the hope that it will be useful,\n' + \
-               'but WITHOUT ANY WARRANTY; without even the implied warranty of\n' + \
-               'MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n' + \
-               'GNU General Public License for more details.\n'
-        label = Label(toplevel, text=text, height=16, width=70, 
-                      justify=LEFT, relief=GROOVE)
-        def ok():
-            toplevel.destroy()
-        ok_button = Button(toplevel, text='OK', command=ok)
-        label.grid()
-        ok_button.grid()
-        toplevel.focus_set()
-
     def _init_key_binding(self):
         def do_switch_active_editor(event):
             self.switch_active_editor()

rope/ui/editor.py

         else:
             font = tkFont.Font(family='Courier', size=13)
         self.text = ScrolledText.ScrolledText(parent, bg='white', font=font,
-                                 undo=True, maxundo=100, highlightcolor='#99A')
+                                 undo=True, maxundo=50, highlightcolor='#99A')
         self.change_inspector = _TextChangeInspector(self, self._text_changed)
         self.searcher = rope.ui.searcher.Searcher(self)
         self._set_editingcontexts(editorcontext)

rope/ui/editorpile.py

         self.editors.insert(0, editor)
         self.core._editor_changed()
 
-    def get_resource_editor(self, file_):
+    def get_resource_editor(self, file_, readonly=False):
         for editor in self.editors:
             if editor.get_file() == file_:
                 self.buttons[editor].invoke()
                 return editor
         editor = rope.ui.fileeditor.FileEditor(
             self.core.get_open_project(), file_,
-            rope.ui.editor.GraphicalEditorFactory(self.editor_frame))
+            rope.ui.editor.GraphicalEditorFactory(self.editor_frame), readonly)
         editor.get_editor().set_status_bar_manager(self.core.status_bar_manager)
         self.editors.append(editor)
         title = Radiobutton(self.editor_list, text=file_.get_name(),

rope/ui/fileactions.py

 import tkMessageBox
 
 import rope.base.project
-
 import rope.ui.core
 from rope.ui.menubar import MenuAddress
 from rope.ui.extension import SimpleAction

rope/ui/fileeditor.py

+import Tkinter
+
+import rope.base.exceptions
 from rope.ui import editingcontexts
 
 class FileEditor(object):
 
-    def __init__(self, project, file_, editor_factory):
+    def __init__(self, project, file_, editor_factory, readonly=False):
         self.file = file_
         self.project = project
         editingcontext = None
         self.file.get_parent().add_change_observer(self._editor_was_modified)
         self.file.add_change_observer(self._file_was_modified)
         self.saving = False
+        self.readonly = readonly
+        #if readonly:
+        #    self.editor.getWidget()['state'] = Tkinter.DISABLED
     
     def _editor_was_modified(self, *args):
         for observer in list(self.modification_observers):
         self.modification_observers.append(observer)
 
     def save(self):
+        if self.readonly:
+            raise rope.base.exceptions.RopeUIException(
+                'File is opened in readonly mode!')
         self.saving = True
         try:
             self.file.write(self.editor.get_text())

rope/ui/helpactions.py

+import sys
+import os.path
+import Tkinter
+
+from rope.base.project import NoProject
+import rope.ui.core
+from rope.ui.menubar import MenuAddress
+from rope.ui.extension import SimpleAction
+
+_no_project = NoProject()
+
+def show_about_dialog(context):
+    toplevel = Tkinter.Toplevel()
+    toplevel.title('About Rope')
+    text = 'rope, a python refactoring IDE ...\n' + \
+           'version ' + rope.VERSION + '\n\n' + \
+           'Copyright (C) 2006 Ali Gholami Rudi\n\n' + \
+           'This program is free software; you can redistribute it and/or modify it\n' + \
+           'under the terms of GNU General Public License as published by the \n' + \
+           'Free Software Foundation; either version 2 of the license, or (at your \n' + \
+           'opinion) any later version.\n\n' + \
+           'This program is distributed in the hope that it will be useful,\n' + \
+           'but WITHOUT ANY WARRANTY; without even the implied warranty of\n' + \
+           'MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n' + \
+           'GNU General Public License for more details.\n'
+    label = Tkinter.Label(toplevel, text=text, height=16, width=70, 
+                          justify=Tkinter.LEFT, relief=Tkinter.GROOVE)
+    def ok():
+        toplevel.destroy()
+    def show_gpl():
+        show_copying(context)
+        toplevel.destroy()
+    label.grid(row=0, column=0, columnspan=2)
+    ok_button = Tkinter.Button(toplevel, text='OK', command=ok)
+    gpl_button = Tkinter.Button(toplevel, text='Show GNU GPL', command=show_gpl)
+    ok_button.grid(row=1, column=0)
+    gpl_button.grid(row=1, column=1)
+    ok_button.focus_set()
+
+def show_doc(context, name):
+    rope_package = (os.path.dirname(sys.modules['rope'].__file__))
+    # Checking whether rope is installed or not
+    if 'docs' in os.listdir(rope_package):
+        root = rope_package
+        resource = _no_project.get_resource(root + '/docs/' + name.split('/')[-1])
+    else:
+        root = os.path.dirname(rope_package)
+        resource = _no_project.get_resource(root + '/' + name)
+    editor_manager = context.get_core().get_editor_manager()
+    editor_manager.get_resource_editor(resource, readonly=True)
+
+def show_readme(context):
+    show_doc(context, 'README.txt')
+
+def show_overview(context):
+    show_doc(context, 'docs/user/overview.txt')
+
+def show_tutorial(context):
+    show_doc(context, 'docs/user/tutorial.txt')
+
+def show_contributing(context):
+    show_doc(context, 'docs/dev/contributing.txt')
+
+def show_library(context):
+    show_doc(context, 'docs/dev/library.txt')
+
+def show_copying(context):
+    show_doc(context, 'COPYING')
+
+
+core = rope.ui.core.Core.get_core()
+core._add_menu_cascade(MenuAddress(['Help'], 'p'), ['all', 'none'])
+actions = []
+
+actions.append(SimpleAction('HelpReadme', show_readme, None,
+                            MenuAddress(['Help', 'Readme'], 'r')))
+actions.append(SimpleAction('HelpOverview', show_overview, None,
+                            MenuAddress(['Help', 'Overview'], 'o')))
+actions.append(SimpleAction('HelpTutorial', show_tutorial, None,
+                            MenuAddress(['Help', 'Tutorial'], 't')))
+
+actions.append(SimpleAction('HelpContributing', show_contributing, None,
+                            MenuAddress(['Help', 'Contributing'], 'n', 1)))
+actions.append(SimpleAction('HelpLibrary', show_library, None,
+                            MenuAddress(['Help', 'Using Rope As A Library'],
+                                        'l', 1)))
+
+actions.append(SimpleAction('About', show_about_dialog, None,
+                            MenuAddress(['Help', 'About Rope'], 'a', 2)))
+
+for action in actions:
+    core.register_action(action)

ropetest/codeanalyzetest.py

         self.assertEquals('a_func  (  "(" ) .   an_attr',
                           word_finder.get_primary_at(26))
 
+    def test_functions_on_ending_parens(self):
+        word_finder = WordRangeFinder('A()')
+        self.assertEquals('A()', word_finder.get_primary_at(2))
+
     def test_splitted_statement(self):
         word_finder = WordRangeFinder('an_object.an_attr')
         self.assertEquals(('an_object', 'an_at', 10),

ropetest/projecttest.py

 import unittest
 import os
 
-from rope.base.project import Project, RootFolder
+from rope.base.project import Project
 from rope.base.exceptions import RopeException
 from ropetest import testutils
 
                           folder.get_child('myfolder'))
 
     def test_project_root_is_root_folder(self):
-        self.assertTrue(isinstance(self.project.get_root_folder(), RootFolder))
+        self.assertEquals('', self.project.get_root_folder().get_path())
 
     def test_resource_change_observer(self):
         sample_file = self.project.get_root_folder().create_file('my_file.txt')
         sample_file_path = os.path.join(self.test_directory, 'sample.txt')
         file(sample_file_path, 'w').close()
         normal_sample_resource = self.project.get_out_of_project_resource(sample_file_path)
-        absolute_sample_resource = self.project.get_out_of_project_resource(os.path.abspath(sample_file_path))
+        absolute_sample_resource = \
+            self.project.get_out_of_project_resource(os.path.abspath(sample_file_path))
         self.assertEquals(normal_sample_resource, absolute_sample_resource)
 
     def test_folder_get_child(self):
 
 if __name__ == '__main__':
     unittest.main()
-
+import glob
 import os
 import shutil
 from distutils.core import setup
 
 import rope
 
-
-def make_scripts():
+def make_temps():
     if not os.path.exists('scripts'):
         os.mkdir('scripts')
     shutil.copy('rope.py', 'scripts/rope')
+    # copying docs
+    if not os.path.exists('rope/docs'):
+        os.mkdir('rope/docs')
+    docs = ['README.txt', 'COPYING']
+    docs.extend(glob.glob('docs/user/*.txt'))
+    docs.extend(glob.glob('docs/dev/*.txt'))
+    for name in docs:
+        shutil.copy(name, 'rope/docs/')
 
+def remove_temps():
+    if os.path.exists('scripts'):
+        shutil.rmtree('scripts')
+    if os.path.exists('rope/docs'):
+        shutil.rmtree('rope/docs')
 
-make_scripts()
+make_temps()
 
 classifiers=['Development Status :: 3 - Alpha',
              'Operating System :: OS Independent',
       url='http://rope.sf.net/',
       packages=['rope', 'rope.base', 'rope.base.oi', 'rope.refactor',
                 'rope.ide', 'rope.ui'],
+      package_data={'rope': ['docs/COPYING', 'docs/*.txt']},
       scripts=['scripts/rope'],
       classifiers=classifiers)
 
+remove_temps()