ruby/src/org/netbeans/modules/ruby/RubyRenameHandler.java
author enebo@netbeans.org
Sun, 08 Dec 2013 12:20:16 -0600
changeset 4555 3773928e70d0
parent 4554 07958c1ff402
child 4556 e276e451257b
permissions -rw-r--r--
Let jruby-parser figure out occurrences and delete netbeans logic for this
     1 /*
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  *
     4  * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
     5  *
     6  * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
     7  * Other names may be trademarks of their respective owners.
     8  *
     9  * The contents of this file are subject to the terms of either the GNU
    10  * General Public License Version 2 only ("GPL") or the Common
    11  * Development and Distribution License("CDDL") (collectively, the
    12  * "License"). You may not use this file except in compliance with the
    13  * License. You can obtain a copy of the License at
    14  * http://www.netbeans.org/cddl-gplv2.html
    15  * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    16  * specific language governing permissions and limitations under the
    17  * License.  When distributing the software, include this License Header
    18  * Notice in each file and include the License file at
    19  * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    20  * particular file as subject to the "Classpath" exception as provided
    21  * by Oracle in the GPL Version 2 section of the License file that
    22  * accompanied this code. If applicable, add the following below the
    23  * License Header, with the fields enclosed by brackets [] replaced by
    24  * your own identifying information:
    25  * "Portions Copyrighted [year] [name of copyright owner]"
    26  *
    27  * Contributor(s):
    28  *
    29  * The Original Software is NetBeans. The Initial Developer of the Original
    30  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
    31  * Microsystems, Inc. All Rights Reserved.
    32  *
    33  * If you wish your version of this file to be governed by only the CDDL
    34  * or only the GPL Version 2, indicate your decision by adding
    35  * "[Contributor] elects to include this software in this distribution
    36  * under the [CDDL or GPL Version 2] license." If you do not indicate a
    37  * single choice of license, a recipient has the option to distribute
    38  * your version of this file under either the CDDL, the GPL Version 2 or
    39  * to extend the choice of license to its licensees as provided above.
    40  * However, if you add GPL Version 2 code and therefore, elected the GPL
    41  * Version 2 license, then the option applies only if the new code is
    42  * made subject to such option by the copyright holder.
    43  */
    44 package org.netbeans.modules.ruby;
    45 
    46 import java.util.Collections;
    47 import java.util.HashSet;
    48 import java.util.Set;
    49 
    50 import org.jrubyparser.ast.Node;
    51 import org.jrubyparser.ast.ILocalVariable;
    52 import org.netbeans.modules.csl.api.InstantRenamer;
    53 import org.netbeans.modules.csl.api.OffsetRange;
    54 import org.netbeans.modules.csl.spi.ParserResult;
    55 import org.netbeans.modules.ruby.lexer.LexUtilities;
    56 import org.openide.util.NbBundle;
    57 
    58 /**
    59  * Handle renaming of local elements
    60  * @todo I should be able to rename top-level methods as well since they
    61  *   are private
    62  * @todo Rename |j| in the following will only rename "j" inside the block!
    63  * <pre>
    64 i = 50
    65 j = 200
    66 k = 100
    67 x = [1,2,3]
    68 x.each do |j|
    69   puts j
    70 end
    71 puts j
    72  * </pre>
    73  * @todo When you fix, make sure BlockarReuse is also fixed!
    74  * @todo Try renaming "hello" in the exception here; my code is confused
    75  *   about what I'm renaming (aliases method name) and the refactoring dialog
    76  *   name is wrong! This is happening because it's also changing GlobalAsgnNode for $!
    77  *   but its parent is LocalAsgnNode, and -its- -grand- parent is a RescueBodyNode! 
    78  *   I should special case this!
    79  * <pre>
    80 def hello
    81   begin
    82     ex = 50
    83     puts "test"
    84   
    85   rescue Exception => hello
    86     puts hello
    87   end
    88 end
    89  *
    90  * </pre>
    91  *
    92  * @author Tor Norbye
    93  */
    94 public class RubyRenameHandler implements InstantRenamer {
    95     
    96     public RubyRenameHandler() {
    97     }
    98 
    99     @Override
   100     public boolean isRenameAllowed(ParserResult info, int caretOffset, String[] explanationRetValue) {
   101         Node root = AstUtilities.getRoot(info);
   102 
   103         if (root == null) {
   104             explanationRetValue[0] = NbBundle.getMessage(RubyRenameHandler.class, "NoRenameWithErrors");
   105             return false;
   106         }
   107 
   108         Node closest = root.getNodeAt(AstUtilities.getAstOffset(info, caretOffset));
   109         if (closest == null) return false;
   110         if (closest instanceof ILocalVariable) return true;  // All local block/method vars can be renamed
   111 
   112         switch (closest.getNodeType()) {
   113         case INSTASGNNODE: case INSTVARNODE: case CLASSVARDECLNODE: case CLASSVARNODE: case CLASSVARASGNNODE:
   114         case GLOBALASGNNODE: case GLOBALVARNODE: case CONSTDECLNODE: case CONSTNODE: case DEFNNODE: case DEFSNODE:
   115         case FCALLNODE: case CALLNODE: case VCALLNODE: case COLON2NODE: case COLON3NODE: case ALIASNODE:
   116         case SYMBOLNODE: // TODO - what about the string arguments in an alias node? Gotta check those
   117             return true;
   118         }
   119 
   120         return false;
   121     }
   122 
   123     @Override
   124     public Set<OffsetRange> getRenameRegions(ParserResult info, int caretOffset) {
   125         Node closest = AstUtilities.findNodeAtOffset(info, caretOffset);
   126         if (closest == null || !(closest instanceof ILocalVariable)) return Collections.emptySet();
   127 
   128         ILocalVariable variable = (ILocalVariable) closest;
   129         Set<OffsetRange> regions = new HashSet<OffsetRange>();
   130 
   131         for (ILocalVariable occurrence: variable.getOccurrences()) {
   132             OffsetRange range = LexUtilities.getLexerOffsets(info, 
   133                     AstUtilities.offsetRangeFor(occurrence.getNamePosition()));
   134 
   135             if (range != OffsetRange.NONE) regions.add(range);
   136         }
   137 
   138         return regions;
   139     }
   140 
   141     // TODO: Check
   142     //  quick tip renaming
   143     //  unused detection
   144     //  occurrences marking
   145     //  code completion
   146     //  live code templates
   147     // ...anyone else who calls findBlock
   148     //
   149     // Test both parent blocks, sibling blocks and descendant blocks
   150     // Make sure the "isUsed" detection is smarter too.
   151 }