/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved. * * Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common * Development and Distribution License("CDDL") (collectively, the * "License"). You may not use this file except in compliance with the * License. You can obtain a copy of the License at * http://www.netbeans.org/cddl-gplv2.html * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the * specific language governing permissions and limitations under the * License. When distributing the software, include this License Header * Notice in each file and include the License file at * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the GPL Version 2 section of the License file that * accompanied this code. If applicable, add the following below the * License Header, with the fields enclosed by brackets [] replaced by * your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * Contributor(s): * * Portions Copyrighted 2008 Sun Microsystems, Inc. */ package org.netbeans.modules.ruby; import java.util.ArrayList; import java.util.List; import javax.swing.text.BadLocationException; import org.jrubyparser.ast.FCallNode; import org.jrubyparser.ast.MethodDefNode; import org.jrubyparser.ast.Node; import org.jrubyparser.ast.NodeType; import org.netbeans.api.ruby.platform.RubyPlatform; import org.netbeans.modules.csl.spi.GsfUtilities; import org.netbeans.modules.csl.spi.ParserResult; import org.netbeans.modules.parsing.api.Snapshot; import org.netbeans.modules.ruby.elements.AstElement; import org.netbeans.modules.ruby.elements.IndexedElement; import org.netbeans.modules.ruby.elements.IndexedMethod; import org.netbeans.modules.ruby.elements.MethodElement; import org.netbeans.modules.ruby.options.TypeInferenceSettings; import org.openide.filesystems.FileObject; /** * This class contains a lot of logic used to build the code indices * for Ruby. * * This level of indexing is not performed on the user's sources, only * on preindexed libraries such as the Rails gems and Ruby's own library. * * We need various extra information that relies on various heuristics * to determine: * * <ul> * <li> Does a method take a block? If so, how many arguments should * the block take and what names should the IDE suggest? * <li> Should a particular method be used with parentheses or not? * For example, when calling * File.exists?("foo") * we normally use parens around the arguments, but when calling * DSL-style methods like validates_presence_of it's common not to. * <li> Are one or more of the arguments expected to to be hashes? * If so, what are the available hash key names? What are their * "types"? (e.g. ":controller" is obviously a Rails controller, * so for code completion I can offer the various controller names, * in other cases we may expect a rails model or database table * name and I should record these well known types somehow. * </ul> * * * @todo Rename PlatformFileIndexer, and move activesupport stuff in here * @todo Dynamically "patch" index entries for things I don't actually have. * * @author Tor Norbye */ public final class RubyIndexerHelper { private RubyIndexerHelper() { // Private utility class, not instantiatable } /** * @todo Compute parameter delimiters (e.g. DSL methods which shouldn't have params should be noted here * @todo Compute hash arguments * @todo Gotta store the WHOLE argument string, but avoid "indexOf" looks at the * attribute list from getting too perky // TODO - store deprecated status? * * @param child * @param indexedList * @param notIndexedList * @param topLevel * @param signature * @param fo * @param doc * @return An updated signature, or null if this method should be removed from index */ public static String getMethodSignature(AstElement child, int flags, String signature, FileObject fo, ContextKnowledge knowledge) { if (knowledge.getParserResult().getSnapshot() == null) { // tests? return signature; } ParserResult parserResult = knowledge.getParserResult(); Node root = knowledge.getRoot(); try { String hashNames = ""; int originalFlags = flags; List<String> rdocs = null; if (isRubyStubs(fo)) { rdocs = getCallSeq(child, parserResult.getSnapshot()); } else if (TypeInferenceSettings.getDefault().getRdocTypeInference()) { rdocs = AstUtilities.gatherDocumentation(parserResult.getSnapshot(), child.getNode()); } // See if the method takes blocks //List<Node> yields = new ArrayList<Node>(); //AstUtilities.addNodesByType(child.getNode(), new int[]{NodeType.YIELDNODE}, yields); //if (yields.size() > 0) { // // Yes, this method appears to have a yield... compute its args // // See if it's optional... // for (Node n : yields) { // // Store arity here // // TODO - compute block names here // // This is tricky though - take a look at optparse's // // summarize method - it has // // yield(indent + l) // // and really, it's the "l" part here that's interesting // // (l=line) not say the indent string that it's prefixing // // with. // } //} // Is the block optional? List<Node> calls = new ArrayList<Node>(); AstUtilities.addNodesByType(child.getNode(), new NodeType[]{NodeType.FCALLNODE}, calls); boolean optionalBlock = false; for (Node call : calls) { if ("block_given?".equals(((FCallNode)call).getName())) { // NOI18N optionalBlock = true; break; } } // Look at callseq to see if the block appears to be available // or even mandatory - do some or all of the descriptions // include a block? String blockArgs = null; if (rdocs != null) { boolean seenBlock = false; boolean seenNonBlock = false; for (String line : rdocs) { // Does this line imply a block? // Look for methods(args) { ... // or method { ... boolean seenLeftParen = false; boolean seenRightParen = false; boolean hasBlock = false; for (int i = 0; i < line.length(); i++) { char c = line.charAt(i); if (c == '(') { seenLeftParen = true; } else if (c == ')') { seenRightParen = true; } else if (c == '{') { if ((!seenLeftParen && !seenRightParen) || (seenLeftParen && seenRightParen)) { hasBlock = true; if (blockArgs == null) { // See if I can find the yield name int blockBegin = line.indexOf('|', i+1); if (blockBegin != -1) { int blockEnd = line.indexOf('|', blockBegin+1); if (blockEnd != -1) { StringBuilder sb = new StringBuilder(); for (int j = blockBegin; j < blockEnd; j++) { char d = line.charAt(j); if (Character.isLetter(d) || d == ',') { sb.append(d); } } if (sb.length() > 0) { blockArgs = sb.toString(); } } } } break; } } } if (hasBlock) { seenBlock = true; } else { seenNonBlock = true; } } if (seenBlock) { if (blockArgs == null) { blockArgs = ""; } if (seenNonBlock) { optionalBlock = true; } } } // How do I name this yield parameter? // yield self if block_given? // RDoc :yield: clauses should be specificed on the line of the starting // element - the def Snapshot snapshot = parserResult.getSnapshot(); int lineStart = GsfUtilities.getRowStart(snapshot.getText(), child.getNode().getPosition().getStartOffset()); int lineEnd = GsfUtilities.getRowEnd(snapshot.getText(), lineStart); String line = snapshot.getText().subSequence(lineStart, lineEnd).toString(); int rdocYieldIdx = line.indexOf(":yield:"); if (rdocYieldIdx == -1) { rdocYieldIdx = line.indexOf(":yields:"); if (rdocYieldIdx != -1) { rdocYieldIdx += ":yields:".length(); } } else { rdocYieldIdx += ":yield:".length(); } if (rdocYieldIdx != -1) { // TODO : strip off "+"'s around it StringBuilder sb = new StringBuilder(); for (int i = rdocYieldIdx; i < line.length(); i++) { char c = line.charAt(i); if (c == '+' || Character.isWhitespace(c)) { continue; } sb.append(c); } blockArgs = sb.toString(); } // Is this element one we don't want to document? If so, // we probably don't want to see it either! Exclude from // code completion. int nodocIndex = line.indexOf(":nodoc:"); if (nodocIndex != -1) { flags |= IndexedElement.NODOC; } int docIndex = line.indexOf(":doc:"); if (docIndex != -1) { flags &= ~IndexedElement.NODOC; } String name = child.getName(); // A few more things can imply that we have a block: an "&" in the // method argument list, as well as methods named "each" or "each_". if (blockArgs == null) { if ("each".equals(name)) { // NOI18N blockArgs = ""; // unknown name } else if (name.startsWith("each_")) { // NOI18N blockArgs = name.substring("each_".length()); // NOI18N } else if (child instanceof MethodElement) { MethodElement me = (MethodElement)child; // Check arg list List<String> params = me.getParameters(); int paramCount = params != null ? params.size() : 0; if (paramCount > 0 && params.get(paramCount-1).startsWith("&")) { // NOI18N blockArgs = ""; // block name unknown // However, make sure that the block variable is actually // used -- Test::Unit::assert(context=nil,&condition) for example // wasn't! // Also, I really need to know whether to use do/end or { } } } } // TODO: Add known block methods: RSpec describe, it, ... if (optionalBlock) { flags |= IndexedMethod.BLOCK; flags |= IndexedMethod.BLOCK_OPTIONAL; } else if (blockArgs != null) { flags |= IndexedMethod.BLOCK; } // Replace attributes int attributeIndex = signature.indexOf(';'); if (flags != originalFlags || attributeIndex == -1) { char first = IndexedElement.flagToFirstChar(flags); char second = IndexedElement.flagToSecondChar(flags); if (attributeIndex == -1) { signature = ((signature+ ";") + first) + second; } else { signature = (signature.substring(0, attributeIndex+1) + first) + second + signature.substring(attributeIndex+3); } } if (blockArgs == null) { blockArgs = ""; } if (child.getNode() instanceof MethodDefNode) { MethodDefNode method = (MethodDefNode) child.getNode(); //hashNames = getAttribute(file, fo, root, method); hashNames = getAttribute(fo, root, method); if (hashNames == null && rdocs != null && child instanceof MethodElement) { MethodElement me = (MethodElement) child; boolean match = false; // try first params with the splat operator for (String param : me.getParameters()) { if (param.startsWith("*")) { hashNames = HashNameAnalyzer.collect(param.substring(1), rdocs); match = true; break; } } // attempt to collect options from rdocs if if one the params is 'options' if (!match && me.getParameters().contains("options")) { hashNames = HashNameAnalyzer.collect("options", rdocs); match = true; } } } if (hashNames == null) { hashNames = ""; } else { // Parse to make sure we don't have problems later... int offset = 0; while (true) { int paren = hashNames.indexOf('(', offset); if (paren != -1) { paren = hashNames.indexOf(')',paren); assert paren != -1; offset = paren; } else { break; } } } RubyType returnType = child.getType(); // See RubyIndexer for a description of the signature format if (blockArgs.length() > 0 || returnType.isKnown() || hashNames.length() > 0) { return signature + ";" + blockArgs + ";" + returnType.asIndexedString() + ";" + hashNames; // NOI18N } else { return signature; } } catch (BadLocationException ble) { // do nothing - see #154991 } return signature; } public static final int DEFAULT_DOC = 0; public static final int NODOC = 1; public static final int DOC = 2; public static final int NODOC_ALL = 3; /** Should the class be removed? Return one of the _DOC constants above */ public static int isNodocClass(AstElement child, Snapshot snapshot) { if (snapshot == null) { // tests? return DEFAULT_DOC; } try { int start = child.getNode().getPosition().getStartOffset(); if (start > snapshot.getText().length()) { return DEFAULT_DOC; } int lineStart = GsfUtilities.getRowStart(snapshot.getText(), start); int lineEnd = GsfUtilities.getRowEnd(snapshot.getText(), lineStart); String line = snapshot.getText().subSequence(lineStart, lineEnd).toString(); // Is this element one we don't want to document? If so, // we probably don't want to see it either! Exclude from // code completion. int nodocIndex = line.indexOf(":nodoc:"); // NOI18N if (nodocIndex != -1) { if (line.indexOf("all", nodocIndex) != -1) { // NOI18N return NODOC_ALL; } else { return NODOC; } } else { int docIndex = line.indexOf(":doc:"); // NOI18N if (docIndex != -1) { return DOC; } } } catch (BadLocationException ble) { // do nothing - see #154991 } return DEFAULT_DOC; } /** See if the given string ends with the given ending, IGNORING ALL * SPACES IN THE INPUT STRING. E.g. " foo b ar " is considered ending * with "foobar". "=" and "-" are considered equivalent * (since this method is used for callseqs - => and -> are used * inconsistently. * * @param s The string to be checked * @param ending The ending we're looking for * @return True iff s ends with "ending" regardless of spaces */ private static boolean endsWithIgnSpace(String s, String ending) { int si = s.length()-1; for (int ei = ending.length()-1; ei >= 0; ei--, si--) { while (si >= 0 && Character.isWhitespace(s.charAt(si))) { si--; } if (si < 0) { return false; } char a = Character.toLowerCase(s.charAt(si)); char b = Character.toLowerCase(ending.charAt(ei)); if (a != b) { // Consider = and - to be equivalent if ((a == '=' ) || (a == '-') && (b == '=' || (b == '-'))) { continue; } return false; } } return true; } private static List<String> getCallSeq(AstElement child, Snapshot snapshot) { List<String> callseq = null; List<String> comments = AstUtilities.gatherDocumentation(snapshot, child.getNode()); if (comments == null) { return null; } for (int i = 0; i < comments.size(); i++) { String line = comments.get(i); if (!line.startsWith("# ") || line.substring(1).trim().length() == 0) { // This is not a callseq if (i > 0) { callseq = new ArrayList<String>(i); for (int j = 0; j < i; j++) { callseq.add(comments.get(j)); } } break; } } return callseq; } private static boolean isRubyStubs(FileObject fo) { return fo.getParent() != null && fo.getParent().getParent() != null && RubyPlatform.RUBYSTUBS.equals(fo.getParent().getParent().getName()); } static String replaceAttributes(String signature, int flags) { int attributeIndex = signature.indexOf(';'); if (attributeIndex == -1) { char first = IndexedElement.flagToFirstChar(flags); char second = IndexedElement.flagToSecondChar(flags); if (attributeIndex == -1) { signature = ((signature + ";") + first) + second; } else { signature = (signature.substring(0, attributeIndex + 1) + first) + second + signature.substring(attributeIndex + 3); } } return signature; } static String getMethodSignatureForUserSources( AstElement methodElement, String signature, int flags, ContextKnowledge knowledge) { RubyType type = methodElement.getType(); if (type.isKnown()) { return signature + ";;" + type.asIndexedString() + ";"; // NOI18N } return signature; } // BEGIN AUTOMATICALLY GENERATED CODE. SEE THE http://hg.netbeans.org/main/misc/ruby/indexhelper PROJECT FOR DETAILS. public static final String HASH_KEY_BOOL = "bool"; // NOI18N public static final String HASH_KEY_STRING = "string"; // NOI18N public static final String HASH_KEY_INTEGER = "string"; // NOI18N private static String clz(Node root, Node n) { AstPath path = new AstPath(root, n); String clz = AstUtilities.getFqnName(path); return clz; } private static String sig(MethodDefNode method) { return AstUtilities.getDefSignature(method); } private static String getAttribute(FileObject file, Node root, MethodDefNode method) { String n = file.getName(); if (n.length() < 2) { return null; } char c = n.charAt(0); switch (c) { case 'a': if ("active_record_helper".equals(n)) { // NOI18N String clz = clz(root,method); if ("ActionView::Helpers::ActiveRecordHelper".equals(clz)) { // NOI18N String sig = sig(method); if (sig.startsWith("form(")) { // NOI18N return "options(action:action)"; // NOI18N } if (sig.startsWith("error_messages_for(")) { // NOI18N return "params(=>header_tag|id|class|object_name)"; // NOI18N } return null; } return null; } if ("aggregations".equals(n)) { // NOI18N String clz = clz(root,method); if ("ActiveRecord::Aggregations::ClassMethods".equals(clz)) { // NOI18N String sig = sig(method); if (sig.startsWith("composed_of(")) { // NOI18N return "options(=>class_name|mapping|allow_nil:bool)"; // NOI18N } return null; } return null; } if ("asset_tag_helper".equals(n)) { // NOI18N String clz = clz(root,method); if ("ActionView::Helpers::AssetTagHelper".equals(clz)) { // NOI18N String sig = sig(method); if (sig.startsWith("auto_discovery_link_tag(")) { // NOI18N return "type(:rss|:atom),tag_options(=>rel|type|title),url_options(=>anchor|only_path:bool|controller:controller|action:action|trailing_slash:bool|host|protocol)"; // NOI18N } if (sig.startsWith("image_tag(")) { // NOI18N return "options(=>alt|size)"; // NOI18N } return null; } return null; } if ("associations".equals(n)) { // NOI18N String clz = clz(root,method); if ("ActiveRecord::Associations::ClassMethods".equals(clz)) { // NOI18N String sig = sig(method); if (sig.startsWith("has_many(")) { // NOI18N return "options(=>class_name|conditions|order|group|foreign_key|dependent|exclusively_dependent|finder_sql|counter_sql|extend|include|limit|offset|select|as|through|source|source_type|uniq),association_id(-table)"; // NOI18N } if (sig.startsWith("has_one(")) { // NOI18N return "options(=>class_name|conditions|order|dependent|foreign_key|include|as)),association_id(-model)"; // NOI18N } if (sig.startsWith("belongs_to(")) { // NOI18N return "options(=>class_name|conditions|foreign_key|counter_cache|include|polymorphic)),association_id(-model)"; // NOI18N } if (sig.startsWith("has_and_belongs_to_many(")) { // NOI18N return "options(=>class_name|join_table|foreign_key|association_foreign_key|conditions|order|uniq:bool|finder_sql|delete_sql|insert_sql|extend|include|group|limit|offset|select)),association_id(-table)"; // NOI18N } return null; } return null; } break; case 'b': if ("base".equals(n)) { // NOI18N String clz = clz(root,method); if (RubyIndex.ACTIVE_RECORD_BASE.equals(clz)) { // NOI18N String sig = sig(method); if (sig.startsWith("find(")) { // NOI18N return "args(:first|:all),args(=>conditions|order|group|limit|offset|joins|readonly:bool|include|select|from|readonly:bool|lock:bool)"; // NOI18N } return null; } if ("ActionController::Base".equals(clz)) { // NOI18N String sig = sig(method); if (sig.startsWith("url_for(")) { // NOI18N return "options(=>anchor|only_path:bool|controller:controller|action:action|trailing_slash:bool|host|protocol)"; // NOI18N } if (sig.startsWith("redirect_to(")) { // NOI18N return "options(=>anchor|only_path:bool|controller:controller|action:action|trailing_slash:bool|host|protocol),options(:back|\"http://)"; // NOI18N } if (sig.startsWith("render(")) { // NOI18N return "options(=>action:action|partial:partial|status:status|template|file:file|text:string|json|inline|nothing)"; // NOI18N } if (sig.startsWith("render_to_string(")) { // NOI18N return "options(=>action:action|partial:partial|status:status|template|file:file|text:string|json|inline|nothing)"; // NOI18N } return null; } return null; } if ("benchmark_helper".equals(n)) { // NOI18N String clz = clz(root,method); if ("ActionView::Helpers::BenchmarkHelper".equals(clz)) { // NOI18N String sig = sig(method); if (sig.startsWith("benchmark(")) { // NOI18N return "level(:debug|:info|:warn|:error)"; // NOI18N } return null; } return null; } break; case 'c': if ("calculations".equals(n)) { // NOI18N String clz = clz(root,method); if ("ActiveRecord::Calculations::ClassMethods".equals(clz)) { // NOI18N String sig = sig(method); if (sig.startsWith("calculate(")) { // NOI18N return "options(=>conditions|joins|order|group|select|distinct:bool),operation(:count|:avg|:min|:max|:sum),column_name(-column)"; // NOI18N } if (sig.startsWith("count(")) { // NOI18N return "args(=>conditions|joins|include|order|group|select|distinct:bool)"; // NOI18N } if (sig.startsWith("minimum(")) { // NOI18N return "options(=>conditions|joins|order|group|select|distinct:bool),column_name(-column)"; // NOI18N } if (sig.startsWith("average(")) { // NOI18N return "options(=>conditions|joins|order|group|select|distinct:bool),column_name(-column)"; // NOI18N } if (sig.startsWith("sum(")) { // NOI18N return "options(=>conditions|joins|order|group|select|distinct:bool),column_name(-column)"; // NOI18N } if (sig.startsWith("maximum(")) { // NOI18N return "options(=>conditions|joins|order|group|select|distinct:bool),column_name(-column)"; // NOI18N } return null; } return null; } if ("cgi_process".equals(n)) { // NOI18N String clz = clz(root,method); if ("ActionController::Base".equals(clz)) { // NOI18N String sig = sig(method); if (sig.startsWith("process_cgi(")) { // NOI18N return "session_options(=>database_manager|session_key|session_id|new_session|session_expires|session_domain|session_secure|session_path)"; // NOI18N } return null; } return null; } break; case 'd': if ("date_helper".equals(n)) { // NOI18N String clz = clz(root,method); if ("ActionView::Helpers::DateHelper".equals(clz)) { // NOI18N String sig = sig(method); if (sig.startsWith("date_select(")) { // NOI18N return "options(=>discard_year:bool|discard_month:bool|discard_day:bool|order|disabled:bool)"; // NOI18N } if (sig.startsWith("time_select(")) { // NOI18N return "options(=>include_seconds:bool)"; // NOI18N } return null; } return null; } break; case 'f': if ("form_helper".equals(n)) { // NOI18N String clz = clz(root,method); if ("ActionView::Helpers::FormHelper".equals(clz)) { // NOI18N String sig = sig(method); if (sig.startsWith("form_for(")) { // NOI18N String path = file.getPath(); if (path.indexOf("-2") != -1 || path.indexOf("-1") == -1) { // NOI18N return "record_or_name_or_array(-model),(rgs=>url:hash|html:hash|builder)"; // NOI18N // NOI18N } else { return "object_name(-model),args(=>url:hash|html:hash|builder)"; // NOI18N // NOI18N } } if (sig.startsWith("fields_for(")) { // NOI18N String path = file.getPath(); if (path.indexOf("-2") != -1 || path.indexOf("-1") == -1) { // NOI18N return "record_or_name_or_array(-model),args(=>url:hash)"; // NOI18N // NOI18N } else { return "object_name(-model),args(=>url:hash)"; // NOI18N // NOI18N } } if (sig.startsWith("text_field(")) { // NOI18N String path = file.getPath(); if (path.indexOf("-2") != -1 || path.indexOf("-1") == -1) { // NOI18N return "record_or_name_or_array(-model)"; // NOI18N // NOI18N } else { return "object_name(-model)"; // NOI18N // NOI18N } } if (sig.startsWith("password_field(")) { // NOI18N String path = file.getPath(); if (path.indexOf("-2") != -1 || path.indexOf("-1") == -1) { // NOI18N return "record_or_name_or_array(-model)"; // NOI18N // NOI18N } else { return "object_name(-model)"; // NOI18N // NOI18N } } if (sig.startsWith("hidden_field(")) { // NOI18N String path = file.getPath(); if (path.indexOf("-2") != -1 || path.indexOf("-1") == -1) { // NOI18N return "record_or_name_or_array(-model)"; // NOI18N // NOI18N } else { return "object_name(-model)"; // NOI18N // NOI18N } } if (sig.startsWith("file_field(")) { // NOI18N String path = file.getPath(); if (path.indexOf("-2") != -1 || path.indexOf("-1") == -1) { // NOI18N return "record_or_name_or_array(-model)"; // NOI18N // NOI18N } else { return "object_name(-model)"; // NOI18N // NOI18N } } if (sig.startsWith("text_area(")) { // NOI18N String path = file.getPath(); if (path.indexOf("-2") != -1 || path.indexOf("-1") == -1) { // NOI18N return "record_or_name_or_array(-model)"; // NOI18N // NOI18N } else { return "object_name(-model)"; // NOI18N // NOI18N } } if (sig.startsWith("check_box(")) { // NOI18N String path = file.getPath(); if (path.indexOf("-2") != -1 || path.indexOf("-1") == -1) { // NOI18N return "record_or_name_or_array(-model)"; // NOI18N // NOI18N } else { return "object_name(-model)"; // NOI18N // NOI18N } } if (sig.startsWith("radio_button(")) { // NOI18N String path = file.getPath(); if (path.indexOf("-2") != -1 || path.indexOf("-1") == -1) { // NOI18N return "record_or_name_or_array(-model)"; // NOI18N // NOI18N } else { return "object_name(-model)"; // NOI18N // NOI18N } } return null; } return null; } if ("form_tag_helper".equals(n)) { // NOI18N String clz = clz(root,method); if ("ActionView::Helpers::FormTagHelper".equals(clz)) { // NOI18N String sig = sig(method); if (sig.startsWith("form_tag(")) { // NOI18N return "options(=>anchor|only_path:bool|controller:controller|action:action|trailing_slash:bool|host|protocol)"; // NOI18N } if (sig.startsWith("select_tag(")) { // NOI18N return "options(=>multiple:bool)"; // NOI18N } if (sig.startsWith("text_area_tag(")) { // NOI18N return "options(=>size)"; // NOI18N } if (sig.startsWith("text_field_tag(")) { // NOI18N return "options(=>disabled:bool|size|maxlength)"; // NOI18N } if (sig.startsWith("password_field_tag(")) { // NOI18N return "options(=>disabled:bool|size|maxlength)"; // NOI18N } if (sig.startsWith("hidden_field_tag(")) { // NOI18N return "options(=>disabled:bool|size|maxlength)"; // NOI18N } return null; } return null; } break; case 'k': if ("kernel".equals(n)) { // NOI18N String clz = clz(root,method); if ("Kernel".equals(clz)) { // NOI18N String sig = sig(method); if (sig.startsWith("describe(")) { // NOI18N return "args(=>behaviour_type|shared:bool)"; // NOI18N } return null; } return null; } break; case 'l': if ("list".equals(n)) { // NOI18N String clz = clz(root,method); if ("ActiveRecord::Acts::List::ClassMethods".equals(clz)) { // NOI18N String sig = sig(method); if (sig.startsWith("acts_as_list(")) { // NOI18N return "options(=>column|scope)"; // NOI18N } return null; } return null; } break; case 'n': if ("nested_set".equals(n)) { // NOI18N String clz = clz(root,method); if ("ActiveRecord::Acts::NestedSet::ClassMethods".equals(clz)) { // NOI18N String sig = sig(method); if (sig.startsWith("acts_as_nested_set(")) { // NOI18N return "options(=>parent_column|left_column|right_column|scope)"; // NOI18N } return null; } return null; } if ("number_helper".equals(n)) { // NOI18N String clz = clz(root,method); if ("ActionView::Helpers::NumberHelper".equals(clz)) { // NOI18N String sig = sig(method); if (sig.startsWith("number_to_phone(")) { // NOI18N return "options(=>area_code:bool|delimiter|extension|country_code)"; // NOI18N } if (sig.startsWith("number_to_currency(")) { // NOI18N return "options(=>precision|unit|separator|delimiter)"; // NOI18N } if (sig.startsWith("number_to_percentage(")) { // NOI18N return "options(=>precision|separator)"; // NOI18N } return null; } return null; } break; case 'p': if ("pagination".equals(n)) { // NOI18N String clz = clz(root,method); if ("ActionController::Pagination".equals(clz)) { // NOI18N String sig = sig(method); if (sig.startsWith("paginate(")) { // NOI18N return "options(=>singular_name|class_name|per_page|conditions|order|order_by|joins|join|include|selected|count)"; // NOI18N } return null; } return null; } if ("pagination_helper".equals(n)) { // NOI18N String clz = clz(root,method); if ("ActionView::Helpers::PaginationHelper".equals(clz)) { // NOI18N String sig = sig(method); if (sig.startsWith("pagination_links(")) { // NOI18N return "options(name|window_size|always_show_anchors:bool|link_to_current_page:bool|params),html_options(=>confirm:string|popup:bool|methodclass|id)"; // NOI18N } return null; } return null; } if ("prototype_helper".equals(n)) { // NOI18N String clz = clz(root,method); if ("ActionView::Helpers::PrototypeHelper".equals(clz)) { // NOI18N String sig = sig(method); if (sig.startsWith("observe_field")) { // NOI18N return "options(=>url:hash|function|frequency|update|with|on)"; // NOI18N } if (sig.startsWith("observe_form")) { // NOI18N return "options(=>url:hash|function|frequency|update|with|on)"; // NOI18N } if (sig.startsWith("link_to_remote(")) { // NOI18N return "options(=>url:hash|update)"; // NOI18N } if (sig.startsWith("remote_function(")) { // NOI18N return "options(=>url:hash|update)"; // NOI18N } if (sig.startsWith("submit_to_remote(")) { // NOI18N return "options(=>url:hash|update)"; // NOI18N } if (sig.startsWith("form_remote_tag(")) { // NOI18N return "options(=>url:hash|update)"; // NOI18N } return null; } return null; } break; case 's': if ("scaffolding".equals(n)) { // NOI18N String clz = clz(root,method); if ("ActionController::Scaffolding::ClassMethods".equals(clz)) { // NOI18N String sig = sig(method); if (sig.startsWith("scaffold(")) { // NOI18N return "model_id(-model),options(=>suffix:bool)"; // NOI18N } return null; } return null; } if ("schema_definitions".equals(n)) { // NOI18N String clz = clz(root,method); if ("ActiveRecord::ConnectionAdapters::TableDefinition".equals(clz)) { // NOI18N String sig = sig(method); if (sig.startsWith("column(")) { // NOI18N return "type(:primary_key|:string|:text|:integer|:float|:decimal|:datetime|:timestamp|:time|:date|:binary|:boolean),options(=>limit|default:nil|null:bool|precision|scale)"; // NOI18N } return null; } return null; } if ("schema_statements".equals(n)) { // NOI18N String clz = clz(root,method); if ("ActiveRecord::ConnectionAdapters::SchemaStatements".equals(clz)) { // NOI18N String sig = sig(method); if (sig.startsWith("create_table(")) { // NOI18N return "options(=>id:bool|primary_key:string|options:hash|temporary:bool|force:bool)"; // NOI18N } if (sig.startsWith("add_column(")) { // NOI18N return "table_name(-table),column_name(-column),options(=>limit|default:nil|null:bool|precision|scale),type(:primary_key|:string|:text|:integer|:float|:decimal|:datetime|:timestamp|:time|:date|:binary|:boolean)"; // NOI18N } if (sig.startsWith("change_column(")) { // NOI18N return "table_name(-table),column_name(-column),options(=>limit|default:nil|null:bool|precision|scale),type(:primary_key|:string|:text|:integer|:float|:decimal|:datetime|:timestamp|:time|:date|:binary|:boolean)"; // NOI18N } if (sig.startsWith("rename_table(")) { // NOI18N String path = file.getPath(); if (path.indexOf("-2") != -1 || path.indexOf("-1") == -1) { // NOI18N return "table_name(-table)"; // NOI18N // NOI18N } else { return "name(-table)"; // NOI18N // NOI18N } } if (sig.startsWith("rename_column(")) { // NOI18N return "table_name(-table),column_name(-column)"; // NOI18N } if (sig.startsWith("change_column_default(")) { // NOI18N return "table_name(-table),column_name(-column)"; // NOI18N } if (sig.startsWith("drop_table(")) { // NOI18N String path = file.getPath(); if (path.indexOf("-2") != -1 || path.indexOf("-1") == -1) { // NOI18N return "table_name(-table)"; // NOI18N // NOI18N } else { return "name(-table)"; // NOI18N // NOI18N } } if (sig.startsWith("add_index(")) { // NOI18N return "table_name(-table),column_name(-column)"; // NOI18N } if (sig.startsWith("remove_index(")) { // NOI18N return "table_name(-table)"; // NOI18N } if (sig.startsWith("remove_column(")) { // NOI18N return "table_name(-table),column_name(-column)"; // NOI18N } return null; } return null; } if ("session_management".equals(n)) { // NOI18N String clz = clz(root,method); if ("ActionController::SessionManagement::ClassMethods".equals(clz)) { // NOI18N String sig = sig(method); if (sig.startsWith("session_store=(")) { // NOI18N return "store(:active_record_store|:drb_store|:mem_cache_store|:memory_store)"; // NOI18N } if (sig.startsWith("session(")) { // NOI18N return "args(=>on:bool|off:bool|only|except|database_manager|session_key|session_id|new_session|session_expires|session_domain|session_secure|session_path)"; // NOI18N } return null; } return null; } if ("streaming".equals(n)) { // NOI18N String clz = clz(root,method); if ("ActionController::Streaming".equals(clz)) { // NOI18N String sig = sig(method); if (sig.startsWith("send_file(")) { // NOI18N return "options(=>filename|type|disposition|stream|buffer_size|status:status)"; // NOI18N } if (sig.startsWith("send_data(")) { // NOI18N return "options(=>filename|type|disposition|status:status)"; // NOI18N } return null; } return null; } break; case 't': if ("tree".equals(n)) { // NOI18N String clz = clz(root,method); if ("ActiveRecord::Acts::Tree::ClassMethods".equals(clz)) { // NOI18N String sig = sig(method); if (sig.startsWith("acts_as_tree(")) { // NOI18N return "options(=>foreign_key|order|counter_cache)"; // NOI18N } return null; } return null; } break; case 'u': if ("url_helper".equals(n)) { // NOI18N String clz = clz(root,method); if ("ActionView::Helpers::UrlHelper".equals(clz)) { // NOI18N String sig = sig(method); if (sig.startsWith("url_for(")) { // NOI18N return "options(=>escape:bool|anchor|only_path:bool|controller:controller|action:action|trailing_slash:bool|host|protocol)"; // NOI18N } if (sig.startsWith("link_to(")) { // NOI18N return "options(=>anchor|only_path:bool|controller:controller|action:action|trailing_slash:bool|host|protocol),html_options(=>confirm:string|popup:bool|methodclass|id)"; // NOI18N } if (sig.startsWith("button_to(")) { // NOI18N return "options(=>anchor|only_path:bool|controller:controller|action:action|trailing_slash:bool|host|protocol),html_options(=>confirm:string|popup:bool|method|disabled:boolclass|id)"; // NOI18N } if (sig.startsWith("mail_to(")) { // NOI18N return "html_options(=>encode|replace_at|replace_dot|subject|body|cc|bccclass|id)"; // NOI18N } return null; } return null; } break; case 'v': if ("validations".equals(n)) { // NOI18N String clz = clz(root,method); if ("ActiveRecord::Validations::ClassMethods".equals(clz)) { // NOI18N String sig = sig(method); if (sig.startsWith("validates_each(")) { // NOI18N return "attrs(=>on:validationactive|allow_nil:bool|if)"; // NOI18N } if (sig.startsWith("validates_confirmation_of(")) { // NOI18N return "attr_names(=>on:validationactive|message|if)"; // NOI18N } if (sig.startsWith("validates_acceptance_of(")) { // NOI18N return "attr_names(=>on:validationactive|message|if|accept)"; // NOI18N } if (sig.startsWith("validates_presence_of(")) { // NOI18N return "attr_names(=>on:validationactive|message|if)"; // NOI18N } if (sig.startsWith("validates_length_of(")) { // NOI18N return "attrs(=>minimum|maximum|is|within|in|allow_nil:bool|too_long|too_short|wrong_length|on:validationactive|message|if)"; // NOI18N } if (sig.startsWith("validates_uniqueness_of(")) { // NOI18N return "attr_names(=>message|scope|case_sensitive:bool|allow_nil:bool|if)"; // NOI18N } if (sig.startsWith("validates_format_of(")) { // NOI18N return "attr_names(=>on:validationactive|message|if|with)"; // NOI18N } if (sig.startsWith("validates_inclusion_of(")) { // NOI18N return "attr_names(=>in|message|allow_nil:bool|if)"; // NOI18N } if (sig.startsWith("validates_exclusion_of(")) { // NOI18N return "attr_names(=>in|message|allow_nil:bool|if)"; // NOI18N } if (sig.startsWith("validates_associated(")) { // NOI18N return "attr_names(=>on:validationactive|if)"; // NOI18N } if (sig.startsWith("validates_numericality_of(")) { // NOI18N return "attr_names(=>on:validationactive|message|if|only_integer:bool|allow_nil:bool)"; // NOI18N } return null; } return null; } if ("verification".equals(n)) { // NOI18N String clz = clz(root,method); if ("ActionController::Verification::ClassMethods".equals(clz)) { // NOI18N String sig = sig(method); if (sig.startsWith("verify(")) { // NOI18N return "options(=>params|session|flash|method|post:submitmethod|xhr:bool|add_flash:hash|add_headers:hash|redirect_to|render|only:bool|except:bool)"; // NOI18N } return null; } return null; } break; } return null; } // END AUTOMATICALLY GENERATED CODE }