├── .gitignore ├── .gitmodules ├── CONTRIBUTING.md ├── Default.sublime-keymap ├── README.creole ├── SublimeJava.java ├── SublimeJava.sublime-commands ├── SublimeJava.sublime-settings ├── build.py ├── classopener.py ├── package.json └── sublimejava.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | release 3 | *.class 4 | *.sublime-project 5 | *.sublime-workspace 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "parsehelp"] 2 | path = parsehelp 3 | url = git://github.com/quarnster/parsehelp.git 4 | [submodule "sublimecompletioncommon"] 5 | path = sublimecompletioncommon 6 | url = git://github.com/quarnster/SublimeCompletionCommon.git 7 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Before opening up a new issue 2 | ---------------------------------- 3 | 4 | * Use the search functionality to see if there's already an issue number 5 | dealing with the problem. If there is, please comment in the existing 6 | issue. 7 | * Make sure that the issue hasn't already been fixed in the master branch. 8 | I don't create a new release of the plugin for every single commit so 9 | it is possible that the issue has already been fixed. 10 | * Provide a small stand alone test case where the issue is reproducible. 11 | * Make sure that the issue is with SublimeJava specifically and not 12 | something that's broken in Sublime Text 2. 13 | 14 | Please do remember that I am not your personal tech support and issues related 15 | to configuring the plugin as appropriate for your system and target platform 16 | have a high chance as being closed immediately. I am just a single person and 17 | the time I allocate to this project is limited. You'll have a better 18 | chance asking on the [Sublime Text 2 forums](http://www.sublimetext.com/forum/), 19 | [Stackoverflow](http://stackoverflow.com/) or possibly 20 | [Twitter](http://twitter.com/) to reach a much larger audience. 21 | -------------------------------------------------------------------------------- /Default.sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "keys": ["."], "command": "sublime_java_dot_complete", 4 | "context": 5 | [ 6 | {"key": "sublimejava.supported_language"}, 7 | {"key": "sublimejava.dotcomplete"}, 8 | {"key": "completion_common.is_code"} 9 | ] 10 | } 11 | ] 12 | -------------------------------------------------------------------------------- /README.creole: -------------------------------------------------------------------------------- 1 | = Plugin discontinued = 2 | **If something is broken, submit a pull request and we'll consider merging it. 3 | 4 | ** Eventually it will be replaced by https://github.com/quarnster/completion. If you'd like to see that project move along quicker, submit a pull request and/or participate in its discussions. 5 | 6 | === Description === 7 | A plugin that provides a Java code completion view inside of Sublime Text 2. It also provides import insertion/completion and goto source/javadocs. 8 | 9 | === Known issues and feature requests === 10 | Please go [[https://github.com/quarnster/SublimeJava/issues?sort=created&direction=desc&state=open|here]] to see the currently known issues and feature request, or to file a new. 11 | 12 | === Prerequisites === 13 | # You need to have java installed on your system and the java binary in your path 14 | 15 | === Installation === 16 | * The easiest way to install SublimeJava is via the excellent Package Control Plugin 17 | ** See http://wbond.net/sublime_packages/package_control#Installation 18 | *** Once package control has been installed, bring up the command palette (cmd+shift+P or ctrl+shift+P) 19 | *** Type Install and select "Package Control: Install Package" 20 | *** Select SublimeJava from the list. Package Control will keep it automatically updated for you 21 | * If you want to manually install it using git instead: 22 | ** Go to your packages directory and type: 23 | *** git clone --recursive https://github.com/quarnster/SublimeJava SublimeJava 24 | ** To update to the latest commit, use this command in the SublimeJava directory: 25 | *** git pull && git submodule foreach --recursive git pull origin master 26 | ** Once you have the sources pulled, you'll need to compile the SublimeJava java source file: 27 | *** javac SublimeJava.java 28 | * You most likely want to configure the "sublimejava_classpath" setting to your own, issue [[https://github.com/quarnster/SublimeJava/issues/23|23]] details how this is done. 29 | * Also, you would mostly likely want to configure the "sublimejava_srcpath" and "sublimejava_docpath" 30 | to use source and doc search. NOTE: At present, this does not handle jar files. 31 | 32 | ==== Optional Key Bindings ==== 33 | You might want to add some keybindings to your user key bindings like the following: 34 | 35 | {{{ 36 | {"keys": ["ctrl+j", "d"], "command": "open_java_doc", "args": {"under_cursor": true }}, 37 | {"keys": ["ctrl+j", "s"], "command": "open_java_source", "args": {"under_cursor": true }}, 38 | {"keys": ["ctrl+j", "i"], "command": "import_java_class"}, 39 | }}} 40 | 41 | === Usage === 42 | Once installed it'll try to provide reasonable completion suggestions when you trigger autocompletion. Please note that it accomplishes this with the use of [[http://docs.oracle.com/javase/tutorial/reflect/index.html|reflection]] so it can only complete compiled classes that are in your classpath. 43 | 44 | To import a class, type the class name wherever it is that you will be using it. Then, with the cursor on class name, 45 | run the "Java: Import Class Under Cursor" command. If the class is found in the classpath, the possible imports will 46 | be presented to you in a quicklist. You can then choose the correct match for your import, and it will be inserted 47 | after the final import statement, or, if no imports are found, after the package declaration. If none of the presented 48 | classes are the class that you wanted to import, check your "sublime_classpath" setting. 49 | 50 | To go to source or javadocs, you may either look for the source under the cursor (using the "Java: Open Source/Javadoc Under Cursor" command, or simply open up a quicklist and filter through all sources ("Java: Open Source/Javadoc" command). 51 | 52 | === License === 53 | This plugin is using the zlib license 54 | 55 | {{{ 56 | Copyright (c) 2012 Fredrik Ehnbom 57 | 58 | This software is provided 'as-is', without any express or implied 59 | warranty. In no event will the authors be held liable for any damages 60 | arising from the use of this software. 61 | 62 | Permission is granted to anyone to use this software for any purpose, 63 | including commercial applications, and to alter it and redistribute it 64 | freely, subject to the following restrictions: 65 | 66 | 1. The origin of this software must not be misrepresented; you must not 67 | claim that you wrote the original software. If you use this software 68 | in a product, an acknowledgment in the product documentation would be 69 | appreciated but is not required. 70 | 71 | 2. Altered source versions must be plainly marked as such, and must not be 72 | misrepresented as being the original software. 73 | 74 | 3. This notice may not be removed or altered from any source 75 | distribution. 76 | }}} 77 | -------------------------------------------------------------------------------- /SublimeJava.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012 Fredrik Ehnbom 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | import java.lang.reflect.*; 24 | import java.net.*; 25 | import java.io.*; 26 | import java.util.*; 27 | import java.util.regex.Pattern; 28 | import java.util.regex.Matcher; 29 | import java.util.jar.*; 30 | 31 | public class SublimeJava 32 | { 33 | 34 | private static String getInstancedType(Class c, String gen, String ret, String[] templateParam) 35 | { 36 | if (gen.startsWith("class ")) 37 | gen = gen.substring("class ".length()); 38 | { 39 | // This is a bit odd, and I'm not sure it's correct. Seems "gen" returns an absolute 40 | // class path that isn't correct. I've only seen this for the specific class that is 41 | // in the repro case of issue #26, but it probably happens elsewhere too so could 42 | // have to be tweaked. 43 | // 44 | // In short, gen will list Tests.Tests$Foo whereas the correct type would be just 45 | // Tests$Foo 46 | String pat = "(\\w+\\.)+"+ Pattern.quote(ret); 47 | Pattern p = Pattern.compile(pat); 48 | Matcher m = p.matcher(gen); 49 | if (m.find()) 50 | { 51 | gen = m.replaceAll(Matcher.quoteReplacement(ret)); 52 | } 53 | } 54 | 55 | if (!gen.equals(ret)) 56 | { 57 | boolean set = false; 58 | TypeVariable tv[] = c.getTypeParameters(); 59 | for (int i = 0; i < tv.length && i < templateParam.length; i++) 60 | { 61 | String pat = "((^|[<,\\s])+)" + tv[i].getName() + "(([,\\s>]|$)+)"; 62 | Pattern p = Pattern.compile(pat); 63 | Matcher m = p.matcher(gen); 64 | if (m.find()) 65 | { 66 | gen = m.replaceAll(Matcher.quoteReplacement(m.group(1) + templateParam[i] + m.group(3))); 67 | } 68 | } 69 | ret = gen; 70 | } 71 | return ret; 72 | } 73 | 74 | private static String[] getCompletion(Method m, String filter, String[] templateParam) 75 | { 76 | String str = m.getName(); 77 | if (!str.startsWith(filter)) 78 | return null; 79 | str += "("; 80 | String ins = str; 81 | int count = 1; 82 | Type[] generic = m.getGenericParameterTypes(); 83 | Class[] normal = m.getParameterTypes(); 84 | for (int i = 0; i < normal.length; i++) 85 | { 86 | if (count > 1) 87 | { 88 | str += ", "; 89 | ins += ", "; 90 | } 91 | 92 | String gen = generic[i].toString(); 93 | String ret = normal[i].getName(); 94 | ret = getInstancedType(m.getDeclaringClass(), gen, ret, templateParam); 95 | str += ret; 96 | ins += "${"+count + ":" + ret.replace("$", "\\$") + "}"; 97 | count++; 98 | } 99 | str += ")\t" + getInstancedType(m.getDeclaringClass(), m.getGenericReturnType().toString(), m.getReturnType().getName(), templateParam); 100 | ins += ")"; 101 | return new String[] {str, ins}; 102 | } 103 | private static String[] getCompletion(Field f, String filter, String[] templateArgs) 104 | { 105 | String str = f.getName(); 106 | if (!str.startsWith(filter)) 107 | return null; 108 | 109 | String rep = str + "\t" + getInstancedType(f.getDeclaringClass(), f.getGenericType().toString(), f.getType().getName(), templateArgs); 110 | return new String[] {rep, str}; 111 | } 112 | private static String[] getCompletion(Class clazz, String filter, String[] templateArgs) 113 | { 114 | return new String[] {clazz.getSimpleName() + "\tclass", clazz.getSimpleName()}; 115 | } 116 | private static String[] getCompletion(T t, String filter, String[] templateArgs) 117 | { 118 | if (t instanceof Method) 119 | { 120 | return getCompletion((Method)t, filter, templateArgs); 121 | } 122 | else if (t instanceof Field) 123 | { 124 | return getCompletion((Field)t, filter, templateArgs); 125 | } 126 | else if (t instanceof Class) 127 | { 128 | return getCompletion((Class)t, filter, templateArgs); 129 | } 130 | return null; 131 | } 132 | private static final String sep = ";;--;;"; 133 | 134 | private static String getClassname(String pack, String clazz) 135 | { 136 | if (pack.endsWith(".*")) 137 | { 138 | return pack.substring(0, pack.length()-2) + "." + clazz; 139 | } 140 | else if (pack.length() != 0) 141 | { 142 | return pack + "$" + clazz; 143 | } 144 | return clazz; 145 | } 146 | 147 | private static final int STATIC_BIT = 1 << 0; 148 | private static final int PRIVATE_BIT = 1 << 1; 149 | private static final int PROTECTED_BIT = 1 << 2; 150 | private static final int PUBLIC_BIT = 1 << 3; 151 | 152 | private static int getModifiers(T t) 153 | { 154 | int modifiers = 0; 155 | if (t instanceof Method) 156 | { 157 | Method m = (Method) t; 158 | modifiers = m.getModifiers(); 159 | } 160 | else if (t instanceof Field) 161 | { 162 | Field f = (Field) t; 163 | modifiers = f.getModifiers(); 164 | } 165 | else if (t instanceof Class) 166 | { 167 | Class c = (Class) t; 168 | modifiers = c.getModifiers(); 169 | } 170 | int returnvalue = 0; 171 | if (Modifier.isStatic(modifiers)) 172 | returnvalue |= STATIC_BIT; 173 | if (Modifier.isPrivate(modifiers)) 174 | returnvalue |= PRIVATE_BIT; 175 | if (Modifier.isProtected(modifiers)) 176 | returnvalue |= PROTECTED_BIT; 177 | if (Modifier.isPublic(modifiers)) 178 | returnvalue |= PUBLIC_BIT; 179 | return returnvalue; 180 | } 181 | 182 | private static void dumpCompletions(T[] arr, String filter, String[] templateArgs) 183 | { 184 | for (T t : arr) 185 | { 186 | String[] completion = getCompletion(t, filter, templateArgs); 187 | if (completion == null) 188 | { 189 | continue; 190 | } 191 | System.out.println(completion[0] + sep + completion[1] + sep + getModifiers(t)); 192 | } 193 | } 194 | 195 | private static boolean getReturnType(Field[] fields, String filter, String templateParam[]) 196 | { 197 | for (Field f : fields) 198 | { 199 | if (filter.equals(f.getName())) 200 | { 201 | String gen = f.getGenericType().toString(); 202 | String ret = f.getType().getName(); 203 | ret = getInstancedType(f.getDeclaringClass(), gen, ret, templateParam); 204 | System.out.println("" + ret); 205 | return true; 206 | } 207 | } 208 | return false; 209 | } 210 | 211 | private static boolean getReturnType(Method[] methods, String filter, String templateParam[]) 212 | { 213 | for (Method m : methods) 214 | { 215 | if (filter.equals(m.getName())) 216 | { 217 | String gen = m.getGenericReturnType().toString(); 218 | String ret = m.getReturnType().getName(); 219 | ret = getInstancedType(m.getDeclaringClass(), gen, ret, templateParam); 220 | System.out.println("" + ret); 221 | return true; 222 | } 223 | } 224 | return false; 225 | } 226 | private static boolean getReturnType(Class[] classes, String filter, String templateParam[]) 227 | { 228 | for (Class clazz : classes) 229 | { 230 | if (filter.equals(clazz.getSimpleName())) 231 | { 232 | System.out.println(clazz.getName()); 233 | return true; 234 | } 235 | } 236 | return false; 237 | } 238 | 239 | private static boolean isPackage(String packageName) 240 | throws IOException 241 | { 242 | Package p = Package.getPackage(packageName); 243 | if (p != null) 244 | return true; 245 | 246 | packageName = packageName.replace(".", File.pathSeparator); 247 | 248 | ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 249 | for (String s : getClasspathEntries()) 250 | { 251 | URL url = classLoader.getResource(s + File.pathSeparator + packageName); 252 | if (url != null) 253 | return true; 254 | else 255 | url = classLoader.getResource(s); 256 | if (url == null) 257 | continue; 258 | 259 | String filename = URLDecoder.decode(url.getFile(), "UTF-8"); 260 | 261 | if (url.getProtocol().equals("jar")) 262 | { 263 | filename = filename.substring(5, filename.indexOf("!")); 264 | 265 | JarFile jf = new JarFile(filename); 266 | Enumeration entries = jf.entries(); 267 | while (entries.hasMoreElements()) 268 | { 269 | String name = entries.nextElement().getName(); 270 | if (name.startsWith(packageName)) 271 | { 272 | return true; 273 | } 274 | } 275 | } 276 | else 277 | { 278 | File folder = new File(filename + File.pathSeparator + packageName); 279 | if (folder.exists()) 280 | return true; 281 | } 282 | } 283 | return false; 284 | } 285 | 286 | private static final String CLASS_FILE_NAME_RE = "(?:\\$|/)?(\\w+).class"; 287 | private static final String CLASS_FULL_NAME_IN_PKG_RE = "(\\w+(\\.|\\$){1})+\\w+"; 288 | private static final String DIGITS_CLASS_NAME_RE = "\\d+"; 289 | private static final String SUBLIME_JAVA_CLASS_RE = "SublimeJava"; 290 | 291 | private static Pattern classFileNamePattern, classFullNameInPackagePattern, digitsClassnamePattern, sublimeJavaClassPattern; 292 | static 293 | { 294 | classFileNamePattern = Pattern.compile(CLASS_FILE_NAME_RE); 295 | classFullNameInPackagePattern = Pattern.compile(CLASS_FULL_NAME_IN_PKG_RE); 296 | digitsClassnamePattern = Pattern.compile(DIGITS_CLASS_NAME_RE); 297 | sublimeJavaClassPattern = Pattern.compile(SUBLIME_JAVA_CLASS_RE); 298 | } 299 | 300 | private static String getImport(String classFileName, String searchClass) 301 | { 302 | Matcher classnameMatcher = classFileNamePattern.matcher(classFileName); 303 | if (classnameMatcher.find()) 304 | { 305 | String classname = classnameMatcher.group(1); 306 | if ((searchClass != null && searchClass.equals(classname)) || 307 | (searchClass == null && !digitsClassnamePattern.matcher(classname).matches())) 308 | { 309 | String fullClassname = classFileName.replace("\\","/").replace("/", ".").replace(".class", ""); 310 | if (fullClassname.startsWith(".")) 311 | { 312 | fullClassname = fullClassname.substring(1); 313 | } 314 | // Only allow class as import if it has a package. Classes in 315 | // default package are not allowed to be imported. 316 | if (classFullNameInPackagePattern.matcher(fullClassname).matches()){ 317 | return fullClassname; 318 | } 319 | } 320 | } 321 | return null; 322 | } 323 | 324 | private static String[] getClasspathEntries() 325 | { 326 | Set paths = new HashSet(); 327 | paths.add("java/lang/String.class"); 328 | for (String s : System.getProperty("java.class.path").split(System.getProperty("path.separator"))) 329 | { 330 | paths.add(s); 331 | } 332 | return paths.toArray(new String[0]); 333 | } 334 | 335 | private static URL getUrlFromClasspathEntry(ClassLoader classLoader, String classpathEntry, String packagePath) 336 | { 337 | URL url = null; 338 | if (classpathEntry.endsWith(".class")) 339 | url = classLoader.getResource(classpathEntry); 340 | else 341 | { 342 | String path = "file://" + new File(classpathEntry).getAbsolutePath(); 343 | if (path.endsWith(".jar")) 344 | { 345 | path = "jar:" + path + "!/" + packagePath; 346 | } 347 | else 348 | { 349 | path += "/" + packagePath; 350 | } 351 | try 352 | { 353 | System.err.println("path: " + path); 354 | url = new URL(path); 355 | } 356 | catch (Exception e) 357 | {} 358 | } 359 | return url; 360 | } 361 | 362 | private static void printImportsNotInJar(File current, File root, String classname, Set possibleImports) 363 | { 364 | Matcher classnameMatcher = classFileNamePattern.matcher(current.getName()); 365 | if (current.isFile() && classnameMatcher.matches()) 366 | { 367 | if (classname == null || classname.equals(classnameMatcher.group(1))) 368 | { 369 | String classFileName = current.getAbsolutePath().substring(root.getAbsolutePath().length()); 370 | printPossibleImport(getImport(classFileName, classname), possibleImports); 371 | } 372 | } 373 | else if (current.isDirectory()) 374 | { 375 | for (File file : current.listFiles()) 376 | { 377 | printImportsNotInJar(file, root, classname, possibleImports); 378 | } 379 | } 380 | } 381 | 382 | private static void getPossibleImports(String classname) 383 | throws IOException 384 | { 385 | Set possibleImports = new HashSet(); 386 | ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 387 | for (String s : getClasspathEntries()) 388 | { 389 | URL url = getUrlFromClasspathEntry(classLoader, s, ""); 390 | if (url == null) 391 | continue; 392 | System.err.println("s: " + s); 393 | System.err.println("url: " + url); 394 | 395 | String filename = URLDecoder.decode(url.getFile(), "UTF-8"); 396 | if (url.getProtocol().equals("jar")) 397 | { 398 | filename = filename.substring(5, filename.indexOf("!")); 399 | 400 | JarFile jf = new JarFile(filename); 401 | Enumeration entries = jf.entries(); 402 | while (entries.hasMoreElements()) 403 | { 404 | String imp = getImport(entries.nextElement().getName(), classname); 405 | printPossibleImport(imp, possibleImports); 406 | 407 | } 408 | } 409 | else 410 | { 411 | File folder = new File(filename); 412 | printImportsNotInJar(folder, folder, classname, possibleImports); 413 | } 414 | } 415 | } 416 | 417 | private static void printPossibleImport(String fullClassname, Set possibleImports) 418 | { 419 | if (fullClassname != null && possibleImports.add(fullClassname)) 420 | System.out.println(fullClassname); 421 | } 422 | 423 | private static void completePackage(String packageName) 424 | throws IOException 425 | { 426 | String packagePath = packageName.replace(".", "/"); 427 | 428 | ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 429 | for (String s : getClasspathEntries()) 430 | { 431 | URL url = getUrlFromClasspathEntry(classLoader, s, packagePath); 432 | if (url == null) 433 | continue; 434 | System.err.println("s: " + s); 435 | System.err.println("packagename: " + packagePath); 436 | System.err.println("url: " + url); 437 | 438 | String filename = URLDecoder.decode(url.getFile(), "UTF-8"); 439 | Set packages = new HashSet(); 440 | if (url.getProtocol().equals("jar")) 441 | { 442 | filename = filename.substring(5, filename.indexOf("!")); 443 | 444 | JarFile jf = new JarFile(filename); 445 | Enumeration entries = jf.entries(); 446 | while (entries.hasMoreElements()) 447 | { 448 | String name = entries.nextElement().getName(); 449 | if (name.startsWith(packagePath)) 450 | { 451 | name = name.substring(packagePath.length()+1); 452 | int idx = name.indexOf('/'); 453 | if (idx != -1) 454 | { 455 | name = name.substring(0, idx); 456 | if (packages.add(name)) 457 | { 458 | System.out.println(name + "\tpackage" + sep + name); 459 | } 460 | continue; 461 | } 462 | name = name.replace(".class", "").replace('/', '.'); 463 | System.out.println(name + "\tclass" + sep + name); 464 | } 465 | } 466 | } 467 | else 468 | { 469 | File folder = new File(filename); 470 | File[] files = folder.listFiles(); 471 | if (files == null) 472 | continue; 473 | for (File f : files) 474 | { 475 | String name = f.getName(); 476 | if (name.endsWith(".class")) 477 | { 478 | name = name.substring(0, name.length()-6); 479 | System.out.println(name + "\tclass" + sep + name); 480 | } 481 | else if (f.isDirectory()) 482 | { 483 | System.out.println(name + "\tpackage" + sep + name); 484 | } 485 | } 486 | } 487 | } 488 | } 489 | 490 | private static void reportError(T e) 491 | { 492 | String type = "Error"; 493 | if (e instanceof Exception) 494 | type = "Exception"; 495 | String cn = e.getClass().getName(); 496 | String msg = e.getMessage(); 497 | if (msg == null) 498 | msg = ""; 499 | 500 | StringWriter sw = new StringWriter(); 501 | PrintWriter pw = new PrintWriter(sw); 502 | e.printStackTrace(pw); 503 | 504 | String stack = sw.toString(); 505 | if (stack.indexOf(cn) == -1 && msg.indexOf(cn) == -1) 506 | { 507 | stack = cn + "\n" + stack; 508 | } 509 | if (stack.indexOf(msg) != -1) 510 | msg = ""; 511 | System.err.println(type + ": " + msg + " " + stack + "\n" + ";;--;;"); 512 | } 513 | 514 | private static class MyClassLoader 515 | extends ClassLoader 516 | { 517 | MyClassLoader() 518 | { 519 | super(MyClassLoader.class.getClassLoader()); 520 | } 521 | public Class loadClass(String name) 522 | throws ClassNotFoundException 523 | { 524 | // First we use the superclass to get a hold of the class. 525 | Class c = super.loadClass(name); 526 | DataInputStream s = null; 527 | try 528 | { 529 | // However, as the default class loader does not reload classes that have changed 530 | // we use the handle to the class to try and get it's actual .class definition 531 | // which we then define in THIS ClassLoader subclass. Hence: dynamic class reloading :) 532 | String path = ClassLoader.getSystemResource(c.getName().replaceAll("\\.", "/") + ".class").getFile(); 533 | File f = new File(path); 534 | if (!f.exists()) { 535 | return c; 536 | } 537 | int len = (int) f.length(); 538 | byte[] data = new byte[len]; 539 | s = new DataInputStream(new FileInputStream(f)); 540 | s.readFully(data); 541 | return defineClass(name, data, 0, len); 542 | } 543 | catch (Exception e) 544 | { 545 | reportError(e); 546 | } 547 | finally 548 | { 549 | if (s != null) 550 | try { s.close(); } catch (Exception e) {} 551 | } 552 | throw new ClassNotFoundException(); 553 | } 554 | } 555 | 556 | private static Class loadClass(String name) 557 | throws ClassNotFoundException 558 | { 559 | // See http://stackoverflow.com/questions/4285855/difference-betweeen-loading-a-class-using-classloader-and-class-forname/7099453#7099453 560 | // for an explanation of using a class loader vs Class.forName 561 | ClassLoader cls = new MyClassLoader(); 562 | return cls.loadClass(name); 563 | } 564 | 565 | public static void main(String... unusedargs) 566 | { 567 | try 568 | { 569 | BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); 570 | boolean first = true; 571 | while (true) 572 | { 573 | try 574 | { 575 | if (!first) 576 | // Just to indicate that there's no more output from the command and we're ready for new input 577 | System.out.println(";;--;;"); 578 | first = false; 579 | String cmd = in.readLine(); 580 | if (cmd == null) 581 | break; 582 | String args[] = cmd.split(";;--;;"); 583 | System.err.println(args.length); 584 | for (int i = 0; i < args.length; i++) 585 | { 586 | System.err.println(args[i]); 587 | } 588 | 589 | try 590 | { 591 | if (args[0].equals("-quit")) 592 | { 593 | System.err.println("quitting upon request"); 594 | return; 595 | } 596 | else if (args[0].equals("-possibleimports")) 597 | { 598 | String arg = null; 599 | if (args.length > 1) 600 | { 601 | arg = args[1]; 602 | } 603 | getPossibleImports(arg); 604 | continue; 605 | } 606 | else if (args[0].equals("-findclass")) 607 | { 608 | String line = null; 609 | ArrayList packages = new ArrayList(); 610 | try 611 | { 612 | while ((line = in.readLine()) != null) 613 | { 614 | if (line.compareTo(sep) == 0) 615 | break; 616 | packages.add(line); 617 | } 618 | } 619 | catch (Exception e) 620 | { 621 | } 622 | boolean found = false; 623 | for (String pack : packages) 624 | { 625 | try 626 | { 627 | String classname = getClassname(pack, args[1]); 628 | System.err.println("Testing for: " + classname); 629 | Class c = loadClass(classname); 630 | System.out.println("" + c.getName()); 631 | found = true; 632 | break; 633 | } 634 | catch (Exception e) 635 | { 636 | } 637 | } 638 | if (found) 639 | continue; 640 | // Still haven't found anything, so try to see if it's an internal class 641 | for (String pack : packages) 642 | { 643 | String classname = getClassname(pack, args[1]); 644 | while (!found && classname != null && classname.indexOf('.') != -1) 645 | { 646 | int idx = classname.lastIndexOf('.'); 647 | classname = classname.substring(0, idx) + "$" + classname.substring(idx+1); 648 | try 649 | { 650 | System.err.println("Testing for: " + classname); 651 | Class c = loadClass(classname); 652 | System.out.println("" + c.getName()); 653 | found = true; 654 | break; 655 | } 656 | catch (Exception e) 657 | { 658 | } 659 | } 660 | if (found) 661 | break; 662 | } 663 | // Nothing yet.. Is it a package by any chance? 664 | if (isPackage(args[1])) 665 | System.out.println(args[1]); 666 | continue; 667 | } 668 | if (args.length < 2) 669 | continue; 670 | Class c = loadClass(args[1]); 671 | String filter = ""; 672 | if (args.length >= 3) 673 | { 674 | filter = args[2]; 675 | if (filter.equals(sep)) 676 | { 677 | filter = ""; 678 | } 679 | } 680 | int len = args.length - 3; 681 | if (len < 0) 682 | len = 0; 683 | String[] templateParam = new String[len]; 684 | for (int i = 0; i < len; i++) 685 | { 686 | templateParam[i] = args[i+3]; 687 | } 688 | if (args[0].equals("-complete")) 689 | { 690 | dumpCompletions(c.getFields(), filter, templateParam); 691 | dumpCompletions(c.getDeclaredFields(), filter, templateParam); 692 | dumpCompletions(c.getMethods(), filter, templateParam); 693 | dumpCompletions(c.getDeclaredMethods(), filter, templateParam); 694 | dumpCompletions(c.getClasses(), filter, templateParam); 695 | dumpCompletions(c.getDeclaredClasses(), filter, templateParam); 696 | } 697 | else if (args[0].equals("-returntype")) 698 | { 699 | if (getReturnType(c.getDeclaredFields(), filter, templateParam)) 700 | continue; 701 | if (getReturnType(c.getDeclaredMethods(), filter, templateParam)) 702 | continue; 703 | if (getReturnType(c.getDeclaredClasses(), filter, templateParam)) 704 | continue; 705 | if (getReturnType(c.getFields(), filter, templateParam)) 706 | continue; 707 | if (getReturnType(c.getMethods(), filter, templateParam)) 708 | continue; 709 | if (getReturnType(c.getClasses(), filter, templateParam)) 710 | continue; 711 | } 712 | } 713 | catch (ClassNotFoundException x) 714 | { 715 | // Maybe it's a package then? 716 | if (args[0].equals("-complete")) 717 | { 718 | completePackage(args[1]); 719 | } 720 | else if (args[0].equals("-returntype")) 721 | { 722 | String name = args[1] + "." + args[2]; 723 | if (isPackage(name)) 724 | System.out.println(name); 725 | } 726 | } 727 | } 728 | catch (Error e) 729 | { 730 | reportError(e); 731 | } 732 | catch (Exception e) 733 | { 734 | reportError(e); 735 | } 736 | } 737 | } 738 | catch (Exception e) 739 | { 740 | reportError(e); 741 | } 742 | } 743 | } 744 | -------------------------------------------------------------------------------- /SublimeJava.sublime-commands: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "caption": "Java: Import Class Under Cursor", 4 | "command": "import_java_class" 5 | }, 6 | { 7 | "caption": "Java: Open Source", 8 | "command": "open_java_source" 9 | }, 10 | { 11 | "caption": "Java: Open Source Under Cursor", 12 | "command": "open_java_source", 13 | "args": { 14 | "under_cursor": true 15 | } 16 | }, 17 | { 18 | "caption": "Java: Open Javadoc", 19 | "command": "open_java_doc" 20 | }, 21 | { 22 | "caption": "Java: Open Javadoc for Class Under Cursor", 23 | "command": "open_java_doc", 24 | "args": { 25 | "under_cursor": true 26 | } 27 | }, 28 | { 29 | 30 | "caption": "Java: Organize Imports", 31 | "command": "organize_java_imports" 32 | }, 33 | { 34 | "caption": "Java: Insert Package Declaration", 35 | "command": "insert_java_package" 36 | } 37 | ] 38 | -------------------------------------------------------------------------------- /SublimeJava.sublime-settings: -------------------------------------------------------------------------------- 1 | { 2 | // You probably want to configure this to something of your own 3 | // ${home}, ${env:}, ${project_path:} and ${folder:} tokens can be used in the sublimejava_classpath option. 4 | // 5 | // ${home} is replaced with the value of the HOME environment variable. 6 | // 7 | // ${env:} is replaced with the "variable" environment variable. 8 | // 9 | // ${project_path:} tries to find a file with the given name in all the registered project folders and 10 | // returns the first file found, or the original file name if none is found. 11 | // Example: ${project_path:main.cpp} tries to find a file named "main.cpp" relative 12 | // to the current project's folders. If none is found, it is replaced with "main.cpp". 13 | // 14 | // ${folder:} is replaced with the dirname of the given path. 15 | // Example: ${folder:/path/to/file} is replaced with "/path/to". 16 | // 17 | // Replacement is done sequentially, first all ${project_path:} are resolved, then ${home} and 18 | // ${env:} tokens, and then the ${folder:} are replaced, 19 | "sublimejava_classpath": [], 20 | 21 | "sublimejava_srcpath": [], 22 | 23 | "sublimejava_docpath": [], 24 | 25 | "sublimejava_dotcomplete": true, 26 | 27 | // When set to true will inhibit the completions suggested 28 | // by default by Sublime Text 2 if SublimeJava can't 29 | // find anything to complete 30 | "completioncommon_inhibit_sublime_completions": true, 31 | 32 | // When set to true will shorten class names in the completion list 33 | // by removing the package name. 34 | "completioncommon_shorten_names": true, 35 | 36 | // Set to true to not display a visual error message box when an 37 | // error or exception occurs 38 | "sublimejava_no_visual_errors": false, 39 | 40 | // When set to true, import class under cursor will also organize imports 41 | "sublimejava_organize_imports": true 42 | } 43 | -------------------------------------------------------------------------------- /build.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import sys 4 | import subprocess 5 | import tempfile 6 | 7 | if __name__ == "__main__": 8 | def get(url): 9 | proc = subprocess.Popen("curl -s %s" % url, shell=True, stdout=subprocess.PIPE) 10 | stdout, stderr = proc.communicate() 11 | return stdout 12 | 13 | def run(cmd): 14 | assert(os.system(cmd) == 0) 15 | 16 | version = json.load(open('package.json'))['packages'][0]['platforms']['*'][0]['version'] 17 | run("javac -source 1.5 -target 1.5 SublimeJava.java") 18 | 19 | package_name = "SublimeJava-%s.sublime-package" % version 20 | 21 | 22 | for arg in sys.argv[1:]: 23 | if arg == "--create": 24 | run("rm -rf release") 25 | run("mkdir release") 26 | run("cp -r sublimecompletioncommon release") 27 | run("find . -maxdepth 1 -type f -exec cp {} release \;") 28 | run("find release -name \".git*\" | xargs rm -rf") 29 | run("find release -name \"*.pyc\" -exec rm {} \;") 30 | run("find release -name \"unittest*\" -exec rm -f {} \;") 31 | run("rm -f release/build.py") 32 | run("cd release && zip -r %s *" % package_name) 33 | elif arg == "--upload": 34 | current_downloads = json.loads(get("https://api.github.com/repos/quarnster/SublimeJava/downloads")) 35 | for download in current_downloads: 36 | assert download['name'] != package_name 37 | f = tempfile.NamedTemporaryFile() 38 | f.write("""{ "name": "%s", "size": %s}""" % (package_name, os.path.getsize("release/%s" % package_name))) 39 | f.flush() 40 | response = json.loads(get("-X POST -d @%s -u quarnster https://api.github.com/repos/quarnster/SublimeJava/downloads" % f.name)) 41 | f.close() 42 | args = """\ 43 | -F "key=%s" \ 44 | -F "acl=%s" \ 45 | -F "success_action_status=201" \ 46 | -F "Filename=%s" \ 47 | -F "AWSAccessKeyId=%s" \ 48 | -F "Policy=%s" \ 49 | -F "Signature=%s" \ 50 | -F "Content-Type=%s" \ 51 | -F "file=@release/%s" \ 52 | https://github.s3.amazonaws.com/""" % (response["path"], response["acl"], response["name"], response["accesskeyid"], response["policy"], response["signature"], response["mime_type"], package_name) 53 | print(get(args)) 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /classopener.py: -------------------------------------------------------------------------------- 1 | import os 2 | import webbrowser 3 | 4 | path_to_full = lambda path: '.'.join(path.replace('\\', '/').split('/')) 5 | remove_dollar = lambda classname: classname.replace('$$', '.') 6 | 7 | 8 | class JavaClassOpener(object): 9 | 10 | MSG_NO_CLASSES_FOUND = \ 11 | "No classes could be found. Check your %s project setting." 12 | 13 | def __init__(self, completion, view, under_cursor, setting_name): 14 | self.completion = completion 15 | self.view = view 16 | self.window = view.window() 17 | self.under_cursor = under_cursor 18 | self.setting_name = setting_name 19 | 20 | def show(self): 21 | classname = self.completion.get_class_under_cursor() \ 22 | if self.under_cursor \ 23 | else None 24 | 25 | options = [] 26 | for path in self.completion.get_setting(self.setting_name, ""): 27 | path = os.path.abspath( 28 | self.completion.expand_path(path, self.window) 29 | ) 30 | options.extend(self._scan_dir(path, classname)) 31 | 32 | def do_open(result): 33 | if result != -1: 34 | filename = options[result][1] 35 | self._view_file(filename) 36 | 37 | if classname is not None and len(options) == 1: 38 | do_open(0) 39 | elif len(options) > 1: 40 | self.window.show_quick_panel([t[0] for t in options], do_open) 41 | else: 42 | self.completion.show_error(self.MSG_NO_CLASSES_FOUND % 43 | self.setting_name) 44 | 45 | def _scan_dir(self, base, classname_to_find=None): 46 | return [] 47 | 48 | def _view_file(self, filename): 49 | pass 50 | 51 | 52 | class JavaSourceOpener(JavaClassOpener): 53 | def __init__(self, completion, view, under_cursor): 54 | super(JavaSourceOpener, self).__init__(completion, 55 | view, 56 | under_cursor, 57 | "sublimejava_srcpath") 58 | 59 | def _scan_dir(self, base, classname_to_find=None): 60 | for root_name, dir_names, filenames in os.walk(base): 61 | try: 62 | dir_names.remove('.svn') 63 | except: 64 | pass 65 | package = path_to_full(root_name[len(base) + 1:]) + "." 66 | for filename in filenames: 67 | if not filename.endswith(".java"): 68 | continue 69 | classname = package + filename.split('.')[0] 70 | if (classname_to_find is not None and 71 | classname != classname_to_find): 72 | continue 73 | yield classname, (root_name + "/" + filename) 74 | 75 | def _view_file(self, filename): 76 | self.view.window().open_file(filename) 77 | 78 | 79 | class JavaDocOpener(JavaClassOpener): 80 | def __init__(self, completion, view, under_cursor): 81 | super(JavaDocOpener, self).__init__(completion, 82 | view, 83 | under_cursor, 84 | "sublimejava_docpath") 85 | 86 | def _scan_dir(self, base, classname_to_find=None): 87 | search_classname = None \ 88 | if classname_to_find is None \ 89 | else remove_dollar(classname_to_find) 90 | 91 | for root_name, dir_names, filenames in os.walk(base): 92 | try: 93 | dir_names.remove('class-use') 94 | except: 95 | pass 96 | package = path_to_full(root_name[len(base) + 1:]) + "." 97 | for filename in filenames: 98 | # - gets all the java overview bidness 99 | if (not filename.endswith('.html') or 100 | '-' in filename or 101 | filename == 'index.html'): 102 | continue 103 | classname = remove_dollar(package + filename[:-5]) 104 | if (search_classname is not None and 105 | classname != search_classname): 106 | continue 107 | yield classname, (root_name + "/" + filename) 108 | 109 | def _view_file(self, filename): 110 | webbrowser.open_new(filename) 111 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "schema_version": "1.1", 3 | "packages": [ 4 | { 5 | "name": "SublimeJava", 6 | "description": "Java completions for Sublime Text 2", 7 | "author": "Fredrik Ehnbom (quarnster)", 8 | "homepage": "http://github.com/quarnster/SublimeJava", 9 | "last_modified": "2013-02-15 08:30:00", 10 | "platforms": { 11 | "*": [ 12 | { 13 | "version": "1.1.23", 14 | "url": "http://cloud.github.com/downloads/quarnster/SublimeJava/SublimeJava-1.1.23.sublime-package" 15 | } 16 | ] 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /sublimejava.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2012 Fredrik Ehnbom 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | """ 23 | import sublime 24 | import sublime_plugin 25 | import os 26 | import re 27 | import bisect 28 | import imp 29 | 30 | def reload(mod): 31 | n = mod.__file__ 32 | if n[-1] == 'c': 33 | n = n[:-1] 34 | globals()[mod.__name__] = imp.load_source(mod.__name__, n) 35 | completioncommon = imp.load_source("completioncommon", os.path.join(os.path.dirname(os.path.abspath(__file__)), "sublimecompletioncommon/completioncommon.py")) 36 | completioncommon.reload(completioncommon) 37 | 38 | try: 39 | import SublimeJava.classopener as classopener 40 | except: 41 | import classopener 42 | imp.reload(classopener) 43 | 44 | 45 | class SublimeJavaDotComplete(completioncommon.CompletionCommonDotComplete): 46 | pass 47 | 48 | 49 | class SublimeJavaCompletion(completioncommon.CompletionCommon): 50 | def __init__(self): 51 | super(SublimeJavaCompletion, self).__init__("SublimeJava.sublime-settings", os.path.dirname(os.path.abspath(__file__))) 52 | self.regex = [ 53 | (re.compile(r"\[I([,)}]|$)"), r"int[]\1"), 54 | (re.compile(r"\[F([,)}]|$)"), r"float[]\1"), 55 | (re.compile(r"\[Z([,)}]|$)"), r"boolean[]\1"), 56 | (re.compile(r"\[B([,)}]|$)"), r"byte[]\1"), 57 | (re.compile(r"\[C([,)}]|$)"), r"char[]\1"), 58 | (re.compile(r"\[S([,)}]|$)"), r"short[]\1"), 59 | (re.compile(r"\[J([,)}]|$)"), r"long[]\1"), 60 | (re.compile(r"\[D([,)}]|$)"), r"double[]\1"), 61 | (re.compile(r"\[\L?([\w\./]+)(;)?"), r"\1[]")] 62 | 63 | def show_error(self, msg): 64 | if self.get_setting("sublimejava_no_visual_errors", False): 65 | print(msg) 66 | else: 67 | sublime.error_message(msg + "\n\nDisable visual error message dialogues with setting:\nsublimejava_no_visual_errors: true") 68 | 69 | def get_packages(self, data, thispackage, type): 70 | packages = re.findall(r"(?:^|\n)[ \t]*import[ \t]+(.*?)[ \t]*;", data) 71 | packages.append("java.lang.*") 72 | packages.append("") # for int, boolean, etc 73 | for package in packages: 74 | idx = type.find(".") 75 | if idx == -1: 76 | idx = len(type) 77 | subtype = type[:idx] 78 | if re.search("[\.\$]{1}%s$" % subtype, package): 79 | # Explicit imports, we want these to have the highest 80 | # priority when searching for the absolute type, so 81 | # insert them at the top of the package list. 82 | # Both the .* version and not is added so that 83 | # blah. and blah$ 84 | # is tested 85 | add = package[:-(len(subtype)+1)] 86 | packages.insert(0, add + ".*") 87 | packages.insert(1, add) 88 | break 89 | packages.append(thispackage + ".*") 90 | return packages 91 | 92 | def get_cmd(self): 93 | classpath = self.get_setting("sublimejava_classpath", ["."]) 94 | newclasspath = [] 95 | window = sublime.active_window() 96 | for path in classpath: 97 | newclasspath.append(self.expand_path(path, window)) 98 | classpath = newclasspath 99 | classpath.insert(0, ".") 100 | classpath = os.pathsep.join(classpath) 101 | return "java -classpath \"%s\" SublimeJava" % classpath 102 | 103 | def is_supported_language(self, view): 104 | if view.is_scratch() or not self.get_setting("sublimejava_enabled", True): 105 | return False 106 | language = self.get_language(view) 107 | return language == "java" or language == "jsp" 108 | 109 | def sub(self, regex, sub, data): 110 | olddata = data 111 | data = regex.sub(sub, data) 112 | while data != olddata: 113 | olddata = data 114 | data = regex.sub(sub, data) 115 | return data 116 | 117 | def fixnames(self, data): 118 | for regex, replace in self.regex: 119 | data = self.sub(regex, replace, data) 120 | return data 121 | 122 | def return_completions(self, comp): 123 | ret = [] 124 | for display, insert in comp: 125 | ret.append((self.fixnames(display), self.fixnames(insert))) 126 | return super(SublimeJavaCompletion, self).return_completions(ret) 127 | 128 | def get_class_under_cursor(self): 129 | view = sublime.active_window().active_view() 130 | data = view.substr(sublime.Region(0, view.size())) 131 | word = view.substr(view.word(view.sel()[0].begin())) 132 | return self.find_absolute_of_type(data, data, word) 133 | 134 | def get_possible_imports(self, classname): 135 | imports = [] 136 | 137 | if classname is not None: 138 | stdout = self.run_completion("-possibleimports;;--;;%s" % classname) 139 | imports = sorted(stdout.split("\n")[:-1]) 140 | 141 | return imports 142 | 143 | 144 | comp = SublimeJavaCompletion() 145 | 146 | 147 | class SublimeJava(sublime_plugin.EventListener): 148 | 149 | def on_query_completions(self, view, prefix, locations): 150 | return comp.on_query_completions(view, prefix, locations) 151 | 152 | def on_query_context(self, view, key, operator, operand, match_all): 153 | if key == "sublimejava.dotcomplete": 154 | return comp.get_setting(key.replace(".", "_"), True) 155 | elif key == "sublimejava.supported_language": 156 | return comp.is_supported_language(view) 157 | else: 158 | return comp.on_query_context(view, key, operator, operand, match_all) 159 | 160 | 161 | MSG_NO_CLASSES_FOUND = "No classes found to import for name %s." 162 | MSG_ALREADY_IMPORTED = "Class %s has either already been imported, is \ 163 | in the current package, or is in the default package." 164 | 165 | RE_IMPORT = "import( static)? ([\w\.]+)\.([\w]+|\*);" 166 | RE_PACKAGE = "package ([\w]+.)*\w+;" 167 | RE_IMPORT_SECTION = "(^import[^;\n]+;[^\n]*\n)+" 168 | 169 | 170 | class ExecuteImportJavaCommand(sublime_plugin.TextCommand): 171 | def run(self, edit, full_classname): 172 | insert_point = 0 173 | newlines_prepend = 0 174 | newlines_append = 1 175 | 176 | import_classname = full_classname.replace("$", ".") 177 | import_statement = "import %s;" % (import_classname) 178 | all_imports_region = self.view.find_all(RE_IMPORT) 179 | 180 | if len(all_imports_region) > 0: 181 | all_imports = sorted([(region, self.view.substr(region)) for region in all_imports_region], key=lambda a: a[1]) 182 | only_imports = [a[1] for a in all_imports] 183 | 184 | # The following logic is used to find the right spot to insert the import statement at if there are multiple 185 | # separate import groups. See discussion in #62 186 | pos = bisect.bisect_left(only_imports, import_statement) 187 | 188 | def score_string(a, b): 189 | score = 0 190 | for i in range(min(len(a), len(b))): 191 | score += a[i] == b[i] 192 | return score 193 | 194 | if pos == len(all_imports) or (pos > 0 and \ 195 | score_string(import_statement, all_imports[pos-1][1]) > score_string(import_statement, all_imports[pos][1])): 196 | # Insert after 197 | insert_point = all_imports[pos-1][0].b 198 | newlines_prepend = 1 199 | newlines_append = 0 200 | else: 201 | # Insert before 202 | insert_point = all_imports[pos][0].a 203 | newlines_prepend = 0 204 | newlines_append = 1 205 | else: 206 | package_declaration_region = self.view.find(RE_PACKAGE, 0) 207 | 208 | if package_declaration_region is not None: 209 | insert_point = package_declaration_region.b 210 | newlines_prepend = 2 211 | newlines_append = 0 212 | 213 | import_statement = "%s%s%s" % ("\n" * newlines_prepend, 214 | import_statement, 215 | "\n" * newlines_append) 216 | 217 | self.view.insert(edit, insert_point, import_statement) 218 | 219 | if comp.get_setting("sublimejava_organize_imports", True): 220 | self.view.run_command("organize_java_imports") 221 | 222 | 223 | class ImportJavaClassCommand(sublime_plugin.TextCommand): 224 | def run(self, edit): 225 | view = self.view 226 | classname = view.substr(view.word(view.sel()[0].begin())) 227 | 228 | if comp.get_class_under_cursor(): 229 | comp.show_error(MSG_ALREADY_IMPORTED % classname) 230 | return 231 | 232 | imports = comp.get_possible_imports(classname) 233 | 234 | def do_import(index): 235 | if index != -1: 236 | self.view.run_command("execute_import_java", {"full_classname": imports[index]}) 237 | 238 | if len(imports) > 0: 239 | view.window().show_quick_panel(imports, do_import) 240 | else: 241 | comp.show_error(MSG_NO_CLASSES_FOUND % classname) 242 | 243 | 244 | class OrganizeJavaImportsCommand(sublime_plugin.TextCommand): 245 | def run(self, edit): 246 | sections = self.view.find_all(RE_IMPORT_SECTION, 0) 247 | section_imports = [self.view.substr(section) for section in sections] 248 | for i in range(len(sections)): 249 | # TODO pass func to organize as java, 3rd party, project 250 | imports = section_imports[i][:-1].split("\n") 251 | imports.sort() 252 | imports = "\n".join(imports) + "\n" 253 | 254 | self.view.replace(edit, sections[i], imports) 255 | 256 | 257 | class InsertJavaPackageCommand(sublime_plugin.TextCommand): 258 | 259 | def run(self, edit): 260 | if self.view.find(RE_PACKAGE, 0): 261 | comp.show_error("Package already declared.") 262 | return 263 | 264 | file_name = self.view.file_name() 265 | for path in comp.get_setting("sublimejava_srcpath", []): 266 | path = os.path.abspath( 267 | comp.expand_path(path, self.view.window()) 268 | ) 269 | if (os.path.commonprefix([path, file_name]) == path): 270 | pkg_path = os.path.dirname(os.path.relpath(file_name, path)) 271 | self.view.insert( 272 | edit, 273 | 0, 274 | "package %s;\n" % pkg_path.replace(os.sep, ".") 275 | ) 276 | break 277 | 278 | 279 | class OpenJavaSourceCommand(sublime_plugin.WindowCommand): 280 | 281 | def run(self, under_cursor=False): 282 | classopener.JavaSourceOpener(comp, 283 | self.window.active_view(), 284 | under_cursor).show() 285 | 286 | 287 | class OpenJavaDocCommand(sublime_plugin.WindowCommand): 288 | 289 | def run(self, under_cursor=False): 290 | classopener.JavaDocOpener(comp, 291 | self.window.active_view(), 292 | under_cursor).show() 293 | --------------------------------------------------------------------------------