├── README ├── autoload ├── Reflection.java ├── java_parser.vim └── javacomplete.vim └── doc └── javacomplete.txt /README: -------------------------------------------------------------------------------- 1 | This is a mirror of http://www.vim.org/scripts/script.php?script_id=1785 2 | 3 | This is javacomplete, an omni-completion script of JAVA language for vim 7. 4 | 5 | It includes javacomplete.vim, java_parser.vim, Reflection.java, and 6 | javacomplete.txt 7 | 8 | Features: 9 | - List members of a class, including (static) fields, (static) methods and ctors. 10 | - List classes or subpackages of a package. 11 | - Provide parameters information of a method, list all overload methods. 12 | - Complete an incomplete word. 13 | - Provide a complete JAVA parser written in Vim script language. 14 | - Use the JVM to obtain most information. 15 | - Use the embedded parser to obtain the class information from source files. 16 | - Tags generated by ctags can also be used. 17 | - JSP is supported, Builtin objects such as request, session can be recognized. 18 | The classes and jar files in the WEB-INF will be appended automatically to classpath. 19 | 20 | 21 | Requirements: 22 | It works on all the platforms where 23 | - Vim version 7.0 and above 24 | - JDK version 1.1 and above 25 | existed 26 | 27 | Input context: 28 | It recognize nearly all kinds of Primary Expressions (see langspec-3.0) 29 | except for "Primary.new Indentifier". Casting conversion is also supported. 30 | Samples of input contexts are as following: ('|' indicates cursor) 31 | (1). after '.', list members of a class or a package 32 | - package.| subpackages and classes of a package 33 | - Type.| static members of the 'Type' class and "class" 34 | - var.| or field.| members of a variable or a field 35 | - method().| members of result of method() 36 | - this.| members of the current class 37 | - ClassName.this.| members of the qualified class 38 | - super.| members of the super class 39 | - array.| members of an array object 40 | - array[i].| array access, return members of the element of array 41 | - "String".| String literal, return members of java.lang.String 42 | - int.| or void.| primitive type or pseudo-type, return "class" 43 | - int[].| array type, return members of a array type and "class" 44 | - java.lang.String[].| 45 | - new int[].| members of the new array instance 46 | - new java.lang.String[i=1][].| 47 | - new Type().| members of the new class instance 48 | - Type.class.| class literal, return members of java.lang.Class 49 | - void.class.| or int.class.| 50 | - ((Type)var).| cast var as Type, return members of Type. 51 | - (var.method()).| same with "var.|" 52 | - (new Class()).| same with "new Class().|" 53 | 54 | (2). after '(', list matching methods with parameters information. 55 | - method(|) methods matched 56 | - var.method(|) methods matched 57 | - new ClassName(|) constructors matched 58 | - this(|) constructors of current class matched 59 | - super(|) constructors of super class matched 60 | Any place between '(' and ')' will be supported soon. 61 | Help information of javadoc is not supported yet. 62 | 63 | (3). after an incomplete word, list all the matched beginning with it. 64 | - var.ab| subset of members of var beginning with `ab` 65 | - ab| list of all maybes 66 | 67 | (4). import statement 68 | - " import java.util.|" 69 | - " import java.ut|" 70 | - " import ja|" 71 | - " import java.lang.Character.|" e.g. "Subset" 72 | - " import static java.lang.Math.|" e.g. "PI, abs" 73 | 74 | (5). package declaration 75 | - " package com.|" 76 | 77 | The above are in simple expression. 78 | (6). after compound expression: 79 | - PrimaryExpr.var.| 80 | - PrimaryExpr.method().| 81 | - PrimaryExpr.method(|) 82 | - PrimaryExpr.var.ab| 83 | e.g. 84 | - "java.lang . System.in .|" 85 | - "java.lang.System.getenv().|" 86 | - "int.class.toString().|" 87 | - "list.toArray().|" 88 | - "new ZipFile(path).|" 89 | - "new ZipFile(path).entries().|" 90 | 91 | (7). Nested expression: 92 | - "System.out.println( str.| )" 93 | - "System.out.println(str.charAt(| )" 94 | - "for (int i = 0; i < str.|; i++)" 95 | - "for ( Object o : a.getCollect| )" 96 | 97 | 98 | Limitations: 99 | The embedded parser works a bit slower than expected. 100 | 101 | TODO: 102 | - Improve performance of the embedded parser. Incremental parser. 103 | - Add quick information using balloonexpr, ballooneval, balloondelay. 104 | - Add javadoc 105 | - Give a hint for class name conflict in different packages. 106 | - Support parameter information for template 107 | - Make it faster and more robust. 108 | 109 | Screenshots: 110 | members of a package, http://blog.chinaunix.net/photo/44758_070917101010.jpg 111 | members of a type, http://blog.chinaunix.net/photo/44758_070917101048.jpg 112 | local variable, http://blog.chinaunix.net/photo/44758_070917101134.jpg 113 | special reference super, http://blog.chinaunix.net/photo/44758_070917101158.jpg 114 | object of method result, http://blog.chinaunix.net/photo/44758_070917101236.jpg 115 | 116 | FeedBack: 117 | Any problem, bug or suggest are welcome to send to fangread@yahoo.com.cn 118 | 119 | BTW, If you want to get more functions on writting java program besides code completion, 120 | you can try VJDE. http://www.vim.org/scripts/script.php?script_id=1213 121 | -------------------------------------------------------------------------------- /autoload/Reflection.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Reflection.java 3 | * 4 | * A utility class for javacomplete mainly for reading class or package information. 5 | * Version: 0.77 6 | * Maintainer: cheng fang 7 | * Last Change: 2007-09-16 8 | * Copyright: Copyright (C) 2007 cheng fang. All rights reserved. 9 | * License: Vim License (see vim's :help license) 10 | * 11 | */ 12 | 13 | import java.lang.reflect.*; 14 | import java.io.*; 15 | import java.util.*; 16 | import java.util.zip.*; 17 | 18 | class Reflection { 19 | static final String VERSION = "0.77"; 20 | 21 | static final int OPTION_FIELD = 1; 22 | static final int OPTION_METHOD = 2; 23 | static final int OPTION_STATIC_FIELD = 4; 24 | static final int OPTION_STATIC_METHOD = 8; 25 | static final int OPTION_CONSTRUCTOR = 16; 26 | static final int OPTION_STATIC = 12; // compound static 27 | static final int OPTION_INSTANCE = 15; // compound instance 28 | static final int OPTION_ALL = 31; // compound all 29 | static final int OPTION_SUPER = 32; 30 | static final int OPTION_SAME_PACKAGE = 64; 31 | 32 | static final int STRATEGY_ALPHABETIC = 128; 33 | static final int STRATEGY_HIERARCHY = 256; 34 | static final int STRATEGY_DEFAULT = 512; 35 | 36 | static final int RETURN_ALL_PACKAGE_INFO = 0x1000; 37 | 38 | static final String KEY_NAME = "'n':"; // "'name':"; 39 | static final String KEY_TYPE = "'t':"; // "'type':"; 40 | static final String KEY_MODIFIER = "'m':"; // "'modifier':"; 41 | static final String KEY_PARAMETERTYPES = "'p':"; // "'parameterTypes':"; 42 | static final String KEY_RETURNTYPE = "'r':"; // "'returnType':"; 43 | static final String KEY_DESCRIPTION = "'d':"; // "'description':"; 44 | static final String KEY_DECLARING_CLASS = "'c':"; // "'declaringclass':"; 45 | 46 | static final String NEWLINE = ""; // "\r\n" 47 | 48 | static boolean debug_mode = false; 49 | 50 | static Hashtable htClasspath = new Hashtable(); 51 | 52 | public static boolean existed(String fqn) { 53 | boolean result = false; 54 | try { 55 | Class.forName(fqn); 56 | result = true; 57 | } 58 | catch (Exception ex) { 59 | } 60 | return result; 61 | } 62 | 63 | public static String existedAndRead(String fqns) { 64 | Hashtable mapPackages = new Hashtable(); // qualified name --> StringBuffer 65 | Hashtable mapClasses = new Hashtable(); // qualified name --> StringBuffer 66 | 67 | for (StringTokenizer st = new StringTokenizer(fqns, ","); st.hasMoreTokens(); ) { 68 | String fqn = st.nextToken(); 69 | try { 70 | Class clazz = Class.forName(fqn); 71 | putClassInfo(mapClasses, clazz); 72 | } 73 | catch (Exception ex) { 74 | String binaryName = fqn; 75 | boolean found = false; 76 | while (true) { 77 | try { 78 | int lastDotPos = binaryName.lastIndexOf('.'); 79 | if (lastDotPos == -1) 80 | break; 81 | binaryName = binaryName.substring(0, lastDotPos) + '$' + binaryName.substring(lastDotPos+1, binaryName.length()); 82 | Class clazz = Class.forName(binaryName); 83 | putClassInfo(mapClasses, clazz); 84 | found = true; 85 | break; 86 | } 87 | catch (Exception e) { 88 | } 89 | } 90 | if (!found) 91 | putPackageInfo(mapPackages, fqn); 92 | } 93 | } 94 | 95 | if (mapPackages.size() > 0 || mapClasses.size() > 0) { 96 | StringBuffer sb = new StringBuffer(4096); 97 | sb.append("{"); 98 | for (Enumeration e = mapPackages.keys(); e.hasMoreElements(); ) { 99 | String s = (String)e.nextElement(); 100 | sb.append("'").append( s.replace('$', '.') ).append("':").append(mapPackages.get(s)).append(","); 101 | } 102 | for (Enumeration e = mapClasses.keys(); e.hasMoreElements(); ) { 103 | String s = (String)e.nextElement(); 104 | sb.append("'").append( s.replace('$', '.') ).append("':").append(mapClasses.get(s)).append(","); 105 | } 106 | sb.append("}"); 107 | return sb.toString(); 108 | } 109 | else 110 | return ""; 111 | } 112 | 113 | private static String getPackageList(String fqn) { 114 | Hashtable mapPackages = new Hashtable(); 115 | putPackageInfo(mapPackages, fqn); 116 | return mapPackages.size() > 0 ? mapPackages.get(fqn).toString() : ""; 117 | } 118 | 119 | private static Hashtable collectClassPath() { 120 | if (!htClasspath.isEmpty()) 121 | return htClasspath; 122 | 123 | // runtime classes 124 | if ("Kaffe".equals(System.getProperty("java.vm.name"))) { 125 | addClasspathesFromDir(System.getProperty("java.home") + File.separator + "share" + File.separator + "kaffe" + File.separator); 126 | } 127 | else if ("GNU libgcj".equals(System.getProperty("java.vm.name"))) { 128 | if (new File(System.getProperty("sun.boot.class.path")).exists()) 129 | htClasspath.put(System.getProperty("sun.boot.class.path"), ""); 130 | } 131 | 132 | if (System.getProperty("java.vendor").toLowerCase(Locale.US).indexOf("microsoft") >= 0) { 133 | // `*.ZIP` files in `Packages` directory 134 | addClasspathesFromDir(System.getProperty("java.home") + File.separator + "Packages" + File.separator); 135 | } 136 | else { 137 | // the following code works for several kinds of JDK 138 | // - JDK1.1: classes.zip 139 | // - JDK1.2+: rt.jar 140 | // - JDK1.4+ of Sun and Apple: rt.jar + jce.jar + jsse.jar 141 | // - JDK1.4 of IBM split rt.jar into core.jar, graphics.jar, server.jar 142 | // combined jce.jar and jsse.jar into security.jar 143 | // - JDK for MacOS X split rt.jar into classes.jar, ui.jar in Classes directory 144 | addClasspathesFromDir(System.getProperty("java.home") + File.separator + "lib" + File.separator); 145 | addClasspathesFromDir(System.getProperty("java.home") + File.separator + "jre" + File.separator + "lib" + File.separator); 146 | addClasspathesFromDir(System.getProperty("java.home") + File.separator + ".." + File.separator + "Classes" + File.separator); 147 | } 148 | 149 | // ext 150 | String extdirs = System.getProperty("java.ext.dirs"); 151 | for (StringTokenizer st = new StringTokenizer(extdirs, File.pathSeparator); st.hasMoreTokens(); ) { 152 | addClasspathesFromDir(st.nextToken() + File.separator); 153 | } 154 | 155 | // user classpath 156 | String classPath = System.getProperty("java.class.path"); 157 | StringTokenizer st = new StringTokenizer(classPath, File.pathSeparator); 158 | while (st.hasMoreTokens()) { 159 | String path = st.nextToken(); 160 | File f = new File(path); 161 | if (!f.exists()) 162 | continue; 163 | 164 | if (path.endsWith(".jar") || path.endsWith(".zip")) 165 | htClasspath.put(f.toString(), ""); 166 | else { 167 | if (f.isDirectory()) 168 | htClasspath.put(f.toString(), ""); 169 | } 170 | } 171 | 172 | return htClasspath; 173 | } 174 | 175 | private static void addClasspathesFromDir(String dirpath) { 176 | File dir = new File(dirpath); 177 | if (dir.isDirectory()) { 178 | String[] items = dir.list(); // use list() instead of listFiles() since the latter are introduced in 1.2 179 | for (int i = 0; i < items.length; i++) { 180 | File f = new File(dirpath + items[i]); 181 | if (!f.exists()) 182 | continue; 183 | 184 | if (items[i].endsWith(".jar") || items[i].endsWith(".zip") || items[i].endsWith(".ZIP")) { 185 | htClasspath.put(f.toString(), ""); 186 | } 187 | else if (items.equals("classes")) { 188 | if (f.isDirectory()) 189 | htClasspath.put(f.toString(), ""); 190 | } 191 | } 192 | } 193 | } 194 | 195 | 196 | /** 197 | * If name is empty, put all loadable package info into map once. 198 | */ 199 | private static void putPackageInfo(Hashtable map, String name) { 200 | String prefix = name.replace('.', '/') + "/"; 201 | Hashtable subpackages = new Hashtable(); 202 | Hashtable classes = new Hashtable(); 203 | for (Enumeration e = collectClassPath().keys(); e.hasMoreElements(); ) { 204 | String path = (String)e.nextElement(); 205 | if (path.endsWith(".jar") || path.endsWith(".zip")) 206 | appendListFromJar(subpackages, classes, path, prefix); 207 | else 208 | appendListFromFolder(subpackages, classes, path, prefix); 209 | } 210 | 211 | if (subpackages.size() > 0 || classes.size() > 0) { 212 | StringBuffer sb = new StringBuffer(1024); 213 | sb.append("{'tag':'PACKAGE','subpackages':["); 214 | for (Enumeration e = subpackages.keys(); e.hasMoreElements(); ) { 215 | sb.append("'").append(e.nextElement()).append("',"); 216 | } 217 | sb.append("],'classes':["); 218 | for (Enumeration e = classes.keys(); e.hasMoreElements(); ) { 219 | sb.append("'").append(e.nextElement()).append("',"); 220 | } 221 | sb.append("]}"); 222 | map.put(name, sb.toString()); 223 | } 224 | } 225 | 226 | public static void appendListFromJar(Hashtable subpackages, Hashtable classes, String path, String prefix) { 227 | try { 228 | for (Enumeration entries = new ZipFile(path).entries(); entries.hasMoreElements(); ) { 229 | String entry = entries.nextElement().toString(); 230 | int len = entry.length(); 231 | if (entry.endsWith(".class") && entry.indexOf('$') == -1 232 | && entry.startsWith(prefix)) { 233 | int splitPos = entry.indexOf('/', prefix.length()); 234 | String shortname = entry.substring(prefix.length(), splitPos == -1 ? entry.length()-6 : splitPos); 235 | if (splitPos == -1) { 236 | if (!classes.containsKey(shortname)) 237 | classes.put(shortname, ""); //classes.put(shortname, "{'tag':'CLASSDEF','name':'"+shortname+"'}"); 238 | } 239 | else { 240 | if (!subpackages.containsKey(shortname)) 241 | subpackages.put(shortname, ""); //subpackages.put(shortname, "{'tag':'PACKAGE','name':'" +shortname+"'}"); 242 | } 243 | } 244 | } 245 | } 246 | catch (Throwable e) { 247 | //e.printStackTrace(); 248 | } 249 | } 250 | 251 | public static void appendListFromFolder(Hashtable subpackages, Hashtable classes, String path, String prefix) { 252 | try { 253 | String fullPath = path + "/" + prefix; 254 | File file = new File(fullPath); 255 | if (file.isDirectory()) { 256 | String[] descents = file.list(); 257 | for (int i = 0; i < descents.length; i++) { 258 | if (descents[i].indexOf('$') == -1) { 259 | if (descents[i].endsWith(".class")) { 260 | String shortname = descents[i].substring(0, descents[i].length()-6); 261 | if (!classes.containsKey(shortname)) 262 | classes.put(shortname, ""); 263 | } 264 | else if ((new File(fullPath + "/" + descents[i])).isDirectory()) { 265 | if (!subpackages.containsKey(descents[i])) 266 | subpackages.put(descents[i], ""); 267 | } 268 | } 269 | } 270 | } 271 | } 272 | catch (Throwable e) { 273 | } 274 | } 275 | 276 | private static int INDEX_PACKAGE = 0; 277 | private static int INDEX_CLASS = 1; 278 | 279 | // generate information of all packages in jar files. 280 | public static String getPackageList() { 281 | Hashtable map = new Hashtable(); 282 | 283 | for (Enumeration e = collectClassPath().keys(); e.hasMoreElements(); ) { 284 | String path = (String)e.nextElement(); 285 | if (path.endsWith(".jar") || path.endsWith(".zip")) 286 | appendListFromJar(path, map); 287 | } 288 | 289 | StringBuffer sb = new StringBuffer(4096); 290 | sb.append("{"); 291 | //sb.append("'*':'").append( map.remove("") ).append("',"); // default package 292 | for (Enumeration e = map.keys(); e.hasMoreElements(); ) { 293 | String s = (String)e.nextElement(); 294 | StringBuffer[] sbs = (StringBuffer[])map.get(s); 295 | sb.append("'").append( s.replace('/', '.') ).append("':") 296 | .append("{'tag':'PACKAGE'"); 297 | if (sbs[INDEX_PACKAGE].length() > 0) 298 | sb.append(",'subpackages':[").append(sbs[INDEX_PACKAGE]).append("]"); 299 | if (sbs[INDEX_CLASS].length() > 0) 300 | sb.append(",'classes':[").append(sbs[INDEX_CLASS]).append("]"); 301 | sb.append("},"); 302 | } 303 | sb.append("}"); 304 | return sb.toString(); 305 | 306 | } 307 | 308 | public static void appendListFromJar(String path, Hashtable map) { 309 | try { 310 | for (Enumeration entries = new ZipFile(path).entries(); entries.hasMoreElements(); ) { 311 | String entry = entries.nextElement().toString(); 312 | int len = entry.length(); 313 | if (entry.endsWith(".class") && entry.indexOf('$') == -1) { 314 | int slashpos = entry.lastIndexOf('/'); 315 | String parent = entry.substring(0, slashpos); 316 | String child = entry.substring(slashpos+1, len-6); 317 | putItem(map, parent, child, INDEX_CLASS); 318 | 319 | slashpos = parent.lastIndexOf('/'); 320 | if (slashpos != -1) { 321 | AddToParent(map, parent.substring(0, slashpos), parent.substring(slashpos+1)); 322 | } 323 | } 324 | } 325 | } 326 | catch (Throwable e) { 327 | //e.printStackTrace(); 328 | } 329 | } 330 | 331 | public static void putItem(Hashtable map, String parent, String child, int index) { 332 | StringBuffer[] sbs = (StringBuffer[])map.get(parent); 333 | if (sbs == null) { 334 | sbs = new StringBuffer[] { new StringBuffer(256), // packages 335 | new StringBuffer(256) // classes 336 | }; 337 | } 338 | if (sbs[index].toString().indexOf("'" + child + "',") == -1) 339 | sbs[index].append("'").append(child).append("',"); 340 | map.put(parent, sbs); 341 | } 342 | 343 | public static void AddToParent(Hashtable map, String parent, String child) { 344 | putItem(map, parent, child, INDEX_PACKAGE); 345 | 346 | int slashpos = parent.lastIndexOf('/'); 347 | if (slashpos != -1) { 348 | AddToParent(map, parent.substring(0, slashpos), parent.substring(slashpos+1)); 349 | } 350 | } 351 | 352 | 353 | public static String getClassInfo(String className) { 354 | Hashtable mapClasses = new Hashtable(); 355 | try { 356 | Class clazz = Class.forName(className); 357 | putClassInfo(mapClasses, clazz); 358 | } 359 | catch (Exception ex) { 360 | } 361 | 362 | if (mapClasses.size() == 1) { 363 | return mapClasses.get(className).toString(); // return {...} 364 | } 365 | else if (mapClasses.size() > 1) { 366 | StringBuffer sb = new StringBuffer(4096); 367 | sb.append("["); 368 | for (Enumeration e = mapClasses.keys(); e.hasMoreElements(); ) { 369 | String s = (String)e.nextElement(); 370 | sb.append(mapClasses.get(s)).append(","); 371 | } 372 | sb.append("]"); 373 | return sb.toString(); // return [...] 374 | } 375 | else 376 | return ""; 377 | } 378 | 379 | private static void putClassInfo(Hashtable map, Class clazz) { 380 | if (map.containsKey(clazz.getName())) 381 | return ; 382 | 383 | try { 384 | StringBuffer sb = new StringBuffer(1024); 385 | sb.append("{") 386 | .append("'tag':'CLASSDEF',").append(NEWLINE) 387 | .append("'flags':'").append(Integer.toString(clazz.getModifiers(), 2)).append("',").append(NEWLINE) 388 | .append("'name':'").append(clazz.getName().replace('$', '.')).append("',").append(NEWLINE) 389 | //.append("'package':'").append(clazz.getPackage().getName()).append("',").append(NEWLINE) // no getPackage() in JDK1.1 390 | .append("'classpath':'1',").append(NEWLINE) 391 | .append("'fqn':'").append(clazz.getName().replace('$', '.')).append("',").append(NEWLINE); 392 | 393 | Class[] interfaces = clazz.getInterfaces(); 394 | if (clazz.isInterface()) { 395 | sb.append("'extends':["); 396 | } else { 397 | Class superclass = clazz.getSuperclass(); 398 | if (superclass != null && !"java.lang.Object".equals(superclass.getName())) { 399 | sb.append("'extends':['").append(superclass.getName().replace('$', '.')).append("'],").append(NEWLINE); 400 | putClassInfo(map, superclass); // !! 401 | } 402 | sb.append("'implements':["); 403 | } 404 | for (int i = 0, n = interfaces.length; i < n; i++) { 405 | sb.append("'").append(interfaces[i].getName().replace('$', '.')).append("',"); 406 | putClassInfo(map, interfaces[i]); // !! 407 | } 408 | sb.append("],").append(NEWLINE);; 409 | 410 | Constructor[] ctors = clazz.getConstructors(); 411 | sb.append("'ctors':["); 412 | for (int i = 0, n = ctors.length; i < n; i++) { 413 | Constructor ctor = ctors[i]; 414 | sb.append("{"); 415 | appendModifier(sb, ctor.getModifiers()); 416 | appendParameterTypes(sb, ctor.getParameterTypes()); 417 | sb.append(KEY_DESCRIPTION).append("'").append(ctors[i].toString()).append("'"); 418 | sb.append("},").append(NEWLINE); 419 | } 420 | sb.append("], ").append(NEWLINE); 421 | 422 | Field[] fields = clazz.getFields(); 423 | //java.util.Arrays.sort(fields, comparator); 424 | sb.append("'fields':["); 425 | for (int i = 0, n = fields.length; i < n; i++) { 426 | Field f = fields[i]; 427 | int modifier = f.getModifiers(); 428 | sb.append("{"); 429 | sb.append(KEY_NAME).append("'").append(f.getName()).append("',"); 430 | if (!f.getDeclaringClass().getName().equals(clazz.getName())) 431 | sb.append(KEY_DECLARING_CLASS).append("'").append(f.getDeclaringClass().getName()).append("',"); 432 | appendModifier(sb, modifier); 433 | sb.append(KEY_TYPE).append("'").append(f.getType().getName()).append("'"); 434 | sb.append("},").append(NEWLINE); 435 | } 436 | sb.append("], ").append(NEWLINE); 437 | 438 | Method[] methods = clazz.getMethods(); 439 | //java.util.Arrays.sort(methods, comparator); 440 | sb.append("'methods':["); 441 | for (int i = 0, n = methods.length; i < n; i++) { 442 | Method m = methods[i]; 443 | int modifier = m.getModifiers(); 444 | sb.append("{"); 445 | sb.append(KEY_NAME).append("'").append(m.getName()).append("',"); 446 | if (!m.getDeclaringClass().getName().equals(clazz.getName())) 447 | sb.append(KEY_DECLARING_CLASS).append("'").append(m.getDeclaringClass().getName()).append("',"); 448 | appendModifier(sb, modifier); 449 | sb.append(KEY_RETURNTYPE).append("'").append(m.getReturnType().getName()).append("',"); 450 | appendParameterTypes(sb, m.getParameterTypes()); 451 | sb.append(KEY_DESCRIPTION).append("'").append(m.toString()).append("'"); 452 | sb.append("},").append(NEWLINE); 453 | } 454 | sb.append("], ").append(NEWLINE); 455 | 456 | Class[] classes = clazz.getClasses(); 457 | sb.append("'classes': ["); 458 | for (int i = 0, n = classes.length; i < n; i++) { 459 | Class c = classes[i]; 460 | sb.append("'").append(c.getName().replace('$', '.')).append("',"); 461 | putClassInfo(map, c); // !! 462 | } 463 | sb.append("], ").append(NEWLINE); 464 | 465 | appendDeclaredMembers(map, clazz, sb); 466 | 467 | sb.append("}"); 468 | map.put(clazz.getName(), sb); 469 | } 470 | catch (Exception ex) { 471 | //ex.printStackTrace(); 472 | } 473 | } 474 | 475 | private static void appendDeclaredMembers(Hashtable map, Class clazz, StringBuffer sb) { 476 | Constructor[] ctors = clazz.getDeclaredConstructors(); 477 | sb.append("'declared_ctors':["); 478 | for (int i = 0, n = ctors.length; i < n; i++) { 479 | Constructor ctor = ctors[i]; 480 | if (!Modifier.isPublic(ctor.getModifiers())) { 481 | sb.append("{"); 482 | appendModifier(sb, ctor.getModifiers()); 483 | appendParameterTypes(sb, ctor.getParameterTypes()); 484 | sb.append(KEY_DESCRIPTION).append("'").append(ctors[i].toString()).append("'"); 485 | sb.append("},").append(NEWLINE); 486 | } 487 | } 488 | sb.append("], ").append(NEWLINE); 489 | 490 | Field[] fields = clazz.getDeclaredFields(); 491 | sb.append("'declared_fields':["); 492 | for (int i = 0, n = fields.length; i < n; i++) { 493 | Field f = fields[i]; 494 | int modifier = f.getModifiers(); 495 | if (!Modifier.isPublic(modifier)) { 496 | sb.append("{"); 497 | sb.append(KEY_NAME).append("'").append(f.getName()).append("',"); 498 | if (!f.getDeclaringClass().getName().equals(clazz.getName())) 499 | sb.append(KEY_DECLARING_CLASS).append("'").append(f.getDeclaringClass().getName()).append("',"); 500 | appendModifier(sb, modifier); 501 | sb.append(KEY_TYPE).append("'").append(f.getType().getName()).append("'"); 502 | sb.append("},").append(NEWLINE); 503 | } 504 | } 505 | sb.append("], ").append(NEWLINE); 506 | 507 | Method[] methods = clazz.getDeclaredMethods(); 508 | sb.append("'declared_methods':["); 509 | for (int i = 0, n = methods.length; i < n; i++) { 510 | Method m = methods[i]; 511 | int modifier = m.getModifiers(); 512 | if (!Modifier.isPublic(modifier)) { 513 | sb.append("{"); 514 | sb.append(KEY_NAME).append("'").append(m.getName()).append("',"); 515 | if (!m.getDeclaringClass().getName().equals(clazz.getName())) 516 | sb.append(KEY_DECLARING_CLASS).append("'").append(m.getDeclaringClass().getName()).append("',"); 517 | appendModifier(sb, modifier); 518 | sb.append(KEY_RETURNTYPE).append("'").append(m.getReturnType().getName()).append("',"); 519 | appendParameterTypes(sb, m.getParameterTypes()); 520 | sb.append(KEY_DESCRIPTION).append("'").append(m.toString()).append("'"); 521 | sb.append("},").append(NEWLINE); 522 | } 523 | } 524 | sb.append("], ").append(NEWLINE); 525 | 526 | Class[] classes = clazz.getDeclaredClasses(); 527 | sb.append("'declared_classes': ["); 528 | for (int i = 0, n = classes.length; i < n; i++) { 529 | Class c = classes[i]; 530 | if (!Modifier.isPublic(c.getModifiers())) { 531 | sb.append("'").append(c.getName().replace('$', '.')).append("',"); 532 | putClassInfo(map, c); // !! 533 | } 534 | } 535 | sb.append("], ").append(NEWLINE); 536 | } 537 | 538 | private static void appendModifier(StringBuffer sb, int modifier) { 539 | sb.append(KEY_MODIFIER).append("'").append(Integer.toString(modifier, 2)).append("', "); 540 | } 541 | 542 | private static void appendParameterTypes(StringBuffer sb, Class[] paramTypes) { 543 | if (paramTypes.length == 0) return ; 544 | 545 | sb.append(KEY_PARAMETERTYPES).append("["); 546 | for (int j = 0; j < paramTypes.length; j++) { 547 | sb.append("'").append(paramTypes[j].getName()).append("',"); 548 | } 549 | sb.append("],"); 550 | } 551 | 552 | private static boolean isBlank(String str) { 553 | int len; 554 | if (str == null || (len = str.length()) == 0) 555 | return true; 556 | for (int i = 0; i < len; i++) 557 | if ((Character.isWhitespace(str.charAt(i)) == false)) 558 | return false; 559 | return true; 560 | } 561 | 562 | // test methods 563 | 564 | static void debug(String s) { 565 | if (debug_mode) 566 | System.out.println(s); 567 | } 568 | static void output(String s) { 569 | if (!debug_mode) 570 | System.out.print(s); 571 | } 572 | 573 | 574 | private static void usage() { 575 | System.out.println("Reflection for javacomplete (" + VERSION + ")"); 576 | System.out.println(" java [-classpath] Reflection [-c] [-d] [-e] [-h] [-v] [-p] [-s] name[,comma_separated_name_list]"); 577 | System.out.println("Options:"); 578 | System.out.println(" -a list all members in alphabetic order"); 579 | System.out.println(" -c list constructors"); 580 | System.out.println(" -C return class info"); 581 | System.out.println(" -d default strategy, i.e. instance fields, instance methods, static fields, static methods"); 582 | System.out.println(" -e check class existed"); 583 | System.out.println(" -E check class existed and read class information"); 584 | System.out.println(" -D debug mode"); 585 | System.out.println(" -p list package content"); 586 | System.out.println(" -P print all package info in the Vim dictionary format"); 587 | System.out.println(" -s list static fields and methods"); 588 | System.out.println(" -h help"); 589 | System.out.println(" -v version"); 590 | } 591 | 592 | public static void main(String[] args) { 593 | String className = null; 594 | int option = 0x0; 595 | boolean wholeClassInfo = false; 596 | boolean onlyStatic = false; 597 | boolean onlyConstructor = false; 598 | boolean listPackageContent = false; 599 | boolean checkExisted = false; 600 | boolean checkExistedAndRead = false; 601 | boolean allPackageInfo = false; 602 | 603 | for (int i = 0, n = args.length; i < n && !isBlank(args[i]); i++) { 604 | //debug(args[i]); 605 | if (args[i].charAt(0) == '-') { 606 | if (args[i].length() > 1) { 607 | switch (args[i].charAt(1)) { 608 | case 'a': 609 | break; 610 | case 'c': // request constructors 611 | option = option | OPTION_CONSTRUCTOR; 612 | onlyConstructor = true; 613 | break; 614 | case 'C': // class info 615 | wholeClassInfo = true; 616 | break; 617 | case 'd': // default strategy 618 | option = option | STRATEGY_DEFAULT; 619 | break; 620 | case 'D': // debug mode 621 | debug_mode = true; 622 | break; 623 | case 'e': // class existed 624 | checkExisted = true; 625 | break; 626 | case 'E': // check existed and read class information 627 | checkExistedAndRead = true; 628 | break; 629 | case 'h': // help 630 | usage(); 631 | return ; 632 | case 'v': // version 633 | System.out.println("Reflection for javacomplete (" + VERSION + ")"); 634 | break; 635 | case 'p': 636 | listPackageContent = true; 637 | break; 638 | case 'P': 639 | option = RETURN_ALL_PACKAGE_INFO; 640 | break; 641 | case 's': // request static members 642 | option = option | OPTION_STATIC_METHOD | OPTION_STATIC_FIELD; 643 | onlyStatic = true; 644 | break; 645 | default: 646 | } 647 | } 648 | } 649 | else { 650 | className = args[i]; 651 | } 652 | } 653 | if (className == null && (option & RETURN_ALL_PACKAGE_INFO) != RETURN_ALL_PACKAGE_INFO) { 654 | return; 655 | } 656 | if (option == 0x0) 657 | option = OPTION_INSTANCE; 658 | 659 | if (wholeClassInfo) 660 | output( getClassInfo(className) ); 661 | else if ((option & RETURN_ALL_PACKAGE_INFO) == RETURN_ALL_PACKAGE_INFO) 662 | output( getPackageList() ); 663 | else if (checkExistedAndRead) 664 | output( existedAndRead(className) ); 665 | else if (checkExisted) 666 | output( String.valueOf(existed(className)) ); 667 | else if (listPackageContent) 668 | output( getPackageList(className) ); 669 | } 670 | } 671 | -------------------------------------------------------------------------------- /autoload/javacomplete.vim: -------------------------------------------------------------------------------- 1 | " Vim completion script - hit 80% complete tasks 2 | " Version: 0.77.1.2 3 | " Language: Java 4 | " Maintainer: cheng fang 5 | " Last Change: 2011-01-30 6 | " Copyright: Copyright (C) 2006-2007 cheng fang. All rights reserved. 7 | " License: Vim License (see vim's :help license) 8 | 9 | 10 | " constants {{{1 11 | " input context type 12 | let s:CONTEXT_AFTER_DOT = 1 13 | let s:CONTEXT_METHOD_PARAM = 2 14 | let s:CONTEXT_IMPORT = 3 15 | let s:CONTEXT_IMPORT_STATIC = 4 16 | let s:CONTEXT_PACKAGE_DECL = 6 17 | let s:CONTEXT_NEED_TYPE = 7 18 | let s:CONTEXT_OTHER = 0 19 | 20 | 21 | let s:ARRAY_TYPE_MEMBERS = [ 22 | \ {'kind': 'm', 'word': 'clone(', 'abbr': 'clone()', 'menu': 'Object clone()', }, 23 | \ {'kind': 'm', 'word': 'equals(', 'abbr': 'equals()', 'menu': 'boolean equals(Object)', }, 24 | \ {'kind': 'm', 'word': 'getClass(', 'abbr': 'getClass()', 'menu': 'Class Object.getClass()', }, 25 | \ {'kind': 'm', 'word': 'hashCode(', 'abbr': 'hashCode()', 'menu': 'int hashCode()', }, 26 | \ {'kind': 'f', 'word': 'length', 'menu': 'int'}, 27 | \ {'kind': 'm', 'word': 'notify(', 'abbr': 'notify()', 'menu': 'void Object.notify()', }, 28 | \ {'kind': 'm', 'word': 'notifyAll(', 'abbr': 'notifyAll()', 'menu': 'void Object.notifyAll()', }, 29 | \ {'kind': 'm', 'word': 'toString(', 'abbr': 'toString()', 'menu': 'String toString()', }, 30 | \ {'kind': 'm', 'word': 'wait(', 'abbr': 'wait()', 'menu': 'void Object.wait() throws InterruptedException', }, 31 | \ {'kind': 'm', 'dup': 1, 'word': 'wait(', 'abbr': 'wait()', 'menu': 'void Object.wait(long timeout) throws InterruptedException', }, 32 | \ {'kind': 'm', 'dup': 1, 'word': 'wait(', 'abbr': 'wait()', 'menu': 'void Object.wait(long timeout, int nanos) throws InterruptedException', }] 33 | 34 | let s:ARRAY_TYPE_INFO = {'tag': 'CLASSDEF', 'name': '[', 'ctors': [], 35 | \ 'fields': [{'n': 'length', 'm': '1', 't': 'int'}], 36 | \ 'methods':[ 37 | \ {'n': 'clone', 'm': '1', 'r': 'Object', 'p': [], 'd': 'Object clone()'}, 38 | \ {'n': 'equals', 'm': '1', 'r': 'boolean', 'p': ['Object'], 'd': 'boolean Object.equals(Object obj)'}, 39 | \ {'n': 'getClass', 'm': '100010001', 'r': 'Class', 'p': [], 'd': 'Class Object.getClass()'}, 40 | \ {'n': 'hashCode', 'm': '100000001', 'r': 'int', 'p': [], 'd': 'int Object.hashCode()'}, 41 | \ {'n': 'notify', 'm': '100010001', 'r': 'void', 'p': [], 'd': 'void Object.notify()'}, 42 | \ {'n': 'notifyAll','m': '100010001', 'r': 'void', 'p': [], 'd': 'void Object.notifyAll()'}, 43 | \ {'n': 'toString', 'm': '1', 'r': 'String', 'p': [], 'd': 'String Object.toString()'}, 44 | \ {'n': 'wait', 'm': '10001', 'r': 'void', 'p': [], 'd': 'void Object.wait() throws InterruptedException'}, 45 | \ {'n': 'wait', 'm': '100010001', 'r': 'void', 'p': ['long'], 'd': 'void Object.wait(long timeout) throws InterruptedException'}, 46 | \ {'n': 'wait', 'm': '10001', 'r': 'void', 'p': ['long','int'], 'd': 'void Object.wait(long timeout, int nanos) throws InterruptedException'}, 47 | \ ]} 48 | 49 | let s:PRIMITIVE_TYPE_INFO = {'tag': 'CLASSDEF', 'name': '!', 'fields': [{'n': 'class','m': '1','t': 'Class'}]} 50 | 51 | let s:JSP_BUILTIN_OBJECTS = {'session': 'javax.servlet.http.HttpSession', 52 | \ 'request': 'javax.servlet.http.HttpServletRequest', 53 | \ 'response': 'javax.servlet.http.HttpServletResponse', 54 | \ 'pageContext': 'javax.servlet.jsp.PageContext', 55 | \ 'application': 'javax.servlet.ServletContext', 56 | \ 'config': 'javax.servlet.ServletConfig', 57 | \ 'out': 'javax.servlet.jsp.JspWriter', 58 | \ 'page': 'javax.servlet.jsp.HttpJspPage', } 59 | 60 | 61 | let s:PRIMITIVE_TYPES = ['boolean', 'byte', 'char', 'int', 'short', 'long', 'float', 'double'] 62 | let s:KEYWORDS_MODS = ['public', 'private', 'protected', 'static', 'final', 'synchronized', 'volatile', 'transient', 'native', 'strictfp', 'abstract'] 63 | let s:KEYWORDS_TYPE = ['class', 'interface', 'enum'] 64 | let s:KEYWORDS = s:PRIMITIVE_TYPES + s:KEYWORDS_MODS + s:KEYWORDS_TYPE + ['super', 'this', 'void'] + ['assert', 'break', 'case', 'catch', 'const', 'continue', 'default', 'do', 'else', 'extends', 'finally', 'for', 'goto', 'if', 'implements', 'import', 'instanceof', 'interface', 'new', 'package', 'return', 'switch', 'throw', 'throws', 'try', 'while', 'true', 'false', 'null'] 65 | 66 | let s:PATH_SEP = ':' 67 | let s:FILE_SEP = '/' 68 | if has("win32") || has("win64") || has("win16") || has("dos32") || has("dos16") 69 | let s:PATH_SEP = ';' 70 | let s:FILE_SEP = '\' 71 | endif 72 | 73 | let s:RE_BRACKETS = '\%(\s*\[\s*\]\)' 74 | let s:RE_IDENTIFIER = '[a-zA-Z_$][a-zA-Z0-9_$]*' 75 | let s:RE_QUALID = s:RE_IDENTIFIER. '\%(\s*\.\s*' .s:RE_IDENTIFIER. '\)*' 76 | 77 | let s:RE_REFERENCE_TYPE = s:RE_QUALID . s:RE_BRACKETS . '*' 78 | let s:RE_TYPE = s:RE_REFERENCE_TYPE 79 | 80 | let s:RE_TYPE_ARGUMENT = '\%(?\s\+\%(extends\|super\)\s\+\)\=' . s:RE_TYPE 81 | let s:RE_TYPE_ARGUMENTS = '<' . s:RE_TYPE_ARGUMENT . '\%(\s*,\s*' . s:RE_TYPE_ARGUMENT . '\)*>' 82 | let s:RE_TYPE_WITH_ARGUMENTS_I = s:RE_IDENTIFIER . '\s*' . s:RE_TYPE_ARGUMENTS 83 | let s:RE_TYPE_WITH_ARGUMENTS = s:RE_TYPE_WITH_ARGUMENTS_I . '\%(\s*' . s:RE_TYPE_WITH_ARGUMENTS_I . '\)*' 84 | 85 | let s:RE_TYPE_MODS = '\%(public\|protected\|private\|abstract\|static\|final\|strictfp\)' 86 | let s:RE_TYPE_DECL_HEAD = '\(class\|interface\|enum\)[ \t\n\r ]\+' 87 | let s:RE_TYPE_DECL = '\<\C\(\%(' .s:RE_TYPE_MODS. '\s\+\)*\)' .s:RE_TYPE_DECL_HEAD. '\(' .s:RE_IDENTIFIER. '\)[< \t\n\r ]' 88 | 89 | let s:RE_ARRAY_TYPE = '^\s*\(' .s:RE_QUALID . '\)\(' . s:RE_BRACKETS . '\+\)\s*$' 90 | let s:RE_SELECT_OR_ACCESS = '^\s*\(' . s:RE_IDENTIFIER . '\)\s*\(\[.*\]\)\=\s*$' 91 | let s:RE_ARRAY_ACCESS = '^\s*\(' . s:RE_IDENTIFIER . '\)\s*\(\[.*\]\)\+\s*$' 92 | let s:RE_CASTING = '^\s*(\(' .s:RE_QUALID. '\))\s*\(' . s:RE_IDENTIFIER . '\)\>' 93 | 94 | let s:RE_KEYWORDS = '\<\%(' . join(s:KEYWORDS, '\|') . '\)\>' 95 | 96 | 97 | " local variables {{{1 98 | let b:context_type = s:CONTEXT_OTHER 99 | "let b:statement = '' " statement before cursor 100 | let b:dotexpr = '' " expression ends with '.' 101 | let b:incomplete = '' " incomplete word: 1. dotexpr.method(|) 2. new classname(|) 3. dotexpr.ab|, 4. ja|, 5. method(| 102 | let b:errormsg = '' 103 | 104 | " script variables {{{1 105 | let s:cache = {} " FQN -> member list, e.g. {'java.lang.StringBuffer': classinfo, 'java.util': packageinfo, '/dir/TopLevelClass.java': compilationUnit} 106 | let s:files = {} " srouce file path -> properties, e.g. {filekey: {'unit': compilationUnit, 'changedtick': tick, }} 107 | let s:history = {} " 108 | 109 | 110 | " This function is used for the 'omnifunc' option. {{{1 111 | function! javacomplete#Complete(findstart, base) 112 | if a:findstart 113 | let s:et_whole = reltime() 114 | let start = col('.') - 1 115 | let s:log = [] 116 | 117 | " reset enviroment 118 | let b:dotexpr = '' 119 | let b:incomplete = '' 120 | let b:context_type = s:CONTEXT_OTHER 121 | 122 | let statement = s:GetStatement() 123 | call s:WatchVariant('statement: "' . statement . '"') 124 | 125 | if statement =~ '[.0-9A-Za-z_]\s*$' 126 | let valid = 1 127 | if statement =~ '\.\s*$' 128 | let valid = statement =~ '[")0-9A-Za-z_\]]\s*\.\s*$' && statement !~ '\<\H\w\+\.\s*$' && statement !~ '\<\(abstract\|assert\|break\|case\|catch\|const\|continue\|default\|do\|else\|enum\|extends\|final\|finally\|for\|goto\|if\|implements\|import\|instanceof\|interface\|native\|new\|package\|private\|protected\|public\|return\|static\|strictfp\|switch\|synchronized\|throw\|throws\|transient\|try\|volatile\|while\|true\|false\|null\)\.\s*$' 129 | endif 130 | if !valid 131 | return -1 132 | endif 133 | 134 | let b:context_type = s:CONTEXT_AFTER_DOT 135 | 136 | " import or package declaration 137 | if statement =~# '^\s*\(import\|package\)\s\+' 138 | let statement = substitute(statement, '\s\+\.', '.', 'g') 139 | let statement = substitute(statement, '\.\s\+', '.', 'g') 140 | if statement =~ '^\s*import\s\+' 141 | let b:context_type = statement =~# '\ 0 263 | " filter according to b:incomplete 264 | if len(b:incomplete) > 0 && b:incomplete != '+' 265 | let result = filter(result, "type(v:val) == type('') ? v:val =~ '^" . b:incomplete . "' : v:val['word'] =~ '^" . b:incomplete . "'") 266 | endif 267 | 268 | if exists('s:padding') && !empty(s:padding) 269 | for item in result 270 | if type(item) == type("") 271 | let item .= s:padding 272 | else 273 | let item.word .= s:padding 274 | endif 275 | endfor 276 | unlet s:padding 277 | endif 278 | 279 | call s:Debug('finish completion' . reltimestr(reltime(s:et_whole)) . 's') 280 | return result 281 | endif 282 | 283 | if strlen(b:errormsg) > 0 284 | echoerr 'javacomplete error: ' . b:errormsg 285 | let b:errormsg = '' 286 | endif 287 | endfunction 288 | 289 | " Precondition: incomplete must be a word without '.'. 290 | " return all the matched, variables, fields, methods, types, packages 291 | fu! s:CompleteAfterWord(incomplete) 292 | " packages in jar files 293 | if !exists('s:all_packages_in_jars_loaded') 294 | call s:DoGetInfoByReflection('-', '-P') 295 | let s:all_packages_in_jars_loaded = 1 296 | endif 297 | 298 | let pkgs = [] 299 | let types = [] 300 | for key in keys(s:cache) 301 | if key =~# '^' . a:incomplete 302 | if type(s:cache[key]) == type('') || get(s:cache[key], 'tag', '') == 'PACKAGE' 303 | call add(pkgs, {'kind': 'P', 'word': key}) 304 | 305 | " filter out type info 306 | elseif b:context_type != s:CONTEXT_PACKAGE_DECL && b:context_type != s:CONTEXT_IMPORT && b:context_type != s:CONTEXT_IMPORT_STATIC 307 | call add(types, {'kind': 'C', 'word': key}) 308 | endif 309 | endif 310 | endfor 311 | 312 | let pkgs += s:DoGetPackageInfoInDirs(a:incomplete, b:context_type == s:CONTEXT_PACKAGE_DECL, 1) 313 | 314 | 315 | " add accessible types which name beginning with the incomplete in source files 316 | " TODO: remove the inaccessible 317 | if b:context_type != s:CONTEXT_PACKAGE_DECL 318 | " single type import 319 | for fqn in s:GetImports('imports_fqn') 320 | let name = fqn[strridx(fqn, ".")+1:] 321 | if name =~ '^' . a:incomplete 322 | call add(types, {'kind': 'C', 'word': name}) 323 | endif 324 | endfor 325 | 326 | " current file 327 | let lnum_old = line('.') 328 | let col_old = col('.') 329 | call cursor(1, 1) 330 | while 1 331 | let lnum = search('\<\C\(class\|interface\|enum\)[ \t\n\r ]\+' . a:incomplete . '[a-zA-Z0-9_$]*[< \t\n\r ]', 'W') 332 | if lnum == 0 333 | break 334 | elseif s:InCommentOrLiteral(line('.'), col('.')) 335 | continue 336 | else 337 | normal w 338 | call add(types, {'kind': 'C', 'word': matchstr(getline(line('.'))[col('.')-1:], s:RE_IDENTIFIER)}) 339 | endif 340 | endwhile 341 | call cursor(lnum_old, col_old) 342 | 343 | " other files 344 | let filepatterns = '' 345 | for dirpath in s:GetSourceDirs(expand('%:p')) 346 | let filepatterns .= escape(dirpath, ' \') . '/*.java ' 347 | endfor 348 | exe 'vimgrep /\s*' . s:RE_TYPE_DECL . '/jg ' . filepatterns 349 | for item in getqflist() 350 | if item.text !~ '^\s*\*\s\+' 351 | let text = matchstr(s:Prune(item.text, -1), '\s*' . s:RE_TYPE_DECL) 352 | if text != '' 353 | let subs = split(substitute(text, '\s*' . s:RE_TYPE_DECL, '\1;\2;\3', ''), ';', 1) 354 | if subs[2] =~# '^' . a:incomplete && (subs[0] =~ '\C\' || fnamemodify(bufname(item.bufnr), ':p:h') == expand('%:p:h')) 355 | call add(types, {'kind': 'C', 'word': subs[2]}) 356 | endif 357 | endif 358 | endif 359 | endfor 360 | endif 361 | 362 | 363 | let result = [] 364 | 365 | " add variables and members in source files 366 | if b:context_type == s:CONTEXT_AFTER_DOT 367 | let matches = s:SearchForName(a:incomplete, 0, 0) 368 | let result += sort(eval('[' . s:DoGetFieldList(matches[2]) . ']')) 369 | let result += sort(eval('[' . s:DoGetMethodList(matches[1]) . ']')) 370 | endif 371 | let result += sort(pkgs) 372 | let result += sort(types) 373 | 374 | return result 375 | endfu 376 | 377 | 378 | " Precondition: expr must end with '.' 379 | " return members of the value of expression 380 | function! s:CompleteAfterDot(expr) 381 | let items = s:ParseExpr(a:expr) " TODO: return a dict containing more than items 382 | if empty(items) 383 | return [] 384 | endif 385 | 386 | 387 | " 0. String literal 388 | call s:Info('P0. "str".|') 389 | if items[-1] =~ '"$' 390 | return s:GetMemberList("java.lang.String") 391 | endif 392 | 393 | 394 | let ti = {} 395 | let ii = 1 " item index 396 | let itemkind = 0 397 | 398 | " 399 | " optimized process 400 | " 401 | " search the longest expr consisting of ident 402 | let i = 1 403 | let k = i 404 | while i < len(items) && items[i] =~ '^\s*' . s:RE_IDENTIFIER . '\s*$' 405 | let ident = substitute(items[i], '\s', '', 'g') 406 | if ident == 'class' || ident == 'this' || ident == 'super' 407 | let k = i 408 | " return when found other keywords 409 | elseif s:IsKeyword(ident) 410 | return [] 411 | endif 412 | let items[i] = substitute(items[i], '\s', '', 'g') 413 | let i += 1 414 | endwhile 415 | 416 | if i > 1 417 | " cases: "this.|", "super.|", "ClassName.this.|", "ClassName.super.|", "TypeName.class.|" 418 | if items[k] ==# 'class' || items[k] ==# 'this' || items[k] ==# 'super' 419 | call s:Info('O1. ' . items[k] . ' ' . join(items[:k-1], '.')) 420 | let ti = s:DoGetClassInfo(items[k] == 'class' ? 'java.lang.Class' : join(items[:k-1], '.')) 421 | if !empty(ti) 422 | let itemkind = items[k] ==# 'this' ? 1 : items[k] ==# 'super' ? 2 : 0 423 | let ii = k+1 424 | else 425 | return [] 426 | endif 427 | 428 | " case: "java.io.File.|" 429 | else 430 | let fqn = join(items[:i-1], '.') 431 | let srcpath = join(s:GetSourceDirs(expand('%:p'), s:GetPackageName()), ',') 432 | call s:Info('O2. ' . fqn) 433 | call s:DoGetTypeInfoForFQN([fqn], srcpath) 434 | if get(get(s:cache, fqn, {}), 'tag', '') == 'CLASSDEF' 435 | let ti = s:cache[fqn] 436 | let itemkind = 11 437 | let ii = i 438 | endif 439 | endif 440 | endif 441 | 442 | 443 | " 444 | " first item 445 | " 446 | if empty(ti) 447 | " cases: 448 | " 1) "int.|", "void.|" - primitive type or pseudo-type, return `class` 449 | " 2) "this.|", "super.|" - special reference 450 | " 3) "var.|" - variable or field 451 | " 4) "String.|" - type imported or defined locally 452 | " 5) "java.|" - package 453 | if items[0] =~ '^\s*' . s:RE_IDENTIFIER . '\s*$' 454 | let ident = substitute(items[0], '\s', '', 'g') 455 | 456 | if s:IsKeyword(ident) 457 | " 1) 458 | call s:Info('F1. "' . ident . '.|"') 459 | if ident ==# 'void' || s:IsBuiltinType(ident) 460 | let ti = s:PRIMITIVE_TYPE_INFO 461 | let itemkind = 11 462 | 463 | " 2) 464 | call s:Info('F2. "' . ident . '.|"') 465 | elseif ident ==# 'this' || ident ==# 'super' 466 | let itemkind = ident ==# 'this' ? 1 : ident ==# 'super' ? 2 : 0 467 | let ti = s:DoGetClassInfo(ident) 468 | endif 469 | 470 | else 471 | " 3) 472 | let typename = s:GetDeclaredClassName(ident) 473 | call s:Info('F3. "' . ident . '.|" typename: "' . typename . '"') 474 | if (typename != '') 475 | if typename[0] == '[' || typename[-1:] == ']' 476 | let ti = s:ARRAY_TYPE_INFO 477 | elseif typename != 'void' && !s:IsBuiltinType(typename) 478 | let ti = s:DoGetClassInfo(typename) 479 | endif 480 | 481 | else 482 | " 4) 483 | call s:Info('F4. "TypeName.|"') 484 | let ti = s:DoGetClassInfo(ident) 485 | let itemkind = 11 486 | 487 | if get(ti, 'tag', '') != 'CLASSDEF' 488 | let ti = {} 489 | endif 490 | 491 | " 5) 492 | if empty(ti) 493 | call s:Info('F5. "package.|"') 494 | unlet ti 495 | let ti = s:GetMembers(ident) " s:DoGetPackegInfo(ident) 496 | let itemkind = 20 497 | endif 498 | endif 499 | endif 500 | 501 | " method invocation: "method().|" - "this.method().|" 502 | elseif items[0] =~ '^\s*' . s:RE_IDENTIFIER . '\s*(' 503 | let ti = s:MethodInvocation(items[0], ti, itemkind) 504 | 505 | " array type, return `class`: "int[] [].|", "java.lang.String[].|", "NestedClass[].|" 506 | elseif items[0] =~# s:RE_ARRAY_TYPE 507 | call s:Info('array type. "' . items[0] . '"') 508 | let qid = substitute(items[0], s:RE_ARRAY_TYPE, '\1', '') 509 | if s:IsBuiltinType(qid) || (!s:HasKeyword(qid) && !empty(s:DoGetClassInfo(qid))) 510 | let ti = s:PRIMITIVE_TYPE_INFO 511 | let itemkind = 11 512 | endif 513 | 514 | " class instance creation expr: "new String().|", "new NonLoadableClass().|" 515 | " array creation expr: "new int[i=1] [val()].|", "new java.lang.String[].|" 516 | elseif items[0] =~ '^\s*new\s\+' 517 | call s:Info('creation expr. "' . items[0] . '"') 518 | let subs = split(substitute(items[0], '^\s*new\s\+\(' .s:RE_QUALID. '\)\s*\([([]\)', '\1;\2', ''), ';') 519 | if subs[1][0] == '[' 520 | let ti = s:ARRAY_TYPE_INFO 521 | elseif subs[1][0] == '(' 522 | let ti = s:DoGetClassInfo(subs[0]) 523 | " exclude interfaces and abstract class. TODO: exclude the inaccessible 524 | if get(ti, 'flags', '')[-10:-10] || get(ti, 'flags', '')[-11:-11] 525 | echo 'cannot instantiate the type ' . subs[0] 526 | let ti = {} 527 | return [] 528 | endif 529 | endif 530 | 531 | " casting conversion: "(Object)o.|" 532 | elseif items[0] =~ s:RE_CASTING 533 | call s:Info('Casting conversion. "' . items[0] . '"') 534 | let subs = split(substitute(items[0], s:RE_CASTING, '\1;\2', ''), ';') 535 | let ti = s:DoGetClassInfo(subs[0]) 536 | 537 | " array access: "var[i][j].|" Note: "var[i][]" is incorrect 538 | elseif items[0] =~# s:RE_ARRAY_ACCESS 539 | let subs = split(substitute(items[0], s:RE_ARRAY_ACCESS, '\1;\2', ''), ';') 540 | if get(subs, 1, '') !~ s:RE_BRACKETS 541 | let typename = s:GetDeclaredClassName(subs[0]) 542 | call s:Info('ArrayAccess. "' .items[0]. '.|" typename: "' . typename . '"') 543 | if (typename != '') 544 | let ti = s:ArrayAccess(typename, items[0]) 545 | endif 546 | endif 547 | endif 548 | endif 549 | 550 | 551 | " 552 | " next items 553 | " 554 | while !empty(ti) && ii < len(items) 555 | " method invocation: "PrimaryExpr.method(parameters)[].|" 556 | if items[ii] =~ '^\s*' . s:RE_IDENTIFIER . '\s*(' 557 | let ti = s:MethodInvocation(items[ii], ti, itemkind) 558 | let itemkind = 0 559 | let ii += 1 560 | continue 561 | 562 | 563 | " expression of selection, field access, array access 564 | elseif items[ii] =~ s:RE_SELECT_OR_ACCESS 565 | let subs = split(substitute(items[ii], s:RE_SELECT_OR_ACCESS, '\1;\2', ''), ';') 566 | let ident = subs[0] 567 | let brackets = get(subs, 1, '') 568 | 569 | " package members 570 | if itemkind/10 == 2 && empty(brackets) && !s:IsKeyword(ident) 571 | let qn = join(items[:ii], '.') 572 | if type(ti) == type([]) 573 | let idx = s:Index(ti, ident, 'word') 574 | if idx >= 0 575 | if ti[idx].kind == 'P' 576 | unlet ti 577 | let ti = s:GetMembers(qn) 578 | let ii += 1 579 | continue 580 | elseif ti[idx].kind == 'C' 581 | unlet ti 582 | let ti = s:DoGetClassInfo(qn) 583 | let itemkind = 11 584 | let ii += 1 585 | continue 586 | endif 587 | endif 588 | endif 589 | 590 | 591 | " type members 592 | elseif itemkind/10 == 1 && empty(brackets) 593 | if ident ==# 'class' || ident ==# 'this' || ident ==# 'super' 594 | let ti = s:DoGetClassInfo(ident == 'class' ? 'java.lang.Class' : join(items[:ii-1], '.')) 595 | let itemkind = ident ==# 'this' ? 1 : ident ==# 'super' ? 2 : 0 596 | let ii += 1 597 | continue 598 | 599 | elseif !s:IsKeyword(ident) && type(ti) == type({}) && get(ti, 'tag', '') == 'CLASSDEF' 600 | " accessible static field 601 | "let idx = s:Index(get(ti, 'fields', []), ident, 'n') 602 | "if idx >= 0 && s:IsStatic(ti.fields[idx].m) 603 | " let ti = s:ArrayAccess(ti.fields[idx].t, items[ii]) 604 | let members = s:SearchMember(ti, ident, 1, itemkind, 1, 0) 605 | if !empty(members[2]) 606 | let ti = s:ArrayAccess(members[2][0].t, items[ii]) 607 | let itemkind = 0 608 | let ii += 1 609 | continue 610 | endif 611 | 612 | " accessible nested type 613 | "if !empty(filter(copy(get(ti, 'classes', [])), 'strpart(v:val, strridx(v:val, ".")) ==# "' . ident . '"')) 614 | if !empty(members[0]) 615 | let ti = s:DoGetClassInfo(join(items[:ii], '.')) 616 | let ii += 1 617 | continue 618 | endif 619 | endif 620 | 621 | 622 | " instance members 623 | elseif itemkind/10 == 0 && !s:IsKeyword(ident) 624 | if type(ti) == type({}) && get(ti, 'tag', '') == 'CLASSDEF' 625 | "let idx = s:Index(get(ti, 'fields', []), ident, 'n') 626 | "if idx >= 0 627 | " let ti = s:ArrayAccess(ti.fields[idx].t, items[ii]) 628 | let members = s:SearchMember(ti, ident, 1, itemkind, 1, 0) 629 | let itemkind = 0 630 | if !empty(members[2]) 631 | let ti = s:ArrayAccess(members[2][0].t, items[ii]) 632 | let ii += 1 633 | continue 634 | endif 635 | endif 636 | endif 637 | endif 638 | 639 | return [] 640 | endwhile 641 | 642 | 643 | " type info or package info --> members 644 | if !empty(ti) 645 | if type(ti) == type({}) 646 | if get(ti, 'tag', '') == 'CLASSDEF' 647 | if get(ti, 'name', '') == '!' 648 | return [{'kind': 'f', 'word': 'class', 'menu': 'Class'}] 649 | elseif get(ti, 'name', '') == '[' 650 | return s:ARRAY_TYPE_MEMBERS 651 | elseif itemkind < 20 652 | return s:DoGetMemberList(ti, itemkind) 653 | endif 654 | elseif get(ti, 'tag', '') == 'PACKAGE' 655 | " TODO: ti -> members, in addition to packages in dirs 656 | return s:GetMembers( substitute(join(items, '.'), '\s', '', 'g') ) 657 | endif 658 | elseif type(ti) == type([]) 659 | return ti 660 | endif 661 | endif 662 | 663 | return [] 664 | endfunction 665 | 666 | 667 | fu! s:MethodInvocation(expr, ti, itemkind) 668 | let subs = split(substitute(a:expr, '\s*\(' . s:RE_IDENTIFIER . '\)\s*\((.*\)', '\1;\2', ''), ';') 669 | 670 | " all methods matched 671 | if empty(a:ti) 672 | let methods = s:SearchForName(subs[0], 0, 1)[1] 673 | elseif type(a:ti) == type({}) && get(a:ti, 'tag', '') == 'CLASSDEF' 674 | let methods = s:SearchMember(a:ti, subs[0], 1, a:itemkind, 1, 0, a:itemkind == 2)[1] 675 | " let methods = s:filter(get(a:ti, 'methods', []), 'item.n == "' . subs[0] . '"') 676 | " if a:itemkind == 1 || a:itemkind == 2 677 | " let methods += s:filter(get(a:ti, 'declared_methods', []), 'item.n == "' . subs[0] . '"') 678 | " endif 679 | else 680 | let methods = [] 681 | endif 682 | 683 | let method = s:DetermineMethod(methods, subs[1]) 684 | if !empty(method) 685 | return s:ArrayAccess(method.r, a:expr) 686 | endif 687 | return {} 688 | endfu 689 | 690 | fu! s:ArrayAccess(arraytype, expr) 691 | if a:expr =~ s:RE_BRACKETS | return {} | endif 692 | let typename = a:arraytype 693 | 694 | let dims = 0 695 | if typename[0] == '[' || typename[-1:] == ']' || a:expr[-1:] == ']' 696 | let dims = s:CountDims(a:expr) - s:CountDims(typename) 697 | if dims == 0 698 | let typename = matchstr(typename, s:RE_IDENTIFIER) 699 | elseif dims < 0 700 | return s:ARRAY_TYPE_INFO 701 | else 702 | "echoerr 'dims exceeds' 703 | endif 704 | endif 705 | if dims == 0 706 | if typename != 'void' && !s:IsBuiltinType(typename) 707 | return s:DoGetClassInfo(typename) 708 | endif 709 | endif 710 | return {} 711 | endfu 712 | 713 | 714 | " Quick information {{{1 715 | function! MyBalloonExpr() 716 | if (searchdecl(v:beval_text, 1, 0) == 0) 717 | return s:GetVariableDeclaration() 718 | endif 719 | return '' 720 | " return 'Cursor is at line ' . v:beval_lnum . 721 | " \', column ' . v:beval_col . 722 | " \ ' of file ' . bufname(v:beval_bufnr) . 723 | " \ ' on word "' . v:beval_text . '"' 724 | endfunction 725 | "set bexpr=MyBalloonExpr() 726 | "set ballooneval 727 | 728 | " parameters information {{{1 729 | fu! javacomplete#CompleteParamsInfo(findstart, base) 730 | if a:findstart 731 | return col('.') - 1 732 | endif 733 | 734 | 735 | let mi = s:GetMethodInvocationExpr(s:GetStatement()) 736 | if empty(mi.method) 737 | return [] 738 | endif 739 | 740 | " TODO: how to determine overloaded functions 741 | "let mi.params = s:EvalParams(mi.params) 742 | if empty(mi.expr) 743 | let methods = s:SearchForName(mi.method, 0, 1)[1] 744 | let result = eval('[' . s:DoGetMethodList(methods) . ']') 745 | elseif mi.method == '+' 746 | let result = s:GetConstructorList(mi.expr) 747 | else 748 | let result = s:CompleteAfterDot(mi.expr) 749 | endif 750 | 751 | if !empty(result) 752 | if !empty(mi.method) && mi.method != '+' 753 | let result = filter(result, "type(v:val) == type('') ? v:val ==# '" . mi.method . "' : v:val['word'] ==# '" . mi.method . "('") 754 | endif 755 | return result 756 | endif 757 | endfu 758 | 759 | " scanning and parsing {{{1 760 | 761 | " Search back from the cursor position till meeting '{' or ';'. 762 | " '{' means statement start, ';' means end of a previous statement. 763 | " Return: statement before cursor 764 | " Note: It's the base for parsing. And It's OK for most cases. 765 | function! s:GetStatement() 766 | if getline('.') =~ '^\s*\(import\|package\)\s\+' 767 | return strpart(getline('.'), match(getline('.'), '\(import\|package\)'), col('.')-1) 768 | endif 769 | 770 | let lnum_old = line('.') 771 | let col_old = col('.') 772 | 773 | while 1 774 | if search('[{};]\|<%\|<%!', 'bW') == 0 775 | let lnum = 1 776 | let col = 1 777 | else 778 | if s:InCommentOrLiteral(line('.'), col('.')) 779 | continue 780 | endif 781 | 782 | normal w 783 | let lnum = line('.') 784 | let col = col('.') 785 | endif 786 | break 787 | endwhile 788 | 789 | silent call cursor(lnum_old, col_old) 790 | return s:MergeLines(lnum, col, lnum_old, col_old) 791 | endfunction 792 | 793 | fu! s:MergeLines(lnum, col, lnum_old, col_old) 794 | let lnum = a:lnum 795 | let col = a:col 796 | 797 | let str = '' 798 | if lnum < a:lnum_old 799 | let str = s:Prune(strpart(getline(lnum), a:col-1)) 800 | let lnum += 1 801 | while lnum < a:lnum_old 802 | let str .= s:Prune(getline(lnum)) 803 | let lnum += 1 804 | endwhile 805 | let col = 1 806 | endif 807 | let lastline = strpart(getline(a:lnum_old), col-1, a:col_old-col) 808 | let str .= s:Prune(lastline, col) 809 | let str = s:RemoveBlockComments(str) 810 | " generic in JAVA 5+ 811 | while match(str, s:RE_TYPE_ARGUMENTS) != -1 812 | let str = substitute(str, '\(' . s:RE_TYPE_ARGUMENTS . '\)', '\=repeat(" ", len(submatch(1)))', 'g') 813 | endwhile 814 | let str = substitute(str, '\s\s\+', ' ', 'g') 815 | let str = substitute(str, '\([.()]\)[ \t]\+', '\1', 'g') 816 | let str = substitute(str, '[ \t]\+\([.()]\)', '\1', 'g') 817 | return s:Trim(str) . matchstr(lastline, '\s*$') 818 | endfu 819 | 820 | " Extract a clean expr, removing some non-necessary characters. 821 | fu! s:ExtractCleanExpr(expr) 822 | let cmd = substitute(a:expr, '[ \t\r\n ]\+\([.()[\]]\)', '\1', 'g') 823 | let cmd = substitute(cmd, '\([.()[\]]\)[ \t\r\n ]\+', '\1', 'g') 824 | 825 | let pos = strlen(cmd)-1 826 | while pos >= 0 && cmd[pos] =~ '[a-zA-Z0-9_.)\]]' 827 | if cmd[pos] == ')' 828 | let pos = s:SearchPairBackward(cmd, pos, '(', ')') 829 | elseif cmd[pos] == ']' 830 | let pos = s:SearchPairBackward(cmd, pos, '[', ']') 831 | endif 832 | let pos -= 1 833 | endwhile 834 | 835 | " try looking back for "new" 836 | let idx = match(strpart(cmd, 0, pos+1), '\= 0 851 | if a:expr[e] == '.' 852 | let subexpr = strpart(a:expr, s, e-s) 853 | call extend(items, isparen ? s:ProcessParentheses(subexpr) : [subexpr]) 854 | let isparen = 0 855 | let s = e + 1 856 | elseif a:expr[e] == '(' 857 | let e = s:GetMatchedIndexEx(a:expr, e, '(', ')') 858 | let isparen = 1 859 | if e < 0 860 | break 861 | else 862 | let e = matchend(a:expr, '^\s*[.[]', e+1)-1 863 | continue 864 | endif 865 | elseif a:expr[e] == '[' 866 | let e = s:GetMatchedIndexEx(a:expr, e, '[', ']') 867 | if e < 0 868 | break 869 | else 870 | let e = matchend(a:expr, '^\s*[.[]', e+1)-1 871 | continue 872 | endif 873 | endif 874 | let e = match(a:expr, '[.([]', s) 875 | endwhile 876 | let tail = strpart(a:expr, s) 877 | if tail !~ '^\s*$' 878 | call extend(items, isparen ? s:ProcessParentheses(tail) : [tail]) 879 | endif 880 | 881 | return items 882 | endfu 883 | 884 | " Given optional argument, call s:ParseExpr() to parser the nonparentheses expr 885 | fu! s:ProcessParentheses(expr, ...) 886 | let s = matchend(a:expr, '^\s*(') 887 | if s != -1 888 | let e = s:GetMatchedIndexEx(a:expr, s-1, '(', ')') 889 | if e >= 0 890 | let tail = strpart(a:expr, e+1) 891 | if tail =~ '^\s*[\=$' 892 | return s:ProcessParentheses(strpart(a:expr, s, e-s), 1) 893 | elseif tail =~ '^\s*\w' 894 | return [strpart(a:expr, 0, e+1) . 'obj.'] 895 | endif 896 | endif 897 | 898 | " multi-dot-expr except for new expr 899 | elseif a:0 > 0 && stridx(a:expr, '.') != match(a:expr, '\.\s*$') && a:expr !~ '^\s*new\s\+' 900 | return s:ParseExpr(a:expr) 901 | endif 902 | return [a:expr] 903 | endfu 904 | 905 | " return {'expr': , 'method': , 'params': } 906 | fu! s:GetMethodInvocationExpr(expr) 907 | let idx = strlen(a:expr)-1 908 | while idx >= 0 909 | if a:expr[idx] == '(' 910 | break 911 | elseif a:expr[idx] == ')' 912 | let idx = s:SearchPairBackward(a:expr, idx, '(', ')') 913 | elseif a:expr[idx] == ']' 914 | let idx = s:SearchPairBackward(a:expr, idx, '[', ']') 915 | endif 916 | let idx -= 1 917 | endwhile 918 | 919 | let mi = {'expr': strpart(a:expr, 0, idx+1), 'method': '', 'params': strpart(a:expr, idx+1)} 920 | let idx = match(mi.expr, '\= 0 922 | let mi.method = '+' 923 | let mi.expr = substitute(matchstr(strpart(mi.expr, idx+4), s:RE_QUALID), '\s', '', 'g') 924 | else 925 | let idx = match(mi.expr, '\<' . s:RE_IDENTIFIER . '\s*(\s*$') 926 | if idx >= 0 927 | let subs = s:SplitAt(mi.expr, idx-1) 928 | let mi.method = substitute(subs[1], '\s*(\s*$', '', '') 929 | let mi.expr = s:ExtractCleanExpr(subs[0]) 930 | endif 931 | endif 932 | return mi 933 | endfu 934 | 935 | " imports {{{1 936 | function! s:GenerateImports() 937 | let imports = [] 938 | 939 | let lnum_old = line('.') 940 | let col_old = col('.') 941 | call cursor(1, 1) 942 | 943 | if &ft == 'jsp' 944 | while 1 945 | let lnum = search('\' || str =~ '' 952 | let str = substitute(str, '.*import=[''"]\([a-zA-Z0-9_$.*, \t]\+\)[''"].*', '\1', '') 953 | for item in split(str, ',') 954 | call add(imports, substitute(item, '\s', '', 'g')) 955 | endfor 956 | endif 957 | endwhile 958 | else 959 | while 1 960 | let lnum = search('\', 'W') 961 | if (lnum == 0) 962 | break 963 | elseif !s:InComment(line("."), col(".")-1) 964 | normal w 965 | " TODO: search semicolon or import keyword, excluding comment 966 | let stat = matchstr(getline(lnum)[col('.')-1:], '\(static\s\+\)\?\(' .s:RE_QUALID. '\%(\s*\.\s*\*\)\?\)\s*;') 967 | if !empty(stat) 968 | call add(imports, stat[:-2]) 969 | endif 970 | endif 971 | endwhile 972 | endif 973 | 974 | call cursor(lnum_old, col_old) 975 | return imports 976 | endfunction 977 | 978 | fu! s:GetImports(kind, ...) 979 | let filekey = a:0 > 0 && !empty(a:1) ? a:1 : s:GetCurrentFileKey() 980 | let props = get(s:files, filekey, {}) 981 | if !has_key(props, a:kind) 982 | let props['imports'] = filekey == s:GetCurrentFileKey() ? s:GenerateImports() : props.unit.imports 983 | let props['imports_static'] = [] 984 | let props['imports_fqn'] = [] 985 | let props['imports_star'] = ['java.lang.'] 986 | if &ft == 'jsp' || filekey =~ '\.jsp$' 987 | let props.imports_star += ['javax.servlet.', 'javax.servlet.http.', 'javax.servlet.jsp.'] 988 | endif 989 | 990 | for import in props.imports 991 | let subs = split(substitute(import, '^\s*\(static\s\+\)\?\(' .s:RE_QUALID. '\%(\s*\.\s*\*\)\?\)\s*$', '\1;\2', ''), ';', 1) 992 | let qid = substitute(subs[1] , '\s', '', 'g') 993 | if !empty(subs[0]) 994 | call add(props.imports_static, qid) 995 | elseif qid[-1:] == '*' 996 | call add(props.imports_star, qid[:-2]) 997 | else 998 | call add(props.imports_fqn, qid) 999 | endif 1000 | endfor 1001 | let s:files[filekey] = props 1002 | endif 1003 | return get(props, a:kind, []) 1004 | endfu 1005 | 1006 | " search for name in 1007 | " return the fqn matched 1008 | fu! s:SearchSingleTypeImport(name, fqns) 1009 | let matches = s:filter(a:fqns, 'item =~# ''\<' . a:name . '$''') 1010 | if len(matches) == 1 1011 | return matches[0] 1012 | elseif !empty(matches) 1013 | echoerr 'Name "' . a:name . '" conflicts between ' . join(matches, ' and ') 1014 | return matches[0] 1015 | endif 1016 | return '' 1017 | endfu 1018 | 1019 | " search for name in static imports, return list of members with the same name 1020 | " return [types, methods, fields] 1021 | fu! s:SearchStaticImports(name, fullmatch) 1022 | let result = [[], [], []] 1023 | let candidates = [] " list of the canonical name 1024 | for item in s:GetImports('imports_static') 1025 | if item[-1:] == '*' " static import on demand 1026 | call add(candidates, item[:-3]) 1027 | elseif item[strridx(item, '.')+1:] ==# a:name 1028 | \ || (!a:fullmatch && item[strridx(item, '.')+1:] =~ '^' . a:name) 1029 | call add(candidates, item[:strridx(item, '.')]) 1030 | endif 1031 | endfor 1032 | if empty(candidates) 1033 | return result 1034 | endif 1035 | 1036 | 1037 | " read type info which are not in cache 1038 | let commalist = '' 1039 | for typename in candidates 1040 | if !has_key(s:cache, typename) 1041 | let commalist .= typename . ',' 1042 | endif 1043 | endfor 1044 | if commalist != '' 1045 | let res = s:RunReflection('-E', commalist, 's:SearchStaticImports in Batch') 1046 | if res =~ "^{'" 1047 | let dict = eval(res) 1048 | for key in keys(dict) 1049 | let s:cache[key] = s:Sort(dict[key]) 1050 | endfor 1051 | endif 1052 | endif 1053 | 1054 | " search in all candidates 1055 | for typename in candidates 1056 | let ti = get(s:cache, typename, 0) 1057 | if type(ti) == type({}) && get(ti, 'tag', '') == 'CLASSDEF' 1058 | let members = s:SearchMember(ti, a:name, a:fullmatch, 12, 1, 0) 1059 | let result[1] += members[1] 1060 | let result[2] += members[2] 1061 | "let pattern = 'item.n ' . (a:fullmatch ? '==# ''' : '=~# ''^') . a:name . ''' && s:IsStatic(item.m)' 1062 | "let result[1] += s:filter(get(ti, 'methods', []), pattern) 1063 | "let result[2] += s:filter(get(ti, 'fields', []), pattern) 1064 | else 1065 | " TODO: mark the wrong import declaration. 1066 | endif 1067 | endfor 1068 | return result 1069 | endfu 1070 | 1071 | 1072 | " search decl {{{1 1073 | " Return: The declaration of identifier under the cursor 1074 | " Note: The type of a variable must be imported or a fqn. 1075 | function! s:GetVariableDeclaration() 1076 | let lnum_old = line('.') 1077 | let col_old = col('.') 1078 | 1079 | silent call search('[^a-zA-Z0-9$_.,?<>[\] \t\r\n ]', 'bW') " call search('[{};(,]', 'b') 1080 | normal w 1081 | let lnum = line('.') 1082 | let col = col('.') 1083 | if (lnum == lnum_old && col == col_old) 1084 | return '' 1085 | endif 1086 | 1087 | " silent call search('[;){]') 1088 | " let lnum_end = line('.') 1089 | " let col_end = col('.') 1090 | " let declaration = '' 1091 | " while (lnum <= lnum_end) 1092 | " let declaration = declaration . getline(lnum) 1093 | " let lnum = lnum + 1 1094 | " endwhile 1095 | " let declaration = strpart(declaration, col-1) 1096 | " let declaration = substitute(declaration, '\.[ \t]\+', '.', 'g') 1097 | 1098 | silent call cursor(lnum_old, col_old) 1099 | return s:MergeLines(lnum, col, lnum_old, col_old) 1100 | endfunction 1101 | 1102 | function! s:FoundClassDeclaration(type) 1103 | let lnum_old = line('.') 1104 | let col_old = col('.') 1105 | call cursor(1, 1) 1106 | while 1 1107 | let lnum = search('\<\C\(class\|interface\|enum\)[ \t\n\r ]\+' . a:type . '[< \t\n\r ]', 'W') 1108 | if lnum == 0 || !s:InCommentOrLiteral(line('.'), col('.')) 1109 | break 1110 | endif 1111 | endwhile 1112 | 1113 | " search mainly for the cases: " class /* block comment */ Ident" 1114 | " " class // comment \n Ident " 1115 | if lnum == 0 1116 | let found = 0 1117 | while !found 1118 | let lnum = search('\<\C\(class\|interface\|enum\)[ \t\n\r ]\+', 'W') 1119 | if lnum == 0 1120 | break 1121 | elseif s:InCommentOrLiteral(line('.'), col('.')) 1122 | continue 1123 | else 1124 | normal w 1125 | " skip empty line 1126 | while getline(line('.'))[col('.')-1] == '' 1127 | normal w 1128 | endwhile 1129 | let lnum = line('.') 1130 | let col = col('.') 1131 | while 1 1132 | if match(getline(lnum)[col-1:], '^[ \t\n\r ]*' . a:type . '[< \t\n\r ]') >= 0 1133 | let found = 1 1134 | " meets comment 1135 | elseif match(getline(lnum)[col-1:], '^[ \t\n\r ]*\(//\|/\*\)') >= 0 1136 | if getline(lnum)[col-1:col] == '//' 1137 | normal $eb 1138 | else 1139 | let lnum = search('\*\/', 'W') 1140 | if lnum == 0 1141 | break 1142 | endif 1143 | normal web 1144 | endif 1145 | let lnum = line('.') 1146 | let col = col('.') 1147 | continue 1148 | endif 1149 | break 1150 | endwhile 1151 | endif 1152 | endwhile 1153 | endif 1154 | 1155 | silent call cursor(lnum_old, col_old) 1156 | return lnum 1157 | endfu 1158 | 1159 | fu! s:FoundClassLocally(type) 1160 | " current path 1161 | if globpath(expand('%:p:h'), a:type . '.java') != '' 1162 | return 1 1163 | endif 1164 | 1165 | " 1166 | let srcpath = javacomplete#GetSourcePath(1) 1167 | let file = globpath(srcpath, substitute(fqn, '\.', '/', 'g') . '.java') 1168 | if file != '' 1169 | return 1 1170 | endif 1171 | 1172 | return 0 1173 | endfu 1174 | 1175 | " regexp samples: 1176 | " echo search('\(\(public\|protected|private\)[ \t\n\r]\+\)\?\(\(static\)[ \t\n\r]\+\)\?\(\\|\\)[ \t\n\r]\+HelloWorld[^a-zA-Z0-9_$]', 'W') 1177 | " echo substitute(getline('.'), '.*\(\(public\|protected\|private\)[ \t\n\r]\+\)\?\(\(static\)[ \t\n\r]\+\)\?\(\\|\\)\s\+\([a-zA-Z0-9_]\+\)\s\+\(\(implements\|extends\)\s\+\([^{]\+\)\)\?\s*{.*', '["\1", "\2", "\3", "\4", "\5", "\6", "\8", "\9"]', '') 1178 | " code sample: 1179 | function! s:GetClassDeclarationOf(type) 1180 | call cursor(1, 1) 1181 | let decl = [] 1182 | 1183 | let lnum = search('\(\\|\\)[ \t\n\r]\+' . a:type . '[^a-zA-Z0-9_$]', 'W') 1184 | if (lnum != 0) 1185 | " TODO: search back for optional 'public | private' and 'static' 1186 | " join lines till to '{' 1187 | let lnum_end = search('{') 1188 | if (lnum_end != 0) 1189 | let str = '' 1190 | while (lnum <= lnum_end) 1191 | let str = str . getline(lnum) 1192 | let lnum = lnum + 1 1193 | endwhile 1194 | 1195 | exe "let decl = " . substitute(str, '.*\(\\|\\)\s\+\([a-zA-Z0-9_]\+\)\s\+\(\(implements\|extends\)\s\+\([^{]\+\)\)\?\s*{.*', '["\1", "\2", "\4", "\5"]', '') 1196 | endif 1197 | endif 1198 | 1199 | return decl 1200 | endfunction 1201 | 1202 | " return list 1203 | " 0 class | interface 1204 | " 1 name 1205 | " [2 implements | extends ] 1206 | " [3 parent list ] 1207 | function! s:GetThisClassDeclaration() 1208 | let lnum_old = line('.') 1209 | let col_old = col('.') 1210 | 1211 | while (1) 1212 | call search('\(\\|\\|\\)[ \t\r\n]\+', 'bW') 1213 | if !s:InComment(line("."), col(".")-1) 1214 | if getline('.')[col('.')-2] !~ '\S' 1215 | break 1216 | endif 1217 | end 1218 | endwhile 1219 | 1220 | " join lines till to '{' 1221 | let str = '' 1222 | let lnum = line('.') 1223 | call search('{') 1224 | let lnum_end = line('.') 1225 | while (lnum <= lnum_end) 1226 | let str = str . getline(lnum) 1227 | let lnum = lnum + 1 1228 | endwhile 1229 | 1230 | 1231 | let declaration = substitute(str, '.*\(\\|\\)\s\+\([a-zA-Z0-9_]\+\)\(\s\+\(implements\|extends\)\s\+\([^{]\+\)\)\?\s*{.*', '["\1", "\2", "\4", "\5"]', '') 1232 | call cursor(lnum_old, col_old) 1233 | if declaration !~ '^[' 1234 | echoerr 'Some error occurs when recognizing this class:' . declaration 1235 | return ['', ''] 1236 | endif 1237 | exe "let list = " . declaration 1238 | return list 1239 | endfunction 1240 | 1241 | " searches for name of a var or a field and determines the meaning {{{1 1242 | 1243 | " The standard search order of a variable or field is as follows: 1244 | " 1. Local variables declared in the code block, for loop, or catch clause 1245 | " from current scope up to the most outer block, a method or an initialization block 1246 | " 2. Parameters if the code is in a method or ctor 1247 | " 3. Fields of the type 1248 | " 4. Accessible inherited fields. 1249 | " 5. If the type is a nested type, 1250 | " local variables of the enclosing block or fields of the enclosing class. 1251 | " Note that if the type is a static nested type, only static members of an enclosing block or class are searched 1252 | " Reapply this rule to the upper block and class enclosing the enclosing type recursively 1253 | " 6. Accessible static fields imported. 1254 | " It is allowed that several fields with the same name. 1255 | 1256 | " The standard search order of a method is as follows: 1257 | " 1. Methods of the type 1258 | " 2. Accessible inherited methods. 1259 | " 3. Methods of the enclosing class if the type is a nested type. 1260 | " 4. Accessible static methods imported. 1261 | " It is allowed that several methods with the same name and signature. 1262 | 1263 | " first return at once if found one. 1264 | " fullmatch 1 - equal, 0 - match beginning 1265 | " return [types, methods, fields, vars] 1266 | fu! s:SearchForName(name, first, fullmatch) 1267 | let result = [[], [], [], []] 1268 | if s:IsKeyword(a:name) 1269 | return result 1270 | endif 1271 | 1272 | " use java_parser.vim 1273 | if javacomplete#GetSearchdeclMethod() == 4 1274 | " declared in current file 1275 | let unit = javacomplete#parse() 1276 | let targetPos = java_parser#MakePos(line('.')-1, col('.')-1) 1277 | let trees = s:SearchNameInAST(unit, a:name, targetPos, a:fullmatch) 1278 | for tree in trees 1279 | if tree.tag == 'VARDEF' 1280 | call add(result[2], tree) 1281 | elseif tree.tag == 'METHODDEF' 1282 | call add(result[1], tree) 1283 | elseif tree.tag == 'CLASSDEF' 1284 | call add(result[0], tree.name) 1285 | endif 1286 | endfor 1287 | 1288 | if a:first && result != [[], [], [], []] | return result | endif 1289 | 1290 | " Accessible inherited members 1291 | let type = get(s:SearchTypeAt(unit, targetPos), -1, {}) 1292 | if !empty(type) 1293 | let members = s:SearchMember(type, a:name, a:fullmatch, 2, 1, 0, 1) 1294 | let result[0] += members[0] 1295 | let result[1] += members[1] 1296 | let result[2] += members[2] 1297 | " "let ti = s:AddInheritedClassInfo({}, type) 1298 | " if !empty(ti) 1299 | " let comparator = a:fullmatch ? "=~# '^" : "==# '" 1300 | " let result[0] += s:filter(get(ti, 'classes', []), 'item ' . comparator . a:name . "'") 1301 | " let result[1] += s:filter(get(ti, 'methods', []), 'item.n ' . comparator . a:name . "'") 1302 | " let result[2] += s:filter(get(ti, 'fields', []), 'item.n ' . comparator . a:name . "'") 1303 | " if a:0 > 0 1304 | " let result[1] += s:filter(get(ti, 'declared_methods', []), 'item.n ' . comparator . a:name . "'") 1305 | " let result[2] += s:filter(get(ti, 'declared_fields', []), 'item.n ' . comparator . a:name . "'") 1306 | " endif 1307 | " if a:first && result != [[], [], [], []] | return result | endif 1308 | " endif 1309 | endif 1310 | 1311 | " static import 1312 | let si = s:SearchStaticImports(a:name, a:fullmatch) 1313 | let result[1] += si[1] 1314 | let result[2] += si[2] 1315 | endif 1316 | return result 1317 | endfu 1318 | 1319 | " TODO: how to determine overloaded functions 1320 | fu! s:DetermineMethod(methods, parameters) 1321 | return get(a:methods, 0, {}) 1322 | endfu 1323 | 1324 | " Parser.GetType() in insenvim 1325 | function! s:GetDeclaredClassName(var) 1326 | let var = s:Trim(a:var) 1327 | call s:Trace('GetDeclaredClassName for "' . var . '"') 1328 | if var =~# '^\(this\|super\)$' 1329 | return var 1330 | endif 1331 | 1332 | 1333 | " Special handling for builtin objects in JSP 1334 | if &ft == 'jsp' 1335 | if get(s:JSP_BUILTIN_OBJECTS, a:var, '') != '' 1336 | return s:JSP_BUILTIN_OBJECTS[a:var] 1337 | endif 1338 | endif 1339 | 1340 | " use java_parser.vim 1341 | if javacomplete#GetSearchdeclMethod() == 4 1342 | let variable = get(s:SearchForName(var, 1, 1)[2], -1, {}) 1343 | return get(variable, 'tag', '') == 'VARDEF' ? java_parser#type2Str(variable.vartype) : get(variable, 't', '') 1344 | endif 1345 | 1346 | 1347 | let ic = &ignorecase 1348 | setlocal noignorecase 1349 | 1350 | let searched = javacomplete#GetSearchdeclMethod() == 2 ? s:Searchdecl(var, 1, 0) : searchdecl(var, 1, 0) 1351 | if (searched == 0) 1352 | " code sample: 1353 | " String tmp; java. 1354 | " lang. String str, value; 1355 | " for (int i = 0, j = 0; i < 10; i++) { 1356 | " j = 0; 1357 | " } 1358 | let declaration = s:GetVariableDeclaration() 1359 | " Assume it a class member, and remove modifiers 1360 | let class = substitute(declaration, '^\(public\s\+\|protected\s\+\|private\s\+\|abstract\s\+\|static\s\+\|final\s\+\|native\s\+\)*', '', '') 1361 | let class = substitute(class, '\s*\([a-zA-Z0-9_.]\+\)\(\[\]\)\?\s\+.*', '\1\2', '') 1362 | let class = substitute(class, '\([a-zA-Z0-9_.]\)<.*', '\1', '') 1363 | call s:Info('class: "' . class . '" declaration: "' . declaration . '" for ' . a:var) 1364 | let &ignorecase = ic 1365 | if class != '' && class !=# a:var && class !=# 'import' && class !=# 'class' 1366 | return class 1367 | endif 1368 | endif 1369 | 1370 | let &ignorecase = ic 1371 | call s:Trace('GetDeclaredClassName: cannot find') 1372 | return '' 1373 | endfunction 1374 | 1375 | " using java_parser.vim {{{1 1376 | " javacomplete#parse() {{{2 1377 | fu! javacomplete#parse(...) 1378 | let filename = a:0 == 0 ? '%' : a:1 1379 | 1380 | let changed = 0 1381 | if filename == '%' 1382 | let filename = s:GetCurrentFileKey() 1383 | let props = get(s:files, filename, {}) 1384 | if get(props, 'changedtick', -1) != b:changedtick 1385 | let changed = 1 1386 | let props.changedtick = b:changedtick 1387 | let lines = getline('^', '$') 1388 | endif 1389 | else 1390 | let props = get(s:files, filename, {}) 1391 | if get(props, 'modifiedtime', 0) != getftime(filename) 1392 | let changed = 1 1393 | let props.modifiedtime = getftime(filename) 1394 | let lines = readfile(filename) 1395 | endif 1396 | endif 1397 | 1398 | if changed 1399 | call java_parser#InitParser(lines) 1400 | call java_parser#SetLogLevel(5) 1401 | let props.unit = java_parser#compilationUnit() 1402 | 1403 | let package = has_key(props.unit, 'package') ? props.unit.package . '.' : '' 1404 | call s:UpdateFQN(props.unit, package) 1405 | endif 1406 | let s:files[filename] = props 1407 | return props.unit 1408 | endfu 1409 | 1410 | " update fqn for toplevel types or nested types. 1411 | " not for local type or anonymous type 1412 | fu! s:UpdateFQN(tree, qn) 1413 | if a:tree.tag == 'TOPLEVEL' 1414 | for def in a:tree.types 1415 | call s:UpdateFQN(def, a:qn) 1416 | endfor 1417 | elseif a:tree.tag == 'CLASSDEF' 1418 | let a:tree.fqn = a:qn . a:tree.name 1419 | for def in a:tree.defs 1420 | if def.tag == 'CLASSDEF' 1421 | call s:UpdateFQN(def, a:tree.fqn . '.') 1422 | endif 1423 | endfor 1424 | endif 1425 | endfu 1426 | 1427 | " TreeVisitor {{{2 1428 | fu! s:visitTree(tree, param) dict 1429 | if type(a:tree) == type({}) 1430 | exe get(self, get(a:tree, 'tag', ''), '') 1431 | elseif type(a:tree) == type([]) 1432 | for tree in a:tree 1433 | call self.visit(tree, a:param) 1434 | endfor 1435 | endif 1436 | endfu 1437 | 1438 | let s:TreeVisitor = {'visit': function('s:visitTree'), 1439 | \ 'TOPLEVEL' : 'call self.visit(a:tree.types, a:param)', 1440 | \ 'BLOCK' : 'let stats = a:tree.stats | if stats == [] | call java_parser#GotoPosition(a:tree.pos) | let stats = java_parser#block().stats | endif | call self.visit(stats, a:param)', 1441 | \ 'DOLOOP' : 'call self.visit(a:tree.body, a:param) | call self.visit(a:tree.cond, a:param)', 1442 | \ 'WHILELOOP' : 'call self.visit(a:tree.cond, a:param) | call self.visit(a:tree.body, a:param)', 1443 | \ 'FORLOOP' : 'call self.visit(a:tree.init, a:param) | call self.visit(a:tree.cond, a:param) | call self.visit(a:tree.step, a:param) | call self.visit(a:tree.body, a:param)', 1444 | \ 'FOREACHLOOP' : 'call self.visit(a:tree.var, a:param) | call self.visit(a:tree.expr, a:param) | call self.visit(a:tree.body, a:param)', 1445 | \ 'LABELLED' : 'call self.visit(a:tree.body, a:param)', 1446 | \ 'SWITCH' : 'call self.visit(a:tree.selector, a:param) | call self.visit(a:tree.cases, a:param)', 1447 | \ 'CASE' : 'call self.visit(a:tree.pat, a:param) | call self.visit(a:tree.stats, a:param)', 1448 | \ 'SYNCHRONIZED': 'call self.visit(a:tree.lock, a:param) | call self.visit(a:tree.body, a:param)', 1449 | \ 'TRY' : 'call self.visit(a:tree.body, a:param) | call self.visit(a:tree.catchers, a:param) | call self.visit(a:tree.finalizer, a:param) ', 1450 | \ 'CATCH' : 'call self.visit(a:tree.param,a:param) | call self.visit(a:tree.body, a:param)', 1451 | \ 'CONDEXPR' : 'call self.visit(a:tree.cond, a:param) | call self.visit(a:tree.truepart, a:param) | call self.visit(a:tree.falsepart, a:param)', 1452 | \ 'IF' : 'call self.visit(a:tree.cond, a:param) | call self.visit(a:tree.thenpart, a:param) | if has_key(a:tree, "elsepart") | call self.visit(a:tree.elsepart, a:param) | endif', 1453 | \ 'EXEC' : 'call self.visit(a:tree.expr, a:param)', 1454 | \ 'APPLY' : 'call self.visit(a:tree.meth, a:param) | call self.visit(a:tree.args, a:param)', 1455 | \ 'NEWCLASS' : 'call self.visit(a:tree.def, a:param)' 1456 | \} 1457 | 1458 | let s:TV_CMP_POS = 'a:tree.pos <= a:param.pos && a:param.pos <= get(a:tree, "endpos", -1)' 1459 | let s:TV_CMP_POS_BODY = 'has_key(a:tree, "body") && a:tree.body.pos <= a:param.pos && a:param.pos <= get(a:tree.body, "endpos", -1)' 1460 | 1461 | " Return a stack of enclosing types (including local or anonymous classes). 1462 | " Given the optional argument, return all (toplevel or static member) types besides enclosing types. 1463 | fu! s:SearchTypeAt(tree, targetPos, ...) 1464 | let s:TreeVisitor.CLASSDEF = 'if a:param.allNonLocal || ' . s:TV_CMP_POS . ' | call add(a:param.result, a:tree) | call self.visit(a:tree.defs, a:param) | endif' 1465 | let s:TreeVisitor.METHODDEF = 'if ' . s:TV_CMP_POS_BODY . ' | call self.visit(a:tree.body, a:param) | endif' 1466 | let s:TreeVisitor.VARDEF = 'if has_key(a:tree, "init") && !a:param.allNonLocal && ' . s:TV_CMP_POS . ' | call self.visit(a:tree.init, a:param) | endif' 1467 | 1468 | let result = [] 1469 | call s:TreeVisitor.visit(a:tree, {'result': result, 'pos': a:targetPos, 'allNonLocal': a:0 == 0 ? 0 : 1}) 1470 | return result 1471 | endfu 1472 | 1473 | " a:1 match beginning 1474 | " return a stack of matching name 1475 | fu! s:SearchNameInAST(tree, name, targetPos, fullmatch) 1476 | let comparator = a:fullmatch ? '==#' : '=~# "^" .' 1477 | let cmd = 'if a:tree.name ' .comparator. ' a:param.name | call add(a:param.result, a:tree) | endif' 1478 | let s:TreeVisitor.CLASSDEF = 'if ' . s:TV_CMP_POS . ' | ' . cmd . ' | call self.visit(a:tree.defs, a:param) | endif' 1479 | let s:TreeVisitor.METHODDEF = cmd . ' | if ' . s:TV_CMP_POS_BODY . ' | call self.visit(a:tree.params, a:param) | call self.visit(a:tree.body, a:param) | endif' 1480 | let s:TreeVisitor.VARDEF = cmd . ' | if has_key(a:tree, "init") && ' . s:TV_CMP_POS . ' | call self.visit(a:tree.init, a:param) | endif' 1481 | 1482 | let result = [] 1483 | call s:TreeVisitor.visit(a:tree, {'result': result, 'pos': a:targetPos, 'name': a:name}) 1484 | "call s:Info(a:name . ' ' . string(result) . ' line: ' . line('.') . ' col: ' . col('.')) . ' ' . a:targetPos 1485 | return result 1486 | endfu 1487 | 1488 | 1489 | " javacomplete#Searchdecl {{{2 1490 | " TODO: 1491 | fu! javacomplete#Searchdecl() 1492 | let var = expand('') 1493 | 1494 | let line = line('.')-1 1495 | let col = col('.')-1 1496 | 1497 | 1498 | if var =~# '^\(this\|super\)$' 1499 | if &ft == 'jsp' 1500 | return '' 1501 | endif 1502 | 1503 | let matchs = s:SearchTypeAt(javacomplete#parse(), java_parser#MakePos(line, col)) 1504 | 1505 | let stat = s:GetStatement() 1506 | for t in matchs 1507 | if stat =~ t.name 1508 | let coor = java_parser#DecodePos(t.pos) 1509 | return var . '(' . (coor.line+1) . ',' . (coor.col) . ') ' . getline(coor.line+1) 1510 | endif 1511 | endfor 1512 | if len(matchs) > 0 1513 | let coor = java_parser#DecodePos(matchs[len(matchs)-1].pos) 1514 | return var . '(' . (coor.line+1) . ',' . (coor.col) . ') ' . getline(coor.line+1) 1515 | endif 1516 | return '' 1517 | endif 1518 | 1519 | " Type.this. 1520 | " new Type() 1521 | " new Type(param1, param2) 1522 | " this.field 1523 | " super.field 1524 | 1525 | let s:log = [] 1526 | 1527 | 1528 | " It may be an imported class. 1529 | let imports = [] 1530 | for fqn in s:GetImports('imports_fqn') 1531 | if fqn =~# '\<' . var . '\>$' 1532 | call add(imports, fqn) 1533 | endif 1534 | endfor 1535 | if len(imports) > 1 1536 | echoerr 'Imports conflicts between ' . join(imports, ' and ') 1537 | endif 1538 | 1539 | 1540 | " Search in this buffer 1541 | let matchs = s:SearchNameInAST(javacomplete#parse(), var, java_parser#MakePos(line, col), 1) 1542 | 1543 | 1544 | let hint = var . ' ' 1545 | if !empty(matchs) 1546 | let tree = matchs[len(matchs)-1] 1547 | let coor = java_parser#DecodePos(tree.pos) 1548 | let hint .= '(' . (coor.line+1) . ',' . (coor.col) . ') ' 1549 | let hint .= getline(coor.line+1) "string(tree) 1550 | else 1551 | for fqn in imports 1552 | let ci = s:DoGetClassInfo(fqn) 1553 | if !empty(ci) 1554 | let hint .= ' ' . fqn 1555 | endif 1556 | " TODO: get javadoc 1557 | endfor 1558 | 1559 | endif 1560 | return hint 1561 | endfu 1562 | 1563 | 1564 | " java {{{1 1565 | 1566 | fu! s:IsBuiltinType(name) 1567 | return index(s:PRIMITIVE_TYPES, a:name) >= 0 1568 | endfu 1569 | 1570 | fu! s:IsKeyword(name) 1571 | return index(s:KEYWORDS, a:name) >= 0 1572 | endfu 1573 | 1574 | fu! s:HasKeyword(name) 1575 | return a:name =~# s:RE_KEYWORDS 1576 | endfu 1577 | 1578 | fu! s:TailOfQN(qn) 1579 | return a:qn[strridx(a:qn, '.')+1:] 1580 | endfu 1581 | 1582 | " options {{{1 1583 | " Methods to search declaration {{{2 1584 | " 1 - by builtin searchdecl() 1585 | " 2 - by special Searchdecl() 1586 | " 4 - by java_parser 1587 | fu! javacomplete#GetSearchdeclMethod() 1588 | if &ft == 'jsp' 1589 | return 1 1590 | endif 1591 | return exists('s:searchdecl') ? s:searchdecl : 4 1592 | endfu 1593 | 1594 | fu! javacomplete#SetSearchdeclMethod(method) 1595 | let s:searchdecl = a:method 1596 | endfu 1597 | 1598 | " JDK1.1 {{{2 1599 | fu! javacomplete#UseJDK11() 1600 | let s:isjdk11 = 1 1601 | endfu 1602 | 1603 | " java compiler {{{2 1604 | fu! javacomplete#GetCompiler() 1605 | return exists('s:compiler') && s:compiler !~ '^\s*$' ? s:compiler : 'javac' 1606 | endfu 1607 | 1608 | fu! javacomplete#SetCompiler(compiler) 1609 | let s:compiler = a:compiler 1610 | endfu 1611 | 1612 | " jvm launcher {{{2 1613 | fu! javacomplete#GetJVMLauncher() 1614 | return exists('s:interpreter') && s:interpreter !~ '^\s*$' ? s:interpreter : 'java' 1615 | endfu 1616 | 1617 | fu! javacomplete#SetJVMLauncher(interpreter) 1618 | if javacomplete#GetJVMLauncher() != a:interpreter 1619 | let s:cache = {} 1620 | endif 1621 | let s:interpreter = a:interpreter 1622 | endfu 1623 | 1624 | " sourcepath {{{2 1625 | fu! javacomplete#AddSourcePath(s) 1626 | if !isdirectory(a:s) 1627 | echoerr 'invalid source path: ' . a:s 1628 | return 1629 | endif 1630 | let path = fnamemodify(a:s, ':p:h') 1631 | if !exists('s:sourcepath') 1632 | let s:sourcepath = [path] 1633 | elseif index(s:sourcepath, path) == -1 1634 | call add(s:sourcepath, path) 1635 | endif 1636 | endfu 1637 | 1638 | fu! javacomplete#DelSourcePath(s) 1639 | if !exists('s:sourcepath') || !isdirectory(a:s)| return | endif 1640 | let idx = index(s:sourcepath, a:s) 1641 | if idx != -1 1642 | call remove(s:sourcepath, idx) 1643 | endif 1644 | endfu 1645 | 1646 | fu! javacomplete#SetSourcePath(s) 1647 | let paths = type(a:s) == type([]) ? a:s : split(a:s, javacomplete#GetClassPathSep()) 1648 | let s:sourcepath = [] 1649 | for path in paths 1650 | if isdirectory(path) 1651 | call add(s:sourcepath, fnamemodify(path, ':p:h')) 1652 | endif 1653 | endfor 1654 | endfu 1655 | 1656 | " return the sourcepath. Given argument, add current path or default package root path 1657 | " NOTE: Avoid path duplicate, otherwise globpath() will return duplicate result. 1658 | fu! javacomplete#GetSourcePath(...) 1659 | return join(s:GetSourceDirs(a:0 > 0 && a:1 ? expand('%:p') : ''), s:PATH_SEP) 1660 | endfu 1661 | 1662 | fu! s:GetSourceDirs(filepath, ...) 1663 | let dirs = exists('s:sourcepath') ? s:sourcepath : [] 1664 | 1665 | if !empty(a:filepath) 1666 | let filepath = fnamemodify(a:filepath, ':p:h') 1667 | 1668 | " get source path according to file path and package name 1669 | let packageName = a:0 > 0 ? a:1 : s:GetPackageName() 1670 | if packageName != '' 1671 | let path = fnamemodify(substitute(filepath, packageName, '', 'g'), ':p:h') 1672 | if index(dirs, path) < 0 1673 | call add(dirs, path) 1674 | endif 1675 | endif 1676 | 1677 | " Consider current path as a sourcepath 1678 | if index(dirs, filepath) < 0 1679 | call add(dirs, filepath) 1680 | endif 1681 | endif 1682 | return dirs 1683 | endfu 1684 | 1685 | " classpath {{{2 1686 | fu! javacomplete#AddClassPath(s) 1687 | if !isdirectory(a:s) 1688 | echoerr 'invalid classpath: ' . a:s 1689 | return 1690 | endif 1691 | 1692 | if !exists('s:classpath') 1693 | let s:classpath = [a:s] 1694 | elseif index(s:classpath, a:s) == -1 1695 | call add(s:classpath, a:s) 1696 | endif 1697 | let s:cache = {} 1698 | endfu 1699 | 1700 | fu! javacomplete#DelClassPath(s) 1701 | if !exists('s:classpath') | return | endif 1702 | let idx = index(s:classpath, a:s) 1703 | if idx != -1 1704 | call remove(s:classpath, idx) 1705 | endif 1706 | endfu 1707 | 1708 | fu! javacomplete#SetClassPath(s) 1709 | if type(a:s) == type("") 1710 | let s:classpath = split(a:s, javacomplete#GetClassPathSep()) 1711 | elseif type(a:s) == type([]) 1712 | let s:classpath = a:s 1713 | endif 1714 | let s:cache = {} 1715 | endfu 1716 | 1717 | fu! javacomplete#GetClassPathSep() 1718 | return s:PATH_SEP 1719 | endfu 1720 | 1721 | fu! javacomplete#GetClassPath() 1722 | return exists('s:classpath') ? join(s:classpath, javacomplete#GetClassPathSep()) : '' 1723 | endfu 1724 | 1725 | " s:GetClassPath() {{{2 1726 | fu! s:GetClassPath() 1727 | let path = s:GetJavaCompleteClassPath() . javacomplete#GetClassPathSep() 1728 | 1729 | if &ft == 'jsp' 1730 | let path .= s:GetClassPathOfJsp() 1731 | endif 1732 | 1733 | if exists('b:classpath') && b:classpath !~ '^\s*$' 1734 | return path . b:classpath 1735 | endif 1736 | 1737 | if exists('s:classpath') 1738 | return path . javacomplete#GetClassPath() 1739 | endif 1740 | 1741 | if exists('g:java_classpath') && g:java_classpath !~ '^\s*$' 1742 | return path . g:java_classpath 1743 | endif 1744 | 1745 | return path . $CLASSPATH 1746 | endfu 1747 | 1748 | fu! s:GetJavaCompleteClassPath() 1749 | " remove *.class from wildignore if it exists, so that globpath doesn't ignore Reflection.class 1750 | " vim versions >= 702 can add the 1 flag to globpath which ignores '*.class" in wildingore 1751 | let has_class = 0 1752 | if &wildignore =~# "*.class" 1753 | set wildignore-=*.class 1754 | let has_class = 1 1755 | endif 1756 | 1757 | let classfile = globpath(&rtp, 'autoload/Reflection.class') 1758 | if classfile == '' 1759 | let classfile = globpath($HOME, 'Reflection.class') 1760 | endif 1761 | if classfile == '' 1762 | " try to find source file and compile to $HOME 1763 | let srcfile = globpath(&rtp, 'autoload/Reflection.java') 1764 | if srcfile != '' 1765 | exe '!' . javacomplete#GetCompiler() . ' -d "' . $HOME . '" "' . srcfile . '"' 1766 | let classfile = globpath($HOME, 'Reflection.class') 1767 | if classfile == '' 1768 | echo srcfile . ' can not be compiled. Please check it' 1769 | endif 1770 | else 1771 | echo 'No Reflection.class found in $HOME or any autoload directory of the &rtp. And no Reflection.java found in any autoload directory of the &rtp to compile.' 1772 | endif 1773 | endif 1774 | 1775 | " add *.class to wildignore if it existed before 1776 | if has_class == 1 1777 | set wildignore+=*.class 1778 | endif 1779 | 1780 | return fnamemodify(classfile, ':p:h') 1781 | endfu 1782 | 1783 | fu! s:GetClassPathOfJsp() 1784 | if exists('b:classpath_jsp') 1785 | return b:classpath_jsp 1786 | endif 1787 | 1788 | let b:classpath_jsp = '' 1789 | let path = expand('%:p:h') 1790 | while 1 1791 | if isdirectory(path . '/WEB-INF' ) 1792 | if isdirectory(path . '/WEB-INF/classes') 1793 | let b:classpath_jsp .= s:PATH_SEP . path . '/WEB-INF/classes' 1794 | endif 1795 | if isdirectory(path . '/WEB-INF/lib') 1796 | let libs = globpath(path . '/WEB-INF/lib', '*.jar') 1797 | if libs != '' 1798 | let b:classpath_jsp .= s:PATH_SEP . substitute(libs, "\n", s:PATH_SEP, 'g') 1799 | endif 1800 | endif 1801 | return b:classpath_jsp 1802 | endif 1803 | 1804 | let prev = path 1805 | let path = fnamemodify(path, ":p:h:h") 1806 | if path == prev 1807 | break 1808 | endif 1809 | endwhile 1810 | return '' 1811 | endfu 1812 | 1813 | " return only classpath which are directories 1814 | fu! s:GetClassDirs() 1815 | let dirs = [] 1816 | for path in split(s:GetClassPath(), s:PATH_SEP) 1817 | if isdirectory(path) 1818 | call add(dirs, fnamemodify(path, ':p:h')) 1819 | endif 1820 | endfor 1821 | return dirs 1822 | endfu 1823 | 1824 | " s:GetPackageName() {{{2 1825 | fu! s:GetPackageName() 1826 | let lnum_old = line('.') 1827 | let col_old = col('.') 1828 | 1829 | call cursor(1, 1) 1830 | let lnum = search('^\s*package[ \t\r\n]\+\([a-zA-Z][a-zA-Z0-9.]*\);', 'w') 1831 | let packageName = substitute(getline(lnum), '^\s*package\s\+\([a-zA-Z][a-zA-Z0-9.]*\);', '\1', '') 1832 | 1833 | call cursor(lnum_old, col_old) 1834 | return packageName 1835 | endfu 1836 | 1837 | fu! s:IsStatic(modifier) 1838 | return a:modifier[strlen(a:modifier)-4] 1839 | endfu 1840 | 1841 | " utilities {{{1 1842 | " Convert a file name into the unique form. 1843 | " Similar with fnamemodify(). NOTE that ':gs' should not be used. 1844 | fu! s:fnamecanonize(fname, mods) 1845 | return fnamemodify(a:fname, a:mods . ':gs?[\\/]\+?/?') 1846 | endfu 1847 | 1848 | " Similar with filter(), but returns a new list instead of operating in-place. 1849 | " `item` has the value of the current item. 1850 | fu! s:filter(expr, string) 1851 | if type(a:expr) == type([]) 1852 | let result = [] 1853 | for item in a:expr 1854 | if eval(a:string) 1855 | call add(result, item) 1856 | endif 1857 | endfor 1858 | return result 1859 | else 1860 | let result = {} 1861 | for item in items(a:expr) 1862 | if eval(a:string) 1863 | let result[item[0]] = item[1] 1864 | endif 1865 | endfor 1866 | return result 1867 | endif 1868 | endfu 1869 | 1870 | fu! s:Index(list, expr, key) 1871 | let i = 0 1872 | while i < len(a:list) 1873 | if get(a:list[i], a:key, '') == a:expr 1874 | return i 1875 | endif 1876 | let i += 1 1877 | endwhile 1878 | return -1 1879 | endfu 1880 | 1881 | fu! s:Match(list, expr, key) 1882 | let i = 0 1883 | while i < len(a:list) 1884 | if get(a:list[i], a:key, '') =~ a:expr 1885 | return i 1886 | endif 1887 | let i += 1 1888 | endwhile 1889 | return -1 1890 | endfu 1891 | 1892 | fu! s:KeepCursor(cmd) 1893 | let lnum_old = line('.') 1894 | let col_old = col('.') 1895 | exe a:cmd 1896 | call cursor(lnum_old, col_old) 1897 | endfu 1898 | 1899 | fu! s:InCommentOrLiteral(line, col) 1900 | if has("syntax") && &ft != 'jsp' 1901 | return synIDattr(synID(a:line, a:col, 1), "name") =~? '\(Comment\|String\|Character\)' 1902 | endif 1903 | endfu 1904 | 1905 | function! s:InComment(line, col) 1906 | if has("syntax") && &ft != 'jsp' 1907 | return synIDattr(synID(a:line, a:col, 1), "name") =~? 'comment' 1908 | endif 1909 | " if getline(a:line) =~ '\s*\*' 1910 | " return 1 1911 | " endif 1912 | " let idx = strridx(getline(a:line), '//') 1913 | " if idx >= 0 && idx < a:col 1914 | " return 1 1915 | " endif 1916 | " return 0 1917 | endfunction 1918 | 1919 | " set string literal empty, remove comments, trim begining or ending spaces 1920 | " test case: ' sb. /* block comment*/ append( "stringliteral" ) // comment ' 1921 | function! s:Prune(str, ...) 1922 | if a:str =~ '^\s*$' | return '' | endif 1923 | 1924 | let str = substitute(a:str, '"\(\\\(["\\''ntbrf]\)\|[^"]\)*"', '""', 'g') 1925 | let str = substitute(str, '\/\/.*', '', 'g') 1926 | let str = s:RemoveBlockComments(str) 1927 | return a:0 > 0 ? str : str . ' ' 1928 | endfunction 1929 | 1930 | " Given argument, replace block comments with spaces of same number 1931 | fu! s:RemoveBlockComments(str, ...) 1932 | let result = a:str 1933 | let ib = match(result, '\/\*') 1934 | let ie = match(result, '\*\/') 1935 | while ib != -1 && ie != -1 && ib < ie 1936 | let result = strpart(result, 0, ib) . (a:0 == 0 ? ' ' : repeat(' ', ie-ib+2)) . result[ie+2: ] 1937 | let ib = match(result, '\/\*') 1938 | let ie = match(result, '\*\/') 1939 | endwhile 1940 | return result 1941 | endfu 1942 | 1943 | fu! s:Trim(str) 1944 | let str = substitute(a:str, '^\s*', '', '') 1945 | return substitute(str, '\s*$', '', '') 1946 | endfu 1947 | 1948 | fu! s:SplitAt(str, index) 1949 | return [strpart(a:str, 0, a:index+1), strpart(a:str, a:index+1)] 1950 | endfu 1951 | 1952 | " TODO: search pair used in string, like 1953 | " 'create(ao.fox("("), new String).foo().' 1954 | function! s:GetMatchedIndexEx(str, idx, one, another) 1955 | let pos = a:idx 1956 | while 0 <= pos && pos < len(a:str) 1957 | let pos = match(a:str, '['. a:one . escape(a:another, ']') .']', pos+1) 1958 | if pos != -1 1959 | if a:str[pos] == a:one 1960 | let pos = s:GetMatchedIndexEx(a:str, pos, a:one, a:another) 1961 | elseif a:str[pos] == a:another 1962 | break 1963 | endif 1964 | endif 1965 | endwhile 1966 | return 0 <= pos && pos < len(a:str) ? pos : -3 1967 | endfunction 1968 | 1969 | function! s:SearchPairBackward(str, idx, one, another) 1970 | let idx = a:idx 1971 | let n = 0 1972 | while idx >= 0 1973 | let idx -= 1 1974 | if a:str[idx] == a:one 1975 | if n == 0 1976 | break 1977 | endif 1978 | let n -= 1 1979 | elseif a:str[idx] == a:another " nested 1980 | let n += 1 1981 | endif 1982 | endwhile 1983 | return idx 1984 | endfunction 1985 | 1986 | fu! s:CountDims(str) 1987 | if match(a:str, '[[\]]') == -1 1988 | return 0 1989 | endif 1990 | 1991 | " int[] -> [I, String[] -> 1992 | let dims = len(matchstr(a:str, '^[\+')) 1993 | if dims == 0 1994 | let idx = len(a:str)-1 1995 | while idx >= 0 && a:str[idx] == ']' 1996 | let dims += 1 1997 | let idx = s:SearchPairBackward(a:str, idx, '[', ']')-1 1998 | endwhile 1999 | endif 2000 | return dims 2001 | endfu 2002 | 2003 | fu! s:GotoUpperBracket() 2004 | let searched = 0 2005 | while (!searched) 2006 | call search('[{}]', 'bW') 2007 | if getline('.')[col('.')-1] == '}' 2008 | normal % 2009 | else 2010 | let searched = 1 2011 | endif 2012 | endwhile 2013 | endfu 2014 | 2015 | " Improve recognition of variable declaration using my version of searchdecl() for accuracy reason. 2016 | " TODO: 2017 | fu! s:Searchdecl(name, ...) 2018 | let global = a:0 > 0 ? a:1 : 0 2019 | let thisblock = a:0 > 1 ? a:2 : 1 2020 | 2021 | call search('\<' . a:name . '\>', 'bW') 2022 | let lnum_old = line('.') 2023 | let col_old = col('.') 2024 | 2025 | call s:GotoUpperBracket() 2026 | let lnum_bracket = line('.') 2027 | let col_bracket = col('.') 2028 | call search('\<' . a:name . '\>', 'W', lnum_old) 2029 | if line('.') != lnum_old || col('.') != col_old 2030 | return 0 2031 | endif 2032 | 2033 | " search globally 2034 | if global 2035 | call cursor(lnum_bracket, col_bracket) 2036 | " search backward 2037 | while (1) 2038 | if search('\([{}]\|\<' . a:name . '\>\)', 'bW') == 0 2039 | break 2040 | endif 2041 | if s:InComment(line('.'), col('.')) "|| s:InStringLiteral() 2042 | continue 2043 | endif 2044 | let cword = expand('') 2045 | if cword == a:name 2046 | return 0 2047 | endif 2048 | if getline('.')[col('.')-1] == '}' 2049 | normal % 2050 | endif 2051 | endwhile 2052 | 2053 | call cursor(lnum_old, col_old) 2054 | " search forward 2055 | call search('[{};]', 'W') 2056 | while (1) 2057 | if search('\([{}]\|\<' . a:name . '\>\)', 'W') == 0 2058 | break 2059 | endif 2060 | if s:InComment(line('.'), col('.')) "|| s:InStringLiteral() 2061 | continue 2062 | endif 2063 | let cword = expand('') 2064 | if cword == a:name 2065 | return 0 2066 | endif 2067 | if getline('.')[col('.')-1] == '{' 2068 | normal % 2069 | endif 2070 | endwhile 2071 | endif 2072 | return 1 2073 | endfu 2074 | "nmap :call Searchdecl(expand('')) 2075 | 2076 | fu! javacomplete#Exe(cmd) 2077 | exe a:cmd 2078 | endfu 2079 | 2080 | " cache utilities {{{1 2081 | 2082 | " key of s:files for current buffer. It may be the full path of current file or the bufnr of unnamed buffer, and is updated when BufEnter, BufLeave. 2083 | fu! s:GetCurrentFileKey() 2084 | return has("autocmd") ? s:curfilekey : empty(expand('%')) ? bufnr('%') : expand('%:p') 2085 | endfu 2086 | 2087 | fu! s:SetCurrentFileKey() 2088 | let s:curfilekey = empty(expand('%')) ? bufnr('%') : expand('%:p') 2089 | endfu 2090 | 2091 | call s:SetCurrentFileKey() 2092 | if has("autocmd") 2093 | autocmd BufEnter *.java call s:SetCurrentFileKey() 2094 | autocmd FileType java call s:SetCurrentFileKey() 2095 | endif 2096 | 2097 | 2098 | " Log utilities {{{1 2099 | fu! s:WatchVariant(variant) 2100 | "echoerr a:variant 2101 | endfu 2102 | 2103 | " level 2104 | " 5 off/fatal 2105 | " 4 error 2106 | " 3 warn 2107 | " 2 info 2108 | " 1 debug 2109 | " 0 trace 2110 | fu! javacomplete#SetLogLevel(level) 2111 | let s:loglevel = a:level 2112 | endfu 2113 | 2114 | fu! javacomplete#GetLogLevel() 2115 | return exists('s:loglevel') ? s:loglevel : 3 2116 | endfu 2117 | 2118 | fu! javacomplete#GetLogContent() 2119 | return s:log 2120 | endfu 2121 | 2122 | fu! s:Trace(msg) 2123 | call s:Log(0, a:msg) 2124 | endfu 2125 | 2126 | fu! s:Debug(msg) 2127 | call s:Log(1, a:msg) 2128 | endfu 2129 | 2130 | fu! s:Info(msg) 2131 | call s:Log(2, a:msg) 2132 | endfu 2133 | 2134 | fu! s:Log(level, key, ...) 2135 | if a:level >= javacomplete#GetLogLevel() 2136 | echo a:key 2137 | call add(s:log, a:key) 2138 | endif 2139 | endfu 2140 | 2141 | fu! s:System(cmd, caller) 2142 | call s:WatchVariant(a:cmd) 2143 | let t = reltime() 2144 | let res = system(a:cmd) 2145 | call s:Debug(reltimestr(reltime(t)) . 's to exec "' . a:cmd . '" by ' . a:caller) 2146 | return res 2147 | endfu 2148 | 2149 | " functions to get information {{{1 2150 | " utilities {{{2 2151 | fu! s:MemberCompare(m1, m2) 2152 | return a:m1['n'] == a:m2['n'] ? 0 : a:m1['n'] > a:m2['n'] ? 1 : -1 2153 | endfu 2154 | 2155 | fu! s:Sort(ci) 2156 | let ci = a:ci 2157 | if has_key(ci, 'fields') 2158 | call sort(ci['fields'], 's:MemberCompare') 2159 | endif 2160 | if has_key(ci, 'methods') 2161 | call sort(ci['methods'], 's:MemberCompare') 2162 | endif 2163 | return ci 2164 | endfu 2165 | 2166 | " Function to run Reflection {{{2 2167 | fu! s:RunReflection(option, args, log) 2168 | let classpath = '' 2169 | if !exists('s:isjdk11') 2170 | let classpath = ' -classpath "' . s:GetClassPath() . '" ' 2171 | endif 2172 | 2173 | let cmd = javacomplete#GetJVMLauncher() . classpath . ' Reflection ' . a:option . ' "' . a:args . '"' 2174 | return s:System(cmd, a:log) 2175 | endfu 2176 | " class information {{{2 2177 | 2178 | 2179 | " The standard search order of a FQN is as follows: 2180 | " 1. a file-name toplevel type or static member type accessed by the file-name type declared in source files 2181 | " 2. other types declared in source files 2182 | " 3. an accessible loadable type. 2183 | " parameters: 2184 | " fqns - list of fqn 2185 | " srcpaths - a comma-separated list of directory names. 2186 | " a:1 - search all. 2187 | " return a dict of fqn -> type info 2188 | " precondition: 2189 | " NOTE: call expand() to convert path to standard form 2190 | fu! s:DoGetTypeInfoForFQN(fqns, srcpath, ...) 2191 | if empty(a:fqns) || empty(a:srcpath) 2192 | return 2193 | endif 2194 | 2195 | " 1 2196 | let files = {} " fqn -> java file path 2197 | for fqn in a:fqns 2198 | " toplevel type 2199 | let filepath = globpath(a:srcpath, substitute(fqn, '\.', '/', 'g') . '.java') 2200 | if filepath != '' 2201 | let files[fqn] = expand(filepath) 2202 | 2203 | " nested type 2204 | elseif stridx(fqn, '.') >= 0 2205 | let idents = split(fqn, '\.') 2206 | let i = len(idents)-2 2207 | while i >= 0 2208 | let filepath = globpath(a:srcpath, join(idents[:i], '/') . '.java') 2209 | if filepath != '' 2210 | let files[fqn] = expand(filepath) 2211 | break 2212 | endif 2213 | let i -= 1 2214 | endwhile 2215 | endif 2216 | endfor 2217 | 2218 | 2219 | " 2 2220 | let dirs = {} " dir.idents -> names of nested type 2221 | " dir.qfitems -> items of quick fix 2222 | " dir.fqn -> fqn 2223 | for fqn in a:fqns 2224 | if !has_key(files, fqn) 2225 | for path in split(a:srcpath, ',') 2226 | let idents = split(fqn, '\.') 2227 | let i = len(idents)-2 2228 | while i >= 0 2229 | let dirpath = path . '/' . join(idents[:i], '/') 2230 | " it is a package 2231 | if isdirectory(dirpath) 2232 | let dirs[fnamemodify(dirpath, ':p:h:gs?[\\/]\+?/?')] = {'fqn': fqn, 'idents': idents[i+1:]} 2233 | break 2234 | endif 2235 | let i -= 1 2236 | endwhile 2237 | endfor 2238 | endif 2239 | endfor 2240 | 2241 | if !empty(dirs) 2242 | let items = {} " dir -> items of quick fix 2243 | 2244 | let filepatterns = '' 2245 | for dirpath in keys(dirs) 2246 | let filepatterns .= escape(dirpath, ' \') . '/*.java ' 2247 | endfor 2248 | 2249 | let cwd = fnamemodify(expand('%:p:h'), ':p:h:gs?[\\/]\+?/?') 2250 | exe 'vimgrep /\s*' . s:RE_TYPE_DECL . '/jg ' . filepatterns 2251 | for item in getqflist() 2252 | if item.text !~ '^\s*\*\s\+' 2253 | let text = matchstr(s:Prune(item.text, -1), '\s*' . s:RE_TYPE_DECL) 2254 | if text != '' 2255 | let subs = split(substitute(text, '\s*' . s:RE_TYPE_DECL, '\1;\2;\3', ''), ';', 1) 2256 | let dirpath = fnamemodify(bufname(item.bufnr), ':p:h:gs?[\\/]\+?/?') 2257 | let idents = dirs[dirpath].idents 2258 | if index(idents, subs[2]) >= 0 && (subs[0] =~ '\C\' || dirpath == cwd) " FIXME? 2259 | let item.subs = subs 2260 | let dirs[dirpath].qfitems = get(dirs[dirpath], 'qfitems', []) + [item] 2261 | endif 2262 | endif 2263 | endif 2264 | endfor 2265 | 2266 | for dirpath in keys(dirs) 2267 | " a. names of nested type must be existed in the same file 2268 | " PackageName.NonFileNameTypeName.NestedType.NestedNestedType 2269 | let qfitems = get(dirs[dirpath], 'qfitems', []) 2270 | let nr = 0 2271 | for ident in dirs[dirpath].idents 2272 | for item in qfitems 2273 | if item.subs[2] == ident 2274 | let nr += 1 2275 | endif 2276 | endfor 2277 | endfor 2278 | if nr == len(dirs[dirpath].idents) 2279 | " b. TODO: Check whether one enclosed another is correct 2280 | let files[fqn] = expand(bufname(qfitems[0].bufnr)) 2281 | endif 2282 | endfor 2283 | endif 2284 | 2285 | 2286 | call s:Info('FQN1&2: ' . string(keys(files))) 2287 | for fqn in keys(files) 2288 | if !has_key(s:cache, fqn) || get(get(s:files, files[fqn], {}), 'modifiedtime', 0) != getftime(files[fqn]) 2289 | let ti = s:GetClassInfoFromSource(fqn[strridx(fqn, '.')+1:], files[fqn]) 2290 | if !empty(ti) 2291 | let s:cache[fqn] = s:Sort(ti) 2292 | endif 2293 | endif 2294 | if (a:0 == 0 || !a:1) 2295 | return 2296 | endif 2297 | endfor 2298 | 2299 | 2300 | " 3 2301 | let commalist = '' 2302 | for fqn in a:fqns 2303 | if has_key(s:cache, fqn) && (a:0 == 0 || !a:1) 2304 | return 2305 | else "if stridx(fqn, '.') >= 0 2306 | let commalist .= fqn . ',' 2307 | endif 2308 | endfor 2309 | if !empty(commalist) 2310 | let res = s:RunReflection('-E', commalist, 'DoGetTypeInfoForFQN in Batch') 2311 | if res =~ "^{'" 2312 | let dict = eval(res) 2313 | for key in keys(dict) 2314 | if !has_key(s:cache, key) 2315 | if type(dict[key]) == type({}) 2316 | let s:cache[key] = s:Sort(dict[key]) 2317 | elseif type(dict[key]) == type([]) 2318 | let s:cache[key] = sort(dict[key]) 2319 | endif 2320 | endif 2321 | endfor 2322 | endif 2323 | endif 2324 | endfu 2325 | 2326 | " a:1 filepath 2327 | " a:2 package name 2328 | fu! s:DoGetClassInfo(class, ...) 2329 | if has_key(s:cache, a:class) 2330 | return s:cache[a:class] 2331 | endif 2332 | 2333 | " array type: TypeName[] or '[I' or '[[Ljava.lang.String;' 2334 | if a:class[-1:] == ']' || a:class[0] == '[' 2335 | return s:ARRAY_TYPE_INFO 2336 | endif 2337 | 2338 | " either this or super is not qualified 2339 | if a:class == 'this' || a:class == 'super' 2340 | if &ft == 'jsp' 2341 | let ci = s:DoGetReflectionClassInfo('javax.servlet.jsp.HttpJspPage') 2342 | if a:class == 'this' 2343 | " TODO: search methods defined in <%! [declarations] %> 2344 | " search methods defined in other jsp files included 2345 | " avoid including self directly or indirectly 2346 | endif 2347 | return ci 2348 | endif 2349 | 2350 | call s:Info('A0. ' . a:class) 2351 | " this can be a local class or anonymous class as well as static type 2352 | let t = get(s:SearchTypeAt(javacomplete#parse(), java_parser#MakePos(line('.')-1, col('.')-1)), -1, {}) 2353 | if !empty(t) 2354 | " What will be returned for super? 2355 | " - the protected or public inherited fields and methods. No ctors. 2356 | " - the (public static) fields of interfaces. 2357 | " - the methods of the Object class. 2358 | " What will be returned for this? 2359 | " - besides the above, all fields and methods of current class. No ctors. 2360 | return s:Sort(s:Tree2ClassInfo(t)) 2361 | "return s:Sort(s:AddInheritedClassInfo(a:class == 'this' ? s:Tree2ClassInfo(t) : {}, t, 1)) 2362 | endif 2363 | 2364 | return {} 2365 | endif 2366 | 2367 | 2368 | if a:class !~ '^\s*' . s:RE_QUALID . '\s*$' || s:HasKeyword(a:class) 2369 | return {} 2370 | endif 2371 | 2372 | 2373 | let typename = substitute(a:class, '\s', '', 'g') 2374 | let filekey = a:0 > 0 ? a:1 : s:GetCurrentFileKey() 2375 | let packagename = a:0 > 1 ? a:2 : s:GetPackageName() 2376 | let srcpath = join(s:GetSourceDirs(a:0 > 0 && a:1 != bufnr('%') ? a:1 : expand('%:p'), packagename), ',') 2377 | 2378 | let names = split(typename, '\.') 2379 | " remove the package name if in the same packge 2380 | if len(names) > 1 2381 | if packagename == join(names[:-2], '.') 2382 | let names = names[-1:] 2383 | endif 2384 | endif 2385 | 2386 | " a FQN 2387 | if len(names) > 1 2388 | call s:DoGetTypeInfoForFQN([typename], srcpath) 2389 | let ci = get(s:cache, typename, {}) 2390 | if get(ci, 'tag', '') == 'CLASSDEF' 2391 | return s:cache[typename] 2392 | elseif get(ci, 'tag', '') == 'PACKAGE' 2393 | return {} 2394 | endif 2395 | endif 2396 | 2397 | 2398 | " The standard search order of a simple type name is as follows: 2399 | " 1. The current type including inherited types. 2400 | " 2. A nested type of the current type. 2401 | " 3. Explicitly named imported types (single type import). 2402 | " 4. Other types declared in the same package. Not only current directory. 2403 | " 5. Implicitly named imported types (import on demand). 2404 | 2405 | " 1 & 2. 2406 | " NOTE: inherited types are treated as normal 2407 | if filekey == s:GetCurrentFileKey() 2408 | let simplename = typename[strridx(typename, '.')+1:] 2409 | if s:FoundClassDeclaration(simplename) != 0 2410 | call s:Info('A1&2') 2411 | let ci = s:GetClassInfoFromSource(simplename, '%') 2412 | " do not cache it 2413 | if !empty(ci) 2414 | return ci 2415 | endif 2416 | endif 2417 | else 2418 | let ci = s:GetClassInfoFromSource(typename, filekey) 2419 | if !empty(ci) 2420 | return ci 2421 | endif 2422 | endif 2423 | 2424 | " 3. 2425 | " NOTE: PackageName.Ident, TypeName.Ident 2426 | let fqn = s:SearchSingleTypeImport(typename, s:GetImports('imports_fqn', filekey)) 2427 | if !empty(fqn) 2428 | call s:Info('A3') 2429 | call s:DoGetTypeInfoForFQN([fqn], srcpath) 2430 | let ti = get(s:cache, fqn, {}) 2431 | if get(ti, 'tag', '') != 'CLASSDEF' 2432 | " TODO: mark the wrong import declaration. 2433 | endif 2434 | return ti 2435 | endif 2436 | 2437 | " 4 & 5 2438 | " NOTE: Keeps the fqn of the same package first!! 2439 | call s:Info('A4&5') 2440 | let fqns = [empty(packagename) ? typename : packagename . '.' . typename] 2441 | for p in s:GetImports('imports_star', filekey) 2442 | call add(fqns, p . typename) 2443 | endfor 2444 | call s:DoGetTypeInfoForFQN(fqns, srcpath) 2445 | for fqn in fqns 2446 | if has_key(s:cache, fqn) 2447 | return get(s:cache[fqn], 'tag', '') == 'CLASSDEF' ? s:cache[fqn] : {} 2448 | endif 2449 | endfor 2450 | 2451 | return {} 2452 | endfu 2453 | 2454 | " Rules of overriding and hiding: 2455 | " 1. Fields cannot be overridden; they can only be hidden. 2456 | " In the subclass, the hidden field of superclass can no longer be accessed 2457 | " directly by its simple name. `super` or another reference must be used. 2458 | " 2. A method can be overriden only if it is accessible. 2459 | " When overriding methods, both the signature and return type must be the 2460 | " same as in the superclass. 2461 | " 3. Static members cannot be overridden; they can only be hidden 2462 | " -- whether a field or a method. But hiding static members has little effect, 2463 | " because static should be accessed via the name of its declaring class. 2464 | " Given optional argument, add protected, default (package) access, private members. 2465 | "fu! s:MergeClassInfo(ci, another, ...) 2466 | " if empty(a:another) | return a:ci | endif 2467 | " 2468 | " if empty(a:ci) 2469 | " let ci = copy(a:another) 2470 | "" if a:0 > 0 && a:1 2471 | "" call extend(ci.fields, get(a:another, 'declared_fields', [])) 2472 | "" call extend(ci.methods, get(a:another, 'declared_methods', [])) 2473 | "" endif 2474 | " return ci 2475 | " endif 2476 | " 2477 | " call extend(a:ci.methods, a:another.methods) 2478 | " 2479 | " for f in a:another.fields 2480 | " if s:Index(a:ci.fields, f.n, 'n') < 0 2481 | " call add(a:ci.fields, f) 2482 | " endif 2483 | " endfor 2484 | " return a:ci 2485 | "endfu 2486 | 2487 | 2488 | " Parameters: 2489 | " class the qualified class name 2490 | " Return: TClassInfo or {} when not found 2491 | " See ClassInfoFactory.getClassInfo() in insenvim. 2492 | function! s:DoGetReflectionClassInfo(fqn) 2493 | if !has_key(s:cache, a:fqn) 2494 | let res = s:RunReflection('-C', a:fqn, 's:DoGetReflectionClassInfo') 2495 | if res =~ '^{' 2496 | let s:cache[a:fqn] = s:Sort(eval(res)) 2497 | elseif res =~ '^[' 2498 | for type in eval(res) 2499 | if get(type, 'name', '') != '' 2500 | let s:cache[type.name] = s:Sort(type) 2501 | endif 2502 | endfor 2503 | else 2504 | let b:errormsg = res 2505 | endif 2506 | endif 2507 | return get(s:cache, a:fqn, {}) 2508 | endfunction 2509 | 2510 | fu! s:GetClassInfoFromSource(class, filename) 2511 | let ci = {} 2512 | if len(tagfiles()) > 0 2513 | let ci = s:DoGetClassInfoFromTags(a:class) 2514 | endif 2515 | 2516 | if empty(ci) 2517 | call s:Info('Use java_parser.vim to generate class information') 2518 | let unit = javacomplete#parse(a:filename) 2519 | let targetPos = a:filename == '%' ? java_parser#MakePos(line('.')-1, col('.')-1) : -1 2520 | for t in s:SearchTypeAt(unit, targetPos, 1) 2521 | if t.name == a:class 2522 | let t.filepath = a:filename == '%' ? s:GetCurrentFileKey() : expand(a:filename) 2523 | return s:Tree2ClassInfo(t) 2524 | "return s:AddInheritedClassInfo(s:Tree2ClassInfo(t), t) 2525 | endif 2526 | endfor 2527 | endif 2528 | return ci 2529 | endfu 2530 | 2531 | fu! s:Tree2ClassInfo(t) 2532 | let t = a:t 2533 | 2534 | " fill fields and methods 2535 | let t.fields = [] 2536 | let t.methods = [] 2537 | let t.ctors = [] 2538 | let t.classes = [] 2539 | for def in t.defs 2540 | if def.tag == 'METHODDEF' 2541 | call add(def.n == t.name ? t.ctors : t.methods, def) 2542 | elseif def.tag == 'VARDEF' 2543 | call add(t.fields, def) 2544 | elseif def.tag == 'CLASSDEF' 2545 | call add(t.classes, t.fqn . '.' . def.name) 2546 | endif 2547 | endfor 2548 | 2549 | " convert type name in extends to fqn for class defined in source files 2550 | if !has_key(a:t, 'classpath') && has_key(a:t, 'extends') 2551 | if has_key(a:t, 'filepath') && a:t.filepath != s:GetCurrentFileKey() 2552 | let filepath = a:t.filepath 2553 | let packagename = get(s:files[filepath].unit, 'package', '') 2554 | else 2555 | let filepath = expand('%:p') 2556 | let packagename = s:GetPackageName() 2557 | endif 2558 | 2559 | let extends = a:t.extends 2560 | let i = 0 2561 | while i < len(extends) 2562 | let ci = s:DoGetClassInfo(java_parser#type2Str(extends[i]), filepath, packagename) 2563 | if has_key(ci, 'fqn') 2564 | let extends[i] = ci.fqn 2565 | endif 2566 | let i += 1 2567 | endwhile 2568 | endif 2569 | 2570 | return t 2571 | endfu 2572 | 2573 | "fu! s:AddInheritedClassInfo(ci, t, ...) 2574 | " let ci = a:ci 2575 | " " add inherited fields and methods 2576 | " let list = [] 2577 | " for i in get(a:t, 'extends', []) 2578 | " call add(list, java_parser#type2Str(i)) 2579 | " endfor 2580 | " 2581 | " if has_key(a:t, 'filepath') && a:t.filepath != expand('%:p') 2582 | " let filepath = a:t.filepath 2583 | " let props = get(s:files, a:t.filepath, {}) 2584 | " let packagename = get(props.unit, 'package', '') 2585 | " else 2586 | " let filepath = expand('%:p') 2587 | " let packagename = s:GetPackageName() 2588 | " endif 2589 | " 2590 | " for id in list 2591 | " let ci = s:MergeClassInfo(ci, s:DoGetClassInfo(id, filepath, packagename), a:0 > 0 && a:1) 2592 | " endfor 2593 | " return ci 2594 | "endfu 2595 | 2596 | " To obtain information of the class in current file or current folder, or 2597 | " even in current project. 2598 | function! s:DoGetClassInfoFromTags(class) 2599 | " find tag of a:class declaration 2600 | let tags = taglist('^' . a:class) 2601 | let filename = '' 2602 | let cmd = '' 2603 | for tag in tags 2604 | if has_key(tag, 'kind') 2605 | if tag['kind'] == 'c' 2606 | let filename = tag['filename'] 2607 | let cmd = tag['cmd'] 2608 | break 2609 | endif 2610 | endif 2611 | endfor 2612 | 2613 | let tags = taglist('^' . (empty(b:incomplete) ? '.*' : b:incomplete) ) 2614 | if filename != '' 2615 | call filter(tags, "v:val['filename'] == '" . filename . "' && has_key(v:val, 'class') ? v:val['class'] == '" . a:class . "' : 1") 2616 | endif 2617 | 2618 | let ci = {'name': a:class} 2619 | " extends and implements 2620 | let ci['ctors'] = [] 2621 | let ci['fields'] = [] 2622 | let ci['methods'] = [] 2623 | 2624 | " members 2625 | for tag in tags 2626 | let member = {'n': tag['name']} 2627 | 2628 | " determine kind 2629 | let kind = 'm' 2630 | if has_key(tag, 'kind') 2631 | let kind = tag['kind'] 2632 | endif 2633 | 2634 | let cmd = tag['cmd'] 2635 | if cmd =~ '\' 2636 | let member['m'] = '1000' 2637 | else 2638 | let member['m'] = '' 2639 | endif 2640 | 2641 | let desc = substitute(cmd, '/^\s*', '', '') 2642 | let desc = substitute(desc, '\s*{\?\s*$/$', '', '') 2643 | 2644 | if kind == 'm' 2645 | " description 2646 | if cmd =~ '\' 2647 | let desc = substitute(desc, '\s\+static\s\+', ' ', '') 2648 | endif 2649 | let member['d'] = desc 2650 | 2651 | let member['p'] = '' 2652 | let member['r'] = '' 2653 | if tag['name'] == a:class 2654 | call add(ci['ctors'], member) 2655 | else 2656 | call add(ci['methods'], member) 2657 | endif 2658 | elseif kind == 'f' 2659 | let member['t'] = substitute(desc, '\([a-zA-Z0-9_[\]]\)\s\+\<' . tag['name'] . '\>.*$', '\1', '') 2660 | call add(ci['fields'], member) 2661 | endif 2662 | endfor 2663 | return ci 2664 | endfu 2665 | 2666 | " package information {{{2 2667 | 2668 | fu! s:DoGetInfoByReflection(class, option) 2669 | if has_key(s:cache, a:class) 2670 | return s:cache[a:class] 2671 | endif 2672 | 2673 | let res = s:RunReflection(a:option, a:class, 's:DoGetInfoByReflection') 2674 | if res =~ '^[{\[]' 2675 | let v = eval(res) 2676 | if type(v) == type([]) 2677 | let s:cache[a:class] = sort(v) 2678 | elseif type(v) == type({}) 2679 | if get(v, 'tag', '') =~# '^\(PACKAGE\|CLASSDEF\)$' 2680 | let s:cache[a:class] = v 2681 | else 2682 | call extend(s:cache, v, 'force') 2683 | endif 2684 | endif 2685 | unlet v 2686 | else 2687 | let b:errormsg = res 2688 | endif 2689 | 2690 | return get(s:cache, a:class, {}) 2691 | endfu 2692 | 2693 | " search in members {{{2 2694 | " TODO: what about default access? 2695 | " public for all 2696 | " protected for this or super 2697 | " private for this 2698 | fu! s:CanAccess(mods, kind) 2699 | return (a:mods[-4:-4] || a:kind/10 == 0) 2700 | \ && (a:kind == 1 || a:mods[-1:] 2701 | \ || (a:mods[-3:-3] && (a:kind == 1 || a:kind == 2)) 2702 | \ || (a:mods[-2:-2] && a:kind == 1)) 2703 | endfu 2704 | 2705 | fu! s:SearchMember(ci, name, fullmatch, kind, returnAll, memberkind, ...) 2706 | let result = [[], [], []] 2707 | 2708 | if a:kind != 13 2709 | for m in (a:0 > 0 && a:1 ? [] : get(a:ci, 'fields', [])) + ((a:kind == 1 || a:kind == 2) ? get(a:ci, 'declared_fields', []) : []) 2710 | if empty(a:name) || (a:fullmatch ? m.n ==# a:name : m.n =~# '^' . a:name) 2711 | if s:CanAccess(m.m, a:kind) 2712 | call add(result[2], m) 2713 | endif 2714 | endif 2715 | endfor 2716 | 2717 | for m in (a:0 > 0 && a:1 ? [] : get(a:ci, 'methods', [])) + ((a:kind == 1 || a:kind == 2) ? get(a:ci, 'declared_methods', []) : []) 2718 | if empty(a:name) || (a:fullmatch ? m.n ==# a:name : m.n =~# '^' . a:name) 2719 | if s:CanAccess(m.m, a:kind) 2720 | call add(result[1], m) 2721 | endif 2722 | endif 2723 | endfor 2724 | endif 2725 | 2726 | if a:kind/10 != 0 2727 | let types = get(a:ci, 'classes', []) 2728 | for t in types 2729 | if empty(a:name) || (a:fullmatch ? t[strridx(t, '.')+1:] ==# a:name : t[strridx(t, '.')+1:] =~# '^' . a:name) 2730 | if !has_key(s:cache, t) || !has_key(s:cache[t], 'flags') || a:kind == 1 || s:cache[t].flags[-1:] 2731 | call add(result[0], t) 2732 | endif 2733 | endif 2734 | endfor 2735 | endif 2736 | 2737 | " key `classpath` indicates it is a loaded class from classpath 2738 | " All public members of a loaded class are stored in current ci 2739 | if !has_key(a:ci, 'classpath') || (a:kind == 1 || a:kind == 2) 2740 | for i in get(a:ci, 'extends', []) 2741 | let ci = s:DoGetClassInfo(java_parser#type2Str(i)) 2742 | let members = s:SearchMember(ci, a:name, a:fullmatch, a:kind == 1 ? 2 : a:kind, a:returnAll, a:memberkind) 2743 | let result[0] += members[0] 2744 | let result[1] += members[1] 2745 | let result[2] += members[2] 2746 | endfor 2747 | endif 2748 | return result 2749 | endfu 2750 | 2751 | 2752 | " generate member list {{{2 2753 | 2754 | fu! s:DoGetFieldList(fields) 2755 | let s = '' 2756 | for field in a:fields 2757 | let s .= "{'kind':'" . (s:IsStatic(field.m) ? "F" : "f") . "','word':'" . field.n . "','menu':'" . field.t . "','dup':1}," 2758 | endfor 2759 | return s 2760 | endfu 2761 | 2762 | fu! s:DoGetMethodList(methods, ...) 2763 | let paren = a:0 == 0 || !a:1 ? '(' : '' 2764 | let s = '' 2765 | for method in a:methods 2766 | let s .= "{'kind':'" . (s:IsStatic(method.m) ? "M" : "m") . "','word':'" . method.n . paren . "','abbr':'" . method.n . "()','menu':'" . method.d . "','dup':'1'}," 2767 | endfor 2768 | return s 2769 | endfu 2770 | 2771 | " kind: 2772 | " 0 - for instance, 1 - this, 2 - super, 3 - class, 4 - array, 5 - method result, 6 - primitive type 2773 | " 11 - for type, with `class` and static member and nested types. 2774 | " 12 - for import static, no lparen for static methods 2775 | " 13 - for import or extends or implements, only nested types 2776 | " 20 - for package 2777 | fu! s:DoGetMemberList(ci, kind) 2778 | if type(a:ci) != type({}) || a:ci == {} 2779 | return [] 2780 | endif 2781 | 2782 | let s = a:kind == 11 ? "{'kind': 'C', 'word': 'class', 'menu': 'Class'}," : '' 2783 | 2784 | let members = s:SearchMember(a:ci, '', 1, a:kind, 1, 0, a:kind == 2) 2785 | 2786 | " add accessible member types 2787 | if a:kind / 10 != 0 2788 | " Use dup here for member type can share name with field. 2789 | for class in members[0] 2790 | "for class in get(a:ci, 'classes', []) 2791 | let v = get(s:cache, class, {}) 2792 | if v == {} || v.flags[-1:] 2793 | let s .= "{'kind': 'C', 'word': '" . substitute(class, a:ci.name . '\.', '\1', '') . "','dup':1}," 2794 | endif 2795 | endfor 2796 | endif 2797 | 2798 | if a:kind != 13 2799 | let fieldlist = [] 2800 | let sfieldlist = [] 2801 | for field in members[2] 2802 | "for field in get(a:ci, 'fields', []) 2803 | if s:IsStatic(field['m']) 2804 | call add(sfieldlist, field) 2805 | elseif a:kind / 10 == 0 2806 | call add(fieldlist, field) 2807 | endif 2808 | endfor 2809 | 2810 | let methodlist = [] 2811 | let smethodlist = [] 2812 | for method in members[1] 2813 | if s:IsStatic(method['m']) 2814 | call add(smethodlist, method) 2815 | elseif a:kind / 10 == 0 2816 | call add(methodlist, method) 2817 | endif 2818 | endfor 2819 | 2820 | if a:kind / 10 == 0 2821 | let s .= s:DoGetFieldList(fieldlist) 2822 | let s .= s:DoGetMethodList(methodlist) 2823 | endif 2824 | let s .= s:DoGetFieldList(sfieldlist) 2825 | let s .= s:DoGetMethodList(smethodlist, a:kind == 12) 2826 | 2827 | let s = substitute(s, '\<' . a:ci.name . '\.', '', 'g') 2828 | let s = substitute(s, '\ 0 ? a:package . '*' : substitute(a:package, '\.', '/', 'g') . '/*' 2912 | let matchpattern = a:0 > 0 ? a:package : a:package . '[\\/]' 2913 | for f in split(globpath(join(pathes, ','), globpattern), "\n") 2914 | for path in pathes 2915 | let idx = matchend(f, escape(path, ' \') . '[\\/]\?\C' . matchpattern) 2916 | if idx != -1 2917 | let name = (a:0 > 0 ? a:package : '') . strpart(f, idx) 2918 | if f[-5:] == '.java' 2919 | if !a:onlyPackages 2920 | call add(list, {'kind': 'C', 'word': name[:-6]}) 2921 | endif 2922 | elseif name =~ '^' . s:RE_IDENTIFIER . '$' && isdirectory(f) && f !~# 'CVS$' 2923 | call add(list, {'kind': 'P', 'word': name}) 2924 | endif 2925 | endif 2926 | endfor 2927 | endfor 2928 | return list 2929 | endfu 2930 | " }}} 2931 | "}}} 2932 | " vim:set fdm=marker sw=2 nowrap: 2933 | -------------------------------------------------------------------------------- /doc/javacomplete.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vim-scripts/javacomplete/f3d9ce93b4452baf5cb15b68d3b2a925a10a6e7d/doc/javacomplete.txt --------------------------------------------------------------------------------