summaryrefslogtreecommitdiff
path: root/plugins/devkit/src
diff options
context:
space:
mode:
authorTor Norbye <tnorbye@google.com>2014-05-28 17:06:51 -0700
committerTor Norbye <tnorbye@google.com>2014-05-28 17:06:55 -0700
commitc667c1f74abd96a2098520effdd5afdff7f0d34b (patch)
treeab650b0e69fb57d40350579d8796da74d8ea49cb /plugins/devkit/src
parent0f831a730c50607e2ffd95020875af6185e17734 (diff)
downloadidea-c667c1f74abd96a2098520effdd5afdff7f0d34b.tar.gz
Snapshot idea/138.343 from git://git.jetbrains.org/idea/community.git
363dc51: row color for byte code viewer under darcula 98900f0: external annotations markers: move to fast line markers d5ee2ec: testdata fixed 2780ae5: ensure test class writable b8cdda6: preserve read-only status for class to test 27c5938: test generation: accept same test class name; avoid same test names 55c4e7a: Merge remote-tracking branch 'origin/master' 81c6fb5: PY-11592 Django 1.6: TEMPLATE_DIRS variable in settings.py is not created properly on project creation d4a3220: SQL: per-statement file structure grouping 3f7f1c1: IDEA-125397 Cyclic Expand Word no longer completes numbers 1443a9f: IDEA-125137 Click on failed unit test console output to navigate to test fails when using Groovy test name with '()' in test method name bb1b109: Merge branch 'master' of git.labs.intellij.net:idea/community d92b567: doesn't show empty langs in postfix completion tree 6252558: IDEA-88175 (wrong inspection Contents of collection 'x' are updated, but never queried) 5298c6b: HgCommandExecutor refactoring d215ac2: identify command from WorkingCopy changed to local 3dcf8ce: Merge remote-tracking branch 'origin/master' 8199a13: CCE 1cd6faa: Merge branch 'master' of git.labs.intellij.net:idea/community b2f594a: Merge remote-tracking branch 'origin/master' c2ad3ae: IDEA-119640 Problem with goto declaration of XML attribute defined in XSD 701f9fb: Merge branch 'master' of git.labs.intellij.net:idea/community fada6f1: Add temporary api to allow custom initialization of debug session data a41ecf9: IDEA-123466 Sheet dialogs doesn't transfer focus back then closed ea02814: Merge branch 'master' of git.labs.intellij.net:idea/community c6be0ae: XBreakpointUtil.getShortText() shortens text now 740b6b1: [log] IDEA-125578 Handle empty permanent graph d0891b3: IDEA-125580 - OpenShift: cloning application hangs if switch to another process 11202b9: DevKit: Goto EP declaration in plugin.xml (IDEA-86100) 8ebe9dd: IDEA-125258 ("Unnecessary boxing" inspection is triggered wrongly with overloaded constructors) 55ba38d: IDEA-121467 Quickfix inserts static imports instead of plain imports for inner classes 978a1dc: try with default pattern provider if selected one was not found (IDEA-125581) 8a80f67: leave foreign params untouched (IDEA-119700) 6c8c34c: avoid assert on IDE close with active debug session c68b3d3: IDEA-125344 (Serializable class without 'read/writeObject()' inspection should ignore classes w/o additions fields) ce6deee: IDEA-125558 new debugger: double result in evaluate expression 0315d6f: Merge branch 'svn_18_3' 7dcc19e: fix expression name for postfix template 2c9f426: [meo] restore backward compatibility in single-element CompositeFilter merge a6f23d0: use NavigationGutterIconBuilder 4ab120e: Merge remote-tracking branch 'origin/master' 1c6bfe0: invoke 'install plugin from disk' action from ctrl+shit+a a345e24: IDEA-125542 new debugger: 'rerun' failed tests are lost 53fd954: IDEA-125542 new debugger: 'rerun' failed tests are lost c9716a4: IDEA-124891 Implementation icon in gutter overlaps preview for Icon class Get rid of icons from collapsed areas f54e61d: ObjectsConvertor cleanup 05b53b7: IDEA-125421 diff tool opens wrong file if VCS file deleted via file system 854abe7: + retina variant 0d0dc51: add new icon 2e35935: broken plugins added ad666ec: do not bother users with warnings from external annotation roots 32fe274: stream api: do not collapse loops when body is not throws compatible (IDEA-125541) 972c75d: Lazy load the icon in DependencyValidationManagerImpl. #188 775e4cc: IDEA-124609 Unexpected indentation of anonymous classes 41c5b88: save caret position in evaluation dialog 4ca7c2b: cleanup 5287678: cleanup 7984ec9: IDEA-122538 Implemented "repository supports merge tracking" detection (for both command line and SVNKit) ffbcd16: fixed debugger tests 3d12351: disable postfix templates for coffeescript bfd4b28: ExtendedBuildModel now exposes the project's build directory. 9786fa2: fixing debugger tests 13e4b01: make test light ad7096e: IDEA-125361 (False positive 'Field may be final') 8ef32c8: missing dep e62c3c5: Merge remote-tracking branch 'origin/master' 895353f: extract DevKitGutterTargetsChecker 5c120d4: goto actions: vk_up/vk_down fix [^vasya] 2750009: Merge remote-tracking branch 'origin/master' 5859ed9: IDEA-125486 (Comparison using == instead of .equals() should replace with Objects.equals() instead of the instance .equals() method) 1316de2: PyCharm 3.4 artwork. 03da78e: Merge remote-tracking branch 'origin/master' 0cb2b06: Merge remote-tracking branch 'origin/master' a6d22de: fix testdata; restore again ExpectedHighlightingData 40420a0: quote parameters if win command processor launched with extra switches (e.g. /D) b61f519: Merge remote-tracking branch 'origin/master' f587cf6: require less stack in ResolveClassTest.testStaticImportNetwork 13441f9: Merge remote-tracking branch 'origin/master' 892e199: fix test 6c56888: Merge remote-tracking branch 'origin/master' 1d8d3f1: lambda: ignore results of assignment is used for expression lambda (IDEA-125473) eaa9ce6: method refs: check if interface functional (IDEA-125511) 5e7d32c: edit sources for documentation component; F4 2cf09bb: include window class into diagnostics (met with documentation popup) 2853c95: cleanup code on commit option d2c04b8: anonymous -> lambda: do not collapse to lambda functional interfaces with generics methods, accepted by method refs though c20f9d2: Merge remote-tracking branch 'origin/master' bfc443c: do not visit additional resource roots twice: additional indexed resource root providers can return intersecting paths sometimes 4b7dc58: a less gc-producing ImmutableText.subtext 2dc1af8: optimize ImmutableText memory usage 8dbda1f: [vcs] Log executable validation failure reason 9b3a535: [git] cleanup 52fb0b3: DebugLogConfigureAction: define the default focused component 47956e6: Merge remote-tracking branch 'origin/master' ec369f5: Local History tests tuned for new VFS behavior 6c0132d: Platform: IDEA-125330 Warning about non-project file modification should be merged with Read-only warning and shown in a modal dialog c611e59: Merge remote-tracking branch 'origin/master' b3afb57: Platform: OS X scrollbars can be disabled for diagnostic purpose d300533: Merge remote-tracking branch 'origin/master' a1d5b1c: fix border in dbe table editors 72f2e89: cleanup dccc79d: Merge remote-tracking branch 'origin/master' 5ffae2a: fix class with wrong qname sometimes returned from findClasses 813ab5f: [log] Don't schedule a refresh task if there is nothing to refresh a40d619: [log] Fix IAE when jumping to row #0 in empty table 2d2cf45: [log] set bek synchronously when applying filters to avoid locks 80e3729: Merge remote-tracking branch 'origin/master' c256133: fixed jdi objects leaks c10a0ca: Merge remote-tracking branch 'origin/master' 354d205: Merge branch 'python-fixes' 99c6577: Fixed false positive for 'yield' expressions in lambdas (PY-11663) 5de561b: Fixed false negative for 'yield' expressions in default values of parameters e23c4c0: Cleanup 222ecc2: hide execute in console action if not available a9d038d: Merge remote-tracking branch 'origin/master' 6823565: Merge remote-tracking branch 'origin/master' 26a710e: Merge branch 'modulefileinex-service' of https://github.com/yole/intellij-community into yole-modulefileinex-service 4be8c9b: Convert ModuleFileIndex to a module-level service. b37122b: merge hyperlink merging changes by meo (https://github.com/JetBrains/intellij-community/pull/185); cleanup 68f7fad: IDEA-125248 Incorrect simplify for float c82e10f: Merge remote-tracking branch 'origin/master' b7c5190: IDEA-125357 Throwable at com.intellij.openapi.wm.impl.WindowManagerImpl.isAlphaModeEnabled(WindowManagerImpl.java:359) 5e0039e: Merge remote-tracking branch 'origin/master' e32d36a: path fixed 9989bd2: Merge remote-tracking branch 'origin/master' ec32500: new "Serializable object implicitly stores non-Serializable object" inspection IDEA-62336 9db4420: - switching data buffering only once per state change - upon before file deletion event remove the file from myFilesToUpdate - rename inner class and other cleanup 438a3e2: final in "foreach" loop 0c86e1e: default message for postfix templates e8cef21: Merge remote-tracking branch 'origin/master' e2a7723: don't access disposed excluded entry (IDEA-120622 workaround) 33929c5: don't use checker.xml for dfa (but leave it there for a while, IDEA-125426) d50f08a: Merge remote-tracking branch 'origin/master' 7f8eae7: unification example postifx templates 45a21b2: Merge remote-tracking branch 'origin/master' 0fcb1c3: introduced pycharm_load_entry_point instead of searching for executable through local file system 76c0e94: moved inspections to groovy-psi 7c078e9: path to standardDsls fixed 1e4a913: moved more stuff to groovy-psi 0c48aca: DevKit: ExtensionPointCandidate: target PsiElement, EP-Locator: performance, search without supers 9dd16ef: Merge remote-tracking branch 'origin/master' 1a0533b: Merge remote-tracking branch 'origin/master' e617070: reverted 47fc79d09ef2793cd5fcc468d1fe7454bc5e4265 0375c72: correctly render more collections elements a149338: rearrange actions and added separators in context menus 0e230ea: fixed PY-12970 Extract variable: disable refactoring for parts of comrehensions 4af130d: use IntObjectMap in DirectoryIndex to avoid holding VirtualFile objects e41dedc: faster AdditionalIndexableFileSet.isInSet bb13e6f: Merge remote-tracking branch 'origin/master' f293b00: invalidate cashes stats 8173bf0: app start stats 1578ddf: JavaRearranger: proper processing of anonymous classes. 5580933: DevKit: extract ExtensionPointLocator 42702c9: moved to core ee9fcba: moved ExpressionConverter to psi 8404532: add groovy-psi to the roots for standardDsls loading e61091e: moved to psi f84bdce: export dependency 8c3458e: moved to properties psi 8a342ab: moved to core 0cf35f7: resource/gen ac478b1: moved resources to groovy-psi, regenerated icons 18ab45e: moved to core 1991ad8: moved to psi dc90b9a: cleanup cc0fbf8: test fix 9ed02a6: Platform: IDEA-125277 provide extension to allow non-project file modifications without notifications 817df5b: return code in django_manage_shell for old django versions 2355467: fix django console for PY-13031 remove old code with execute_manager and setup_environ fix django prompt 7b308ab: EA-54820 - assert: ClassMappingNameConverter.getVariants: fixed fd94f78: EA-54820 - assert: ClassMappingNameConverter.getVariants: diagnostics d0c1508: syncronize 'assert' c1ec45d: toString() restored 8142475: replace assertSame with assertEquals when comparing virtualFiles 9ee6a48: IDEA-123484: generate stub type definition for implicit trait type mixing a4094bf: IDEA-123484: multi-trait type dff7733: IDEA-123484: as operator with traits 0eefe8c: IDEA-124932 "Analyze Backward Dependencies" on any class fails with AssertionError: diagnostics d133496: javadoc 03ffdcf: IDEA-118036 Exception editing JSP file: diagnostics 49d5445: escaping redone, now only for double quotes cdca783: GPUB: drop deprecated stuff, parseTokenSmart & cleanup 6aef47f: Merge remote-tracking branch 'origin/master' d001816: PY-12734 Templates in app directories not found with implicit TEMPLATE_LOADERS 4fa70d5: do not write new setting to profile if it has not changed from the default value d38e995: fix for private fields 6e3b903: cleanup ProjectView popup menu and EA-56883 on Java8 a8ca529: Merge remote-tracking branch 'origin/master' f8b1755: revert: make too many mess in testdata to have everything escaped in error messages; or we won't have an ability just to accept actual error message and would have to rewrite it manually 6d3bf6d: IDEA-125464 Non-pinned and non-docked tool windows do not hide anymore f6a8a5e: (IDEA-89870, IDEA-114058) hg4idea: java.lang.AssertionError: Invalid command 51a5628: make exception dialog appear faster for long traces ea09e6e: don't fire UseVirtualFileEqualsInspection on comparisons with this 506a2cb: include anonym prefix in internal canonical text to avoid incompatibility messages with "same" types on both sides 5953b8f: accessibility of private method via anonymous classes fixed e43dbe0: IDEA-122430 Search in class hierarchy: preselect class b290c14: IDEA-125430 do not clear package view attributes a4898d7: Utility method for text alignment ee26a53: [log] Add registry key vcs.log.bek.sort=false. 2147279: [log] Add bek button. 02e5afb: [log] Add GitBekParentFixer for GitLogProvider. 9774870: [log] Create BekSorter. e3177e8: [log] Fix DelegateGraphFacade when applying filters. 56c0dd0: [log] fix VcsLogColorManagerImpl. 313c4b1: [git] cleanup: move method to the only place in tests where it is used a981a80: [git] IDEA-125328 Don't crash if commit/author time is empty a0f6188: Merge branch 'python-fixes' f0bc595: Specify HTTP URL scheme for proxy string passed to 'pip' (PY-12771) 843daaa: IDEA-125237 part II, the considering of static final fields 9213ca5: [log] IDEA-125309 One more fix for a possible deadlock f4b288b: fix gray gap in ComboBox under Darcula and IntelliJ lafs bdeea08: fix java.lang.IllegalArgumentException: Row index out of range when history is empty b553f5c: check for plugin incompatibilities before update for a new version (IDEA-24014) 1fc11e7: cleanup f6f7050: Merge remote-tracking branch 'origin/master' dc5157d: Fix CME in terminal (IDEA-125398). d8b5ecb: IDEA-125378 SQL: Implement keyword-based operators completion d591ab2: IDEA-107350 EvaluateXPath dialog: input field height is too small, text is unreadable 033aed0: IDEA-91663 (Method can be variable arity method shall have an option to ignore methods with multiple array arguments) 2335b92: make test light f0fdc59: IDEA-124590 Java Rearranger: Implement "section" support 61b6ca0: icon class regenerated 67b1070: Merge remote-tracking branch 'origin/master' ce11bc5: Merge remote-tracking branch 'origin/master' 3501998: PY-13018 ForeignKey to model in different file brakes backward references if referenced as APP_NAME.Model 383494f: Package info with links to confluence added 961bf42: Start plugins wizard #29 move plugins saving to beforeOkAction 42c25f2: DevKit: resources, icons, DevKitBundle 36d20ee: fix wrong LAF shown in Settings/Appearance on first run 86cda23: do not show empty Refactoring popup group aa68afd: IDEA-124119 [regression] Breakpoints dialog: checkbox for "condition" field is gone 544238b: Don't draw terminal content out of the terminal panel component (IDEA-125317). fdf377f: Merge remote-tracking branch 'origin/master' 9f9e67e: Don't draw terminal content out of the terminal panel component (IDEA-125317). 635cfcc: show renderer's icon in variables view a44cf2c: Merge remote-tracking branch 'origin/master' 70e83b9: Fixed scrolling issue in terminal (IDEA-125223). 947ef5c: show number of recurrent call in stack frames list a092c29: optimization: avoid unnecessary disk access operations 09d7890: Revert "Fix list selection background when unfocused" 15b772c: rerun browser on performed fix c7d2582: an internal inspection checking that virtual files are compared with "equals" d763d57: PushedFilePropertiesUpdater: don't start dumb mode because of vfs changes outside the project 844d483: compare virtual files by equals, not == b9b3b32: cleanup 82c9f72: cosmetics a8adf75: continue "auto expressions in Coffee" spec 00e17e3: borders for IntelliJ LaF 5857e85: removed another copy of processing files with given set of data keys 782f5eb: Merge remote-tracking branch 'origin/master' 164f48e: make customizable 9fe0fda: jediterm updated to fix IDEA-125385 421f658: selection border 356ac7a: default table render offset 5854fc8: support empty borders 92226c5: Merge remote-tracking branch 'origin/master' e3e854a: IDEA-125287 [new Java debugger] trimmed string value in Variables view f2f3a64: project file conversion: API, @NotNull and javadocs 96a6ad6: IDEA-121475 Row height in "Environment Variables" too low 19e6f54: IDEA-125369 (Error highlighting remains after expanding annotation to normal form) 3a80bec: IDEA-121475 Row height in "Environment Variables" too low After-review rollback 757fb8d: heads, parents and several other mercurial commands should be executed as local commands 6bb121c: IDEA-64250 ("simplifiable annotation" inspection could unwrap single-element array arguments) 54fdb56: [log] IDEA-125309 Fix deadlock. invokeLater instead of Wait 59630f4: Merge remote-tracking branch 'origin/master' cc055ad: fixed PY-12405 --noinput option does not work when running django unit tests 22b7cde: IDEA-122962 - Clouds: perform connection test in background - fix UI breaking on long error message eb65298: IDEA-125287 [new Java debugger] trimmed string value in Variables view ee02141: sources roots without sources converted to resource roots f2d9cda: @Nullable 2c2bc76: remove hardcoded RED color in tooltips 16d79c0: add module dependency [debugger-impl] -> [util] 2ff22a8: fix focus border around selected elements fabedf6: for indexed refactoring d4d6b60: sources roots without sources converted to resource roots 09c92d9: invalid source roots removed d750e35: XDebugger: Added myCustomTopPropertiesPanelWrapper to XLightBreakpointPropertiesPanel a1a393b: IDEA-124402 Exception during choosing input XML file 07e7838: IDEA-125289 Unexpected navigation after folding After-review fix 903857f: write action 268b90e: IDEA-124119 [regression] Breakpoints dialog: checkbox for "condition" field is gone - added mnemonic 2b3c1e1: notnull 2df4494: moved to psi a708897: notnull 90859a3: moved to psi a50eb7d: moved to psi, reduce dependencies 0a59e6f: notnull 76b18d8: moved to core 27e528f: continue "auto expressions in Coffee" spec fix NPE - absoluteLocalPathToSourceIndex 8a7ce9b: ensure fonts are not lost during collapse/expand, second popup shown, etc 0282f9e: IDEA-125047 Wrong error in XSLT injection d8f9f2b: Merge remote-tracking branch 'origin/master' 3baeea4: try to highlight only wrong argument in inapplicable call 607732e: ensure scrolling finished before popup is shown 7e77395: include build target info in error message for unexpected errors 1676779: IDEA-123049 Rearrange Code is breaking code (Code Style > Java > Arrangement) f91bf07: capitalization corrected 8f14954: IDEA-125302: The Analyze Stacktrace menu option remembers only one log file across multiple projects 7520a18: notnullification 94e42a8: don't warn on annotations containing errors 90a4b5c: make test light b2fc5b1: allow xml escaping in test highlighting data 3794f56: Merge remote-tracking branch 'origin/master' 68565ae: PY-2980 Unresolved reference for ForeignKey defined as string d923e07: IDEA-122956 Popups in Project Structure dialog have wrong initial size sometimes 202643b: init "auto expressions in Coffee" spec a23319a: cleanup 552c2c5: cleanup cb9b56b: cleanup b719055: Merge remote-tracking branch 'origin/master' 67ec3fc: IDEA-125269 List: bad selection BG/FG color cfa83df: use JBColor cb3f677: use default color scheme if using darcula color scheme with default LAF 1b9cdd8: get rid of unused DirectoryInfo.orderEntries field dd3c283: IDEA-124793 Editor gutter background not painted, gutter icons displaced 9f9a8b5: IDEA-121475 Row height in "Environment Variables" too low 98c8696: apply code cleanup after code generation, let's see how it would work on the long run (IDEA-124904) 7c15dce: cleanup tools extended a915a2d: update editor notifications on roots changed c21f106: check app directory first (IDEA-125294) c731f05: bomb test 9424172: Merge remote-tracking branch 'origin/master' 61fd17b: IDEA-125189 new debugger: pause doesn't select thread in frames combo c4f4f08: IDEA 123484: fix test ba16a8c: move all header into starting script for django console (PY-11728) dbe0fc8: IDEA-121687 New module->Ruby on Rails doesn't allow to specify sdk IDEA-121685 New module->Ruby has confusing UI 6db5e53: HgCommandExecutor refactoring (IDEA-119330 Reorganize mercurial command execution ) 3066cd3: IDEA-124119 [regression] Breakpoints dialog: checkbox for "condition" field is gone fbdaf43: foreach simplification + for in js postfix template 77e2c15: ignore 'winShellScriptingQuoting' test if not Windows 77df25e: AnnotateStackTraceAction in now reusable a1b2bed: Merge remote-tracking branch 'origin/master' cb8a9f0: add module dependencies: do not show duplicates (IDEA-125033) f5656e8: gutter icon for external annotations (IDEA-39633) bb5631c: action to change assignable var type (IDEA-125234) ec9a61b: extract method: redundant cast treatments (IDEA-125259) 68f0af6: IDEA-125262 Can't create Django, AppEngine and other specific Python projects 6ebbcc6: cleanup e11cf17: introduce process queue to auto-clean tossed resolve results 1a6a08f: compilation 2855b05: groovy sucks 11ba843: moved to psi 726502f: moved to psi 9fdcc12: removed static interface inheritance to reduce unnecessary dependencies 77522ae: inline GroovyFileType.GROOVY_LANGUAGE a093f0d: introduce groovy-psi module 65b4b98: moved to ui-ex 4f7b33b: CopyPastePostProcessor interface converted to class so incompatible plugins won't break copy-paste functionality (and exception will be shown only on first invocation) c946899: removed unused action to lessen dependency lang-impl on vcs-impl 0cb7117: removed unused code to get rid of dependencies from lang-impl to vcs-impl 68c8c32: some dependencies from lang-impl to vcs-impl removed a61b41f: IDEA 123484: support for trait fields. Accessors and pack__name fields abe983d: IDEA 123484: get rid of PsiImplUtil.isTrait(PsiClass) 98777dd: Gradle: nonclasspath finder updated to search psi classes in groovy sources 50c85d4: OC-9949 Breakpoint editing popup layout is broken e7884cc: vcs specific classes moved from lang-impl to vcs-impl 47fc79d: PY-12810 "imported" urls are not supported by {% url %} tag ( "patterns +=" now supported) 535e2f3: svn: Refactored SvnConfigureProxiesDialog - execute "svn info" as "test connection" implementation (instead of direct SVNRepository usage) 4c22b04: svn: Refactored BranchMerger - get latest source revision using InfoClient (instead of direct SVNRepository usage) 2046a89: svn: Refactored SvnUpdateEnvironment - get latest repository revision using InfoClient (instead of direct SVNRepository usage) f024c0c: svn: Refactored SvnUpdateEnvironment - removed unnecessary parameters, code simplified 39e5d54: svn: Fixed LatestExistentSearcher - ensure repository relative url starts with slash (for correct relative urls comparison) 65370cd: svn: Refactored LatestExistentSearcher - get latest repository revision using InfoClient (instead of direct SVNRepository usage) 6c0f1f1: svn: Refactored LatestExistentSearcher - fixed warnings, rewritten recursion with while loop d4fd095: IDEA-125237 (Misordered 'assertEquals()' arguments: support non-primitives) a11c2bd: svn: Refactored LatestExistentSearcher - check if item exists in given revision using InfoClient (instead of direct SVNRepository usage) 957aebe: svn: Refactored LatestExistentSearcher - use repository url passed as parameter (and not resolve repository url by itself) a4fcdde: IDEA-125289 Unexpected navigation after folding 894bbbd: fixed no toString value 8168063: replace @Nullable findInstance with @NotNull utility with logging (EA-55734) 061c4b0: compilation fixed: use dependency on 'annotations' module instead of 'annotations.jar' a454164: style 30287ac: IDEA-125285, EA-645852: AppCode exception on start up +review c08b9bd: Merge remote-tracking branch 'origin/master' 546f5fd: IDEA-86100 DevKit: support "Go To Related File..." sort after/before template files f4d0b25: svn: Refactored SvnUtil.remoteFolderIsEmpty - check folder content using BrowseClient (instead of direct SVNRepository usage) 12a5319: tweak insents 0e3c26e: [log] allow more results for the first request to get filtered results 06aebe0: Emmet: remove redundant borders from preview 2012b06: IDEA 123484: clashing methods from super-traits. Prefer the method from the last trait 1ba5924: IDEA 123484: access to super method with super. qualifier 205f80d: IDEA-124891 Implementation icon in gutter overlaps preview for Icon class c17387a: IDEA-86100 DevKit: support "Go To Related File..." 298ee51: Merge remote-tracking branch 'origin/master' 3c638cb: show variable name for arg_x and slot_x variables 3280f4f: [log] LOG.info the ISE (Joiner failure), not LOG.error f2e2a1b: [log] Joiner: use THashSet with much faster removeAll a32815c: [log] clear IntStack in DfsUtil. a5380f0: [log] Optimize IntTimestampGetter. bba2e43: [log] Optimize PermanentGraphImpl#getAllCommits() 02daba7: [log] Move GraphCommit implementation to vcs-log-graph 13378bb: [log] Add vcs-log-graph-api to root build modules 057c82c: fix author width update a1fe976: [log] Rewrite the process of refreshing log & building the DataPack 5cffaba: [log] Add sort type to PermanentGraph#createVisibleGraph. 081b82e: [log] Fix arrow. fa93a67: [log] Fix edges sort problem. 42874b2: [log] Create SmartDeltaCompressor. 89d784b: [log] Create new PermanentListIntToIntMap. It is useful for IntDeltaCompressor. a4141ad: [log] Fix NOT_LOAD_COMMIT. 82f85bd: [log] Fix color id. de26777: [log] Extract GraphLayoutImpl from GraphLayoutBuilder. 1b43a23: [log] Add DelegatedPermanentGraphInfo. 50b2fc4: [log] Create PermanentGraphInfo & PermanentCommitsInfo. 3cd6eb1: [log] performance fix. f5413a0: [log] PermanentGraphBuilderImpl - initial version 9210824: [log] Always request ordered recent commits c4804c2: [log] move getFilteredDetails to VcsLogFilterer 63b4315: [log] use interface 2ca8eac: [log] Remove obsolete ContentRevisionFactory ef6d65e: [log] Add new vcs-log-graph-api module in ultimate dd97706: [log] Fix jump to not load commit. 0510209: [log] Use trove HashMap. 8476e99: [log] Optimize in PermanentCommitsInfo for case CommitId == Integer. 3b585f0: [log] optimize full graph build. 2441f57: [log] Generify VcsLogJoiner. 954a4c2: [log] Fix mouse over action. 44505e8: [log] add equals & hashcode for PrintElement. 5d94e56: [log] Collapse all. 64433f9: [log] Fix oneOfHead(). 50388d4: [log] fix CurrentBranches. 24275af: [log] Fix CollapsedVisibleGraph. e86fb65: [log] performance problem 20b21f2: [log] Fix with for graph cell. 108183c: [log] Fix EdgesInRowGenerator. 4cc1adc: [log] Fix underdone nodes. e34b093: [log] Fix colorId. af5b227: [log] Drop VcsCommit. 616274e: [log] COMPILED! 4211697: [log] fix DataPack 3cbb213: [log] Change signature of GraphFacade#getAllCommits(); 545ef9f: [log] Rename getTime() -> getTimestamp(). 6fbc6f6: [log] Rename getHash() -> getId(). 33a7f03: [log] compilation fix. 4cb058c: ~~ [log] Add module dependence. bc7a666: [log] Add DelegateGraphFacade. b3d2206: [log] Add getVisibleRowIndex to VisibleGraph. 0a77ab3: [log] Make FilterVisibleGraph. c9795ab: [log] Make CollapsedVisibleGraph. 5be555e: [log] optimize import. 6650399: [log] Add branchNodeIndexes in PermanentGraphImpl. 293ef9a: [log] Add constructor. 15dd5b1: [log] Add IdFlags. 34c687e: [log] Fix PermanentGraphImpl. a4c2797: [log] Move method from CollapsedVisibleGraph to AbstractVisibleGraph. e671eb1: Extract deprecation warnings only if they present in text. b30e3f0: [log] In GraphColorManager JBColor -> int(colorId) && int -> CommitId 340ba68: [log] Fix EdgesInRowGenerator and add several tests. 1ec4cda: [log] Add LinearGraphWithElementsInfoParser. e3369e2: [log] Remove unnecessary BufferedReader usage. bd18165: [log] Remake test package structure. 4a67d5e: [log] Fix equals fox CommitId. 408d6878: [log] Remake tests. 62cad84: [log] Small fix. 88e5afd: [log] Remove newgraph package. c3fbf99: [log] Add Flags#setAll(). b8c1375: [log] Move DfsUtil. 770aff8: [log] Remake image printer. b49cea0: [log] Remake PrintElementsManagerImpl. 97c5f6d: [log] Add AbstractPrintElementsManager. fe3a680: [log] Create new PrintElementWithGraphElement. 80d21e9: [log] Remake ContainingBranchesGetter. 5996a24: [log] GraphCellGeneratorImpl -> PrintElementGeneratorImpl. 6b56353: [log] Generate equals & hashcode methods. 55b4219: [log] Remake AbstractPrintElementGenerator. 938956f: [log] Add simple implementation of PrintElements. 3784b45: [log] Remake EdgesInRowGenerator. ad7d357: [log] Remake FragmentGenerator. 1a1d238: [log] Add draft of CollapsedVisibleGraph. 95a51cd: [log] Add AbstractVisibleGraph. b8a9f02: [log] Fix CurrentBranches. 8722519: [log] PrintedLinearGraph.getLayoutIndex() -> getGraphLayout(). f6467e4: [log] Add draft files for PrintElementGenerator. 6f8ff22: [log] Add FilterGraphWithHiddenNodes. 6563dea: [log] Some changes in CollapsedGraphWithHiddenNodes. 0994121: [log] Add constructor. ad25cff: [log] Remake GraphWithElementsInfoImpl to CollapsedGraphWithHiddenNodes. 3abc13e: [log] Extract inner class. c9a5fb2: [log] Add LinearGraphAsGraphWithHiddenNodes. 3b2baa1: [log] Fix update event in GraphWithHiddenNodesAsPrintedGraph. a9bee1b: [log] graph.impl.visible.utils -> graph.impl.visible.adapters 2f4b46f: [log] Move all from vcs.log.facade.utils -> vcs.log.graph.utils. 579ee01c: [log] PermanentGraphLayout -> GraphLayout. 8e14dd2: [log] Add draft of PermanentGraphImpl. 6e5d35f: [log] Remake PermanentGraphBuilder to PermanentLinearGraphBuilder with CommitId. 313093e: [log] Remake PermanentCommitsInfo with CommitId. 8151d60: [log] Create new internal graph api. 797b59b: [log] Add PermanentCommitsInfo. 5e909f3: [log] Add IntTimestampGetter. 5049be6: [log] Drop unnecessary method from PermanentGraphLayout. adfa4b0: [log] DfsUtil: use IntStack. 24768a4: [log-api] Update api. b02fc19: [log-api] Update api. d1fce17: GRAPH API e7526b1: svn: Refactored BranchMerger - removed unused fields/parameters 65bab47: DevKit: refactor InspectionDescriptionNotFoundInspection 5a18a7e: svn: Refactored SelectLocationDialog - get repository root url using InfoClient (instead of direct SVNRepository usage) caa8262: support ContinuationWebSocketFrame cf56368: Calculate future feature only if it present in text. 1a79f0f: svn: Refactored SelectLocationDialog - removed unused code, removed duplication 445763e: do not calculate mirror file for each access to jar file 5cfdfeb: fix IllegalArgumentException: The file: doesn't exist. 49b2232: AC/C++: EA-56192 only allow directories in NullFileReferenceHelper roots +review CR-OC-1544 30d5ad4: js postfix template: parenthesized 3ce7f95: quote param containing ampersand for .bat files 0008feb: Based on comments for http://crucible.labs.intellij.net/cru/CR-IC-5400 * quote param containing ampersand for .bat exe files * improve test to actually execute .cmd/.bat files 2fb4ba8: reverted 69ea226: custom strategy bug fix, new Weak key soft value map e7ed4bf: moved to ui-ex bc74ad3: moved to core-impl c2083de: [log] Handle the case when null value is passed to the renderer 86c197a: IDEA-125227 New debugger: please disable switch from expression mode to code fragment mode when evaluating multiline expressions 519d350: Gradle: discover local gradle sdk sources local gradle dependencies 911aa11: inline GroovyFileType.GROOVY_LANGUAGE 9668cc5: renamed 06ad15b: javadoc, IncorrectOperationException replaced with UnsupportedOperationException as more standard 940d174: javadoc, cleanup, removed bulk operations since they are not jdk8 compatible anyway 7e1f445: AC/C++: EA-56192 allow files in NullFileReferenceHelper roots +review CR-OC-1544 ce9a38c: fixed "unmute on stop" action 2ca53a3: Calculate dunderAll in stubs only if file text contains __all__. 80cb0da: fixed PY-12970 Extract variable: disable refactoring for parts of comrehensions 1fe1440: typo 317cd70: fixed tomcat tests 47060bb: Merge remote-tracking branch 'origin/master' f343aa2: simplification postfix template fb45e36: Gradle: nonclasspath finder updated to search psi classes in groovy sources 897595c: Gradle: EA-54734 - assert: JavaSourceFilterScope.<init> 58d3c58: Gradle: EA-56552 - assert: NonClasspathClassFinder.findClass 3003df2: change class signature: process diamond (IDEA-125236) 479578f: move edit settings down 0da1a20: ensure expression valid, initializer expression should be already replaced (IDEA-125231) 57fa2db: fix tests 30d1d10: select and scroll to most recent revision (IDEA-60345) e54ffc6: scroll tree view file history to most recent change c3d8951: make scroll to selection in file history work better 90de406: support content loading for resource files from output roots (needed by some annotation processors) 6faa3ff: remove unnecessary addClass() calls 113323e: doc test: assert HTML body text only 96d93a8: add new not expression test c7ce438: cleanup 78a4030: Merge remote-tracking branch 'origin/master' 57f67f8: IDEA-125184 Mac launcher should not crash when IDEA_JDK env. var. does not correspond with JVMVersion from Info.plist. e7a2262: js postfix templates: not expression 7877bab: IDEA-125129 I would like to reopen IDEA-117698 3b26929: move jql lexer to gen source root 6723047: postfix template: js introduce variable be8ee4f: refactor expression postfix template with chooser cae5144: cache ClasspathCache.transformName 4119688: javadoc typos d12d8bd: [log] IDEA-116867 Use the same root renderer for author as for message ee1a2cf: IDEA 123484: traits are supported only since 2.3 version 86ffe87: IDEA 123484: complete trait keyword and extends/implements lists in traits 91875b6: IDEA 123484: trait methods are not allowed to be protected 5462267: IDEA 123484: clena up utils and tests 4e540d4: use previously calculated data from PHM if no read right now is performed: calculate the value instead of wait for IO to complete 53e5113: cvs file history in Browse CVS Repository 727804a: AC/C++: EA-56794 - assert: FileReferenceSet.getAbsoluteTopLevelDirLocations +review CR-OC 3326c46: cache already checked plugins (IDEA-124828) 0f9f39d: auto expressions spec: done duplication check 5d4a66c: auto expressions spec: init duplication check 901d664: Merge branch 'github-theme' f55b604: Updated JavaScript and CoffeeScript colors 9c26625: Updated Buildout, GQL, ReST and YAML colors 2bc7a23: Updated Django and Mako templates colors a027a36: Updated CSS, LESS and SASS colors f1a44bc: revert couple, 135 compatibility 8d2b706: change method return type on call site (IDEA-125166) 3316081: Updated HTML and XML colors e4bbd52: move diff utility classes to Util module 54754f3: add evaluate expression action to variable's context menu f915ea5: fixed NPE in evaluate handler 954f437: Updated default colors and set many Python styles to inherited 6c992e4: fix compilation ae76055: Fix list selection background when unfocused 98f8e9f: don't highlight if psi was invalidated (EA-56385 - PIEAE: PsiUtilCore.ensureValid) 9c76516: prevent SOE when creating PIEAE (EA-56284, EA-56810, EA-56809) 968779e: IDEA-123484: override/implement actions are aware of traits 1f6e60a: IDEA-123484: trait -> type_definitions token set c4764e6: IDEA-123484: use newHashSet(map()) instead of map2Set(), import static ContainerUtil.* ae3fce2: Sorted options 08ce945: svn: When looking for cached credentials for given url/realm check also if there are cached credentials for its parent urls/realms (and use them if found) ac6e1c9: IDEA-124077 Enum code reformat destroys enum a92b91b: rearranger @Nullable replacement 1311963: [diff] Simplify the "gear" button implementation e910c1c: fix Cut functionality and tests 9c75b87: [vcs] Simplify the gear action group definition 1dd998d: BlockSupportImpl: assertFileLength2->reportInconsistentLength 57b759f: IDEA-124712 ctrl-alt-space: duplicate completion variants 83a2697: IDEA-125194 Strange control-flow inspection 0f495ff: [git tests] cleanup ce0259e: svn: Refactored RepositoryLoader - use common client factory model (instead of direct SVNRepository usage) d7f2104: WEB-12199 Grunt tool window fails to load Gruntfile tasks, when an "&" is in the path e718569: Merge remote-tracking branch 'origin/master' c8485a5: Merge remote-tracking branch 'origin/master' e79e97b: js potfix templates: notnull & null 2c935f8: [git tests] Fix expected-actual order 81c101c: simplify settings (CR-IC-5127) c061e81: distinguish generated roots during rename/move warnings (IDEA-125147) 61460f7: combine all jdk jars into one marker in OutputChecker b49b8fd: [git tests] Remove a useless blinking test 053f37f: [git tests] Log index.lock error 6616b1a: [git tests] Log debug GitHandler stuff if test failed fed92bd: fixed PY-12991 Missing Added by user mark on paths added to the interpreter abfedef: throw IllegalStateException if number of keys in node to split is different from expected 73218a0: platform: plugin descriptors loading optimized a64c39b: EA-56305 (delegate FS used for parenting) 02521d2: [vcs] Add toolbar to the Shelf d9c049b: [vcs] ShowHideRecycledAction: don't proceed if project is null, cleanup db69de7: svn: Moved several classes to corresponding packages ("org.jetbrains.idea.svn.lowLevel" to "org.jetbrains.idea.svn.svnkit.lowLevel", diff related classes to "org.jetbrains.idea.svn.diff" package) 5546e02: Addede GitHub color scheme fd96e01: fixed tests 9a1a122: Merge remote-tracking branch 'origin/master' 850f818: Revert start_new_thread override. 5c8997d: svn: Refactored RepositoryBrowserDialog.doUnifiedDiff - use common client factory model (instead of direct SVNDiffClient usage) 71d8ba1: svn: Implemented "unified diff" clients (both for command line and SVNKit) e4dc06d: Merge remote-tracking branch 'origin/master' 02a4391: lazy scope initialization in case of big usage view open fba79fe: Slim. Introduce variable test fixed c816eb8: added javadoc d8f4bdd: IDEA-99541 New run configuration type to run Ant get rid of ProcessHandler.waitFor() e3f9e71: javafx: quick fix to switch to javafx css in property starts with -fx-; make css dialects configurable visible when file-mappings are available (IDEA-125020) 3cccccf: file templates: no NPE if exception doesn't contain message 5f0091f: refixed NPE in PasteHandler ea62a1d: NPE in PasteHandler 9ee678f: flusher on low memory should work the same way as periodic memory flusher to avoid loosing updates [r=Eugene.Zhuravlev] 12a7479: Merge remote-tracking branch 'origin/master' 1e48abe: good code red: nested classes inheritance e190be1: fix test 53b9222: processOverlapping should iterate in order cf632d0: do not open API for abuse 7bae702: refactoring - create both RTF and HTML flavours in one copy-paste postprocessor 66d211f: cleanup 4c253dc: refactor API interface 71ea747: make rich copy work for documents with non-normalized line separators c7f67f1: refactoring dc95443: cleanup 74c97e0: remove impact of ADD_IMPORTS_ON_PASTE setting on other copy-paste-related functionality 897f2ba: unused/static imports 873cada: cleanup 5a50486: @Override aa9f4b5: extracted GroovyRunnerPsiUtil f256b8d: moved ErrorUtil to psi 6243ae7: moved GroovyImportHelper to psi 242a795: removed dependency of GrMethodBaseImpl to code style. The clients of GrMethod.setReturnType() should call GrReferenceAdjuster.shortenAllReferenceIn() if they need to 5bdb72f: merged PsiUtil and GroovyPsiUtil f4c8ed4: moved to psi 939b86b: moved method to GroovyPsiUtil 9206109: moved to psi 532fdb0: notnull b6594a3: moved to core 91d1a5b: moved org.jetbrains.plugins.groovy.intentions.utils package to psi 55bb9af: moved to psi b2b65c2: removed dependency on platform 68910bc: moved to psi bda7716: moved to psi 91e946e: moved to core a95cbce: cleanup 7ca3394: cleanup 41b594c: notnull 00f6e5d: correctly identify class usage in dumps 28c6a3c: XML/HTML: move inspection descriptions to proper modules 47f5b9d: Revert "[vcs-log-graph] use Couple" 510ba46: fixed load project test 269e211: Code clean-up. 99f2434: fixed groovy debugger tests a32c4c4: add new test xml map serialization 1b65256: EA-55646: debug output 00964e5: update according to changes in GotoActionModel 9ea4344: rollback 7de12bf: Mono<T> == Function<T, T> 1c4c11b: [svn4idea] use Couple c159007: [testFramework] use Couple 00c700f: [vcs-log-graph] use Couple b8aa01b: [vcs-impl] use Couple 2d2df3b: remove unnecessary static 27c06dd: [ui-designer] use Couple 891b65f: [lvcs-impl] use Couple 9f2118c: [vcs-api] use Couple 31bd437: [properties] use Couple de97b7f: [platform-api] use Couple bbc1d35: [platform-impl] use Couple 7347cd3: [extensions] use Couple fb4d104: [idea-ui] use Couple 756e0ad: [hg4idea] use Couple 5bf1a92: [java-analysis-impl] use Couple ccb6b7c: [java-impl] use Couple c8b9199: [java-indexing-api] use Couple c04909d: [java-psi-api] use Couple d3278e6: [java-psi-impl] use Couple 70fcb07: [javaFx] use Couple eaed3bf: [jetgroovy] use Couple 526441d: [gradle] use Couple 3e1c9d1: [lang-impl] use Couple 7afb583: [dom-openapi] use Couple bffa11b: [debugger-impl] use Couple e9254bf: [github] use Couple 8fc8555: [git4idea] use Couple 187dd78: [vcs-impl] use Couple 2985542: [platform-impl] use Couple 47797e5: [core-impl] use Couple a583560: [core-api] use Couple a8727c8: [compiler-openapi] use Couple 9d30723: [compiler-impl] use Couple 1a54d31: [common-javaFX-plugin] use Couple e25eff2: Merge remote-tracking branch 'origin/master' 3974fdd: Fixed console debug for remote interpreter (PY-12959). db0de10: vfs: .zip file cache reset fixed e677a69: Remove double borders d54e0b0: svn: Refactored RepositoryBrowserDialog.doGraphicalDiff - use correct client factory depending on settings (and not always factory for SVNKit) c7313cf: Merge remote-tracking branch 'origin/master' 18844ff: PY-12810 "imported" urls are not supported by {% url %} tag (fixed quotes trouble) eeab21a: svn: Implemented diff for "url + url" pair for command line 3c6cccf: Data Views specs d47c2cd: EA-56596 - CCE: XmlLanguageInjector$.visitElement 7d729ca: platform: let out/err readers have independent policies e829e8c: store vfs charset in user data 8f05759: IDEA-121961 Debugger: evaluate from variables doesn't include context 0829ee7: better fix for IDEA-125082 90d6990: new inference: separate caching 8148aa2: filter out elements not matching scope (IDEA-125178) b1b6025: extract field from auto closable (IDEA-125141) 827e889: show the notification with a delay to avoid blinking when "too much output" ceases quickly a38bfd3: calculate editor notifications in a single background thread eae74d2: Cache enabling b6b3618: EA-56566 Fix NPE in TaskItemProvider.dispose c77cd7b: Merge remote-tracking branch 'origin/master' 714b3df: fixed PY-12698 Noticeable hang when selecting not yet added virtualenv as project interpreter b44ffe6: EA-56384 Fix NPE in JiraRepository.discoverApiVersion aef1f3e: do not show static node if empty 4c4b111: java-xdebugger: JRuby breakpoints handler a49b8ff: java-xdebugger: fixed tests 31434a5: java-xdebugger: IDEA-125088 Debugger: Evaluate Code Fragment works like Evaluate Expression 11d6bce: java-xdebugger: IDEA-125081 Debugger: Force Step Into works like simple Step Into 7c6f28c: java-xdebugger: allow method calls in quick evaluation with alt 428a04e: use editor font in expression combobox 1b7a152: java-xdebugger: correct context for watches evaluation e4d5f88: java-xdebugger: store java breakpoints in xbreakpoint's user data 3f3fdcd: java-xdebugger: fixes after review a542026: java-xdebugger: use correct context for variables calculation 2a64442: java-xdebugger: IDEA-125035 Debugger: breakpoints: unmute on finish: inconsistencies between behavior and appearance 4f64e19: java-xdebugger: IDEA-125037 Debugger: "Watch method return values" setting label is changed on stop f1de6f7: java-xdebugger: IDEA-125039 Debugger: Throwable at PsiDocumentManagerBase.commitAndRunReadAction() dca55b4: java-xdebugger: IDEA-125038 Debugger: setting field breakpoint causes IAE at SimpleColoredComponent.append() e1a926a: java-xdebugger: IDEA-125044 Debug: Run to Cursor: IAE at XDebugSessionImpl.breakpointReached() c2dc88d: java-xdebugger: fixing tests d369a04: java-xdebugger: avoid assert on context update b53018e: java-xdebugger: fixed breakpoint tests cd4b029: java-xdebugger: no need to recreate java breakpoints dc17bb0: java-xdebugger: do not store empty expressions 36e4354: java-xdebugger: show breakpoint icon when hit for non line breakpoints 9ade5d7: java-xdebugger: add stepping filter action acda84e: java-xdebugger: show extra array elements on demand 563511a: java-xdebugger: no need for extra constant d7a2306: java-xdebugger: correct context for variables calculation 98cb4bd: java-xdebugger: display object id and correct string coloring f882b59: java-xdebugger: show info messages in variables 1d06412: java-xdebugger: show return value on top of variables b1e62e0: java-xdebugger: fixed incorrect frames refresh with enabled filters e86c7d9: java-xdebugger: simplify external breakpoints handlers 764f536: java-xdebugger: fixed exceptions d21c49b: java-xdebugger: avoid asserts 4f439fe: java-xdebugger: static group and this variable cf699a8: java-xdebugger: variables icons 26a0ffb: java-xdebugger: get nashorn frames this way until we integrate fully 3bf4519: java-xdebugger: correctly handle frame change f0b82ee: java-xdebugger: correctly handle session change feb4620: java-xdebugger: lazy value label calculation 0f99ccd: java-xdebugger: avoid gathering variables with resumed context 6073323: java-xdebugger: do not calculate top stack frames for all threads 4b2ff87: java-xdebugger: avoid NPE 0ac53b8: java-xdebugger: fixed dump threads action d9d1c74: java-xdebugger: fixed memory leaks c83bb60: java-xdebugger: tooltip evaluation support 3b0b8f9: java-xdebugger: expressions language selection UI b025aaa: java-xdebugger: removed duplicated debugger editors provider d6299a6: java-xdebugger: correct context for expression 2293837: java-xdebugger: do not deprecate old setters b0f9849: java-xdebugger: fixes after review af29d44: java-xdebugger: store condition with language and custom info 071b912: java-xdebugger: custom java breakpoint handlers 2635efb: java-xdebugger: add to watch support a3b7433: java-xdebugger: View text action support 570305e: java-xdebugger: filter library frames support dbe29fe: java-xdebugger: jump to object source support aef95ac: java-xdebugger: use debugger thread to store descriptors tree 04571c0: java-xdebugger: mark object 439666c: java-xdebugger: set value prototype - minor extra fixes 3c7f48c: java-xdebugger: set value prototype 9265407: java-xdebugger: support of "view as" action 74a63863: java-xdebugger: drop frame action on the debugger toolbar b09633c: java-xdebugger: restore variables tree state when stepping bf02fe4: java-xdebugger: fixed actions order 1c7a3d4: java-xdebugger: fixed thread name presentation 24fb430: java-xdebugger: correctly calculate frames d5d617a: java-xdebugger: avoid ArrayIndexOutOfBoundsException c160771: java-xdebugger: special icon for current thread 61d146a: java-xdebugger: drop frame action f631088: java-xdebugger: do not show empty type dc9789f: java-xdebugger: rebuild on auto variables setting change 223d0ae: java-xdebugger: return correct active execution stack d225fdb: java-xdebugger: jump to source 59a75a5: java-xdebugger: do not create old DebugSessionTab af21fe9: java-xdebugger: pause support 3aade1c: java-xdebugger: "view as" action prototype ad3f1fc: java-xdebugger: init breakpoints on process attach 8835958: java-xdebugger: dump/export threads actions on the toolbar 8ce7361: java-xdebugger: customize data views action in watches context menu 7252d5d: java-xdebugger: customize thread views action in frames context menu 00ab63e: java-xdebugger: customize data views action in variables context menu f2e28dd: java-xdebugger: use xdebugger's mute breakpoints state 1cdd4f7: java-xdebugger: settings popup support ca891ba: java-xdebugger: imitate old frames renderer ef0745a: java-xdebugger: threads tab support - update on select b857507: java-xdebugger: assert not needed 27d67ad: java-xdebugger: threads tab support 60986c7: toString for line breakpoint 2479d8f: java-xdebugger: threads combo support prototype d66fc12: java-xdebugger: watches support prototype d2fe211: java-xdebugger: variables support prototype ef6369f: java-xdebugger: initial commit: - debugger start - java breakpoints handlers - set/unset breakpoints - simple frames view support f4fb687: EA-56555 (diagnostic) 8b27b16: Cleanup (weird exception constructor) 75f96f6: better corners for Darcula a0fae704: Merge remote-tracking branch 'origin/master' 665e0e1: hang on start up 66df65d: rename test methods to run on java 8 (ClassFormatError fixed) 5898e80: Filter out remote sources dir from extra sys path (PY-12958). 0a0b678: hide service information 2b8c333: fixed type annotations restoring (IDEA-124889) ef7c8b9: new inference: inference in terms of inference variable as type params (I) 0bea7ea: new inference: encapsulation 922715c: Fixed buggy calculateExtraSysPath 6133865: CR-IC-5378 add comment on java.io.File.exists usage instead of vfs refresh b2d382e: IDEA-99541 New run configuration type to run Ant 14a4668: EA-56795 (diagnostic) ee0b565: Cleanup (nullability; parameter type) a95b6d6: Slim. Template languages must be over HTML_DOCUMENT element 3c41d20: Embedding Ruby code into Slim. ba802ea: Merge branch 'master' of git.labs.intellij.net:idea/community 3a51776: Merge remote-tracking branch 'origin/master' 37c2e28: Gradle: extra tooling model build error builder added 4874a78: switch off couple inspection 9ad3184: EA-55850 (a constant used is only available since Win7) 56cea62: resources -> src 78e49f7: Cleanup (typo) 7a4ac7b: EA-56033 (early diagnostic) e52b674: cleanup b55f2ac: WEB-6779 JS Debugger / Variables: Automatically show used variables: mutability 4077052: Merge remote-tracking branch 'origin/master' 8b18eb8: IDEA-123484: independent GroovyLineMarkerProvider. Don't suggest implicit trait methods in implementation list 2f60fb3: IDEA-123484: concrete trait methods are implicitly generated in inheritors a7eb2a0: IDEA-123484: abstract trait methods should have explicit 'abstract' keyword 2fbab8a: lazy Object type in GrLightParameter 44eaa05: remove dependency from Groovy PSI to Groovy completion 18fa4ff: split groovy completion contributor into separate completion providers ce1beaf: IDEA-124731 semicolon or curly brace after constants 3a2dfbd: IDEA-124731 enum constants can be placed on several lines with comma placed on the next line 4bdc91c: lazy light PsiType for fast creation from string. Use it where possible in groovy psi 8c44253: cleanup stub generator 46a6910: IDEA-125082 (Weaken intention for LinkedHashSet suggests AbstractCollection) 38ba59f: fixed PY-12964 add path to the interpreter doesn't work f07444f: uniformly handling class loading from paths containing special symbols like spaces 88eb679: Arrangement: store opened section rules instead of start comments a4e42e3: WI-23285 Arranger: sections is keep added (already existed section is not found) 224ff5c: save some memory by not caching constructors in picocontainer; they're not reused anyway 3a25aee: DevKit: add InspectionDescriptionNotFoundInspectionTest.testHighlightingForDescriptionCustomShortName ed33e55: revert changes that made tests hang fedbb1d9: IDEA-124403, EA-55541, EA-55517, EA-49088, EA-49127 soft wraps recalculation issues 6112926: EA-55847 - AIOOBE: ImmutableText.charAt d2d6f45: cleanup (following review #212) e4f6aa4: EA-56779 - IVFAE: PersistentFSImpl.getFileId 2004f6a: improve description 16bd2ee: add "Suppress for 'Tests' scope" quickfix to "Prohibited exception caught" inspection f96b846: new "'BigDecimal' legacy method called" inspection 2be4b97: better layout for combobox eb6acaf: svn: Refactored RepositoryBrowserDialog.doGraphicalDiff - moved diff logic for "url + url" to SvnKitDiffClient ed5d225: svn: Refactored RepositoryBrowserDialog.doGraphicalDiff - do not wrap obtained changes in custom UrlContentRevision (see c38efc0eaffe39c4ac4250cdd4c2615a321db906) - changes collected by SvnDiffEditor correctly work with "Show Diff" action fb0c65a: InternetAttachSourceProvider: don't sync refresh from outside edt (EA-56768) 9770742: EA-56761 - PIEAE: ClsRepositoryPsiElement.getManager 8317a56: EA-56519 - CCE: PsiDocumentManagerBase.handleCommitWithoutPsi 2ecda67: vfs: create handler as soon as archive root is loaded b9b3a28: Cleanup (unneeded second refresh dropped) 00140af: IDEA-124361 Suggested variable names for Optional variable needs improving b92c65c: svn: Show "changes viewer" dialog even if changes collection is empty (dialog will display necessary information message by itself) 90a5b72: Merge remote-tracking branch 'origin/master' 6eb4127: fixed PY-11909 problem with extract variable a9d6cb1: svn: Moved several utility methods to SvnUtil class 71177eb: fixed PY-11922 pytest problem with marker expression containing spaces f895885: [log] IDEA-124546 enable "Edit Source" from commit list 3a483ab: [vcs] Delegate to the fuller constructor 8bc8177: fixed PY-11929 Insert type assertion should be disabled for references introduced in dict comprehensions de5a5e6: Add new icons for new arrangement section rule 19d7bf9: Merge remote-tracking branch 'origin/master' b61e49e: [log] Trim the hash in go-to-hash action a351cff: fixed PY-12037 long path cause wide menu 5972053: touch file to recompile 3e8ef45: continue WEB-6779 JS Debugger / Variables: Automatically show used variables 0c8bcaa: init WEB-6779 JS Debugger / Variables: Automatically show used variables ce66c08: compute memberFilter only once 1701927: IDEA-124688 Console scroll to the end - jumps on the end of the line sometimes 287390b: be prepared to event log changes when expiring notifications (EA-53620 - IOOBE: MarkupModelImpl.addLineHighlighter) 296a5dc: Platform: deleting shortcut from bound and inherited action fixed d0e540a: IDEA-106517 Exception on welcome screen when using speedsearch 5978682: svn: Refactored DirectoryWithBranchComparer - SVNKit diff logic moved to SvnKitDiffClient fc13a83: IDEA-116057 second-keystroke popup 1fe493a: fixed PY-12120 Invalid caret position after Enter press in unicode strings 69b6e2e: check for UserDataHolder directly 5cd9a8f: svn: Refactored SvnDiffEditor - use File instead of VirtualFile 8511042: diff: add icon for synchronized scrolling 8e9af01: js postfix templates: if, else, return, throw a309ba7: Remove Open Directory action. 23db467: use Couple fc77edb: fixed PY-12195 Changing signature of decorator removes '@' 6a1b621: cleanup 5653e4a: IDEA-124974 NPE from Switcher if 2 tool windows have the same first-strike char f74c773: do not log refresh stacktrace 6da5c0c: OC-9871 fd6d2fa: Gradle: tooling version updated in libLicenses.gant 0290479: use Couple e44b143: use Couple baecc09: use Couple 3a60ce3: use Couple 47f6b1b: IDEA-125121 MacMessages: NPE when parent is null 637a597: do not expand stubs when evaluating getContextName c39f3b1: Gradle: update Tooling API version: 1.12-rc-1 => 1.12 bbfc26b: IDEA-125119 NPE if message for MacMessage is null 5991df3: IDEA-124839 Gradle sync reports "Error:You can't change a configuration which is not in unresolved state!" and "Could not resolve all dependencies" 19c251b: fixed PY-12313 Incorrect __file__ value when running tests in a folder on Windows f9d754d: IDEA-124839 Gradle sync reports "Error:You can't change a configuration which is not in unresolved state!" b186522: @NotNull 4e90ea9: dependency fixed 3e98719: removed lang-api dependencies e5ba40d: removed dependency on platform-api 19444a1: notnull 4a8f5f8: @NotNull 9accb24: moved to core 38f3c11: moved out of psi c4390c4: Fixed IDEA-122488 IDEA 13 1 not using default font [CR-IC-5349] 8667c40: do not update when root name not changed 4569c83: fixed PY-12369 Code Inspection fails for negative default parameter 20c2b28: update plugins: check plugins according to the new version to be patched (IDEA-78385;IDEA-124308) 5f74804: IDEA-124543 sout template formatting f0918e0: fixed PY-12401 inline refactoring looses comments 851fd3b: IDEA-124305 JSP: Import statements suddenly disappearing every now and then 850ea16: typo acac62d: Cleanup (locale conversion; properties grouped) 21152ea: Cleanup (test simplified) a979900: vfs: core JAR FS migrated 4bf38d4: vfs: archive file system 96aad22: IDEA-64312 Maven: frequent .iml changes after exclude/source folder updates; project leak fix 607ba58: Platform: keymap tests cosmetics c6354dd: accept as cleanup tool 85f72e1: IDEA-123379 (Unable to generate toString() with template "+ super.toString()" if sub class doesn't have any variables) 2e1a1ab: fold some IJ boilerplate in console fd7f7a6: remove PSI event nesting because of eager PushedFilePropertiesUpdater (EA-56525) aafb2dc: use ensureValid (EA-56712) 5494673: TodoIndex supports snapshot mapping e676714: myContents shared snapshot index can be reset via clear (due to todoindex dependency on settings) 55f9327: use TW stripeTitle if needed. skip extra popup-step in case of one item. 8b36033: separate NavBar action place into popup and NavBarToolbar places (IDEA-124199) 3e3b44b: fixes for review comments 136d116: move normalizeMemberName to debugger view support b3675f7: IDEA-56033 Mercurial: add support for reverting an uncommitted merge 2b804da: IDEA-124393 Mercurial: Log: user filter applying causes empty log 27272f3: EA-55721 - assert: DbElementImpl.getDataSource 8c49254: EA-56008 - assert: DatabaseVirtualFileSystem.getProjectSafe 42cd37cc: related to EA-56292 - NPE: ComboBoxFieldPanel.createComponent c4a273f: EA-56671 - E: Runtime.syscall bffc362: adjust offset while looking for selector 6ef97d6: EA-56674 - IAE: DbFindUsagesHandlerFactory.canFindUsages c96459f: postfix templates convert configurable postfix list to tree & change store strategy 8481ffc: IDEA-118554 (Inspection suggestion: BigDecimal divide() without specifying a scale and/or RoundingMode) 69d1364: batching tasks to reduce the number of events posted to EDT c4e2bbe: remove useless HideFunctionValuesAction (now we correctly grouping functions) a862f08: Merge remote-tracking branch 'origin/master' 2386bed: testdata for IDEA-24479 760d73e: use manager.getProject (CR-IC-5302) 5c11efb: Merge branch 'master' of git.labs.intellij.net:idea/community 5ab59ac: EA-55808 - ISE: ComponentManagerImpl.getPicoContainer - a more robust fix 543d9f6: Merge remote-tracking branch 'origin/master' 5b53af1: Merge remote-tracking branch 'origin/master' 8de4646: IDEA-123896 Navigate -> symbol action doesn't find overloaded methods 7dc78a8: IDEA-124772 Code completion in import statement: suggestion shows package from "java.lang", but when it's selected "java.lang" prefix isn't 7e77d3d: Merge remote-tracking branch 'origin/master' 4b6debd: Merge remote-tracking branch 'origin/master' ad58ba7: results of code cleanup 1c55611: do not inspect binary files 0feba83: warn about raw arrays passed to varargs method (IDEA-16977) b32b333: assignment fix for super wildcards (IDEA-125031) 6e2f355: EA-56686 - NPE: UnusedDeclarationInspection$$.runInspection afe28ba: provide editor with project - no harm (IDEA-124656) a02113f: Remove class. 304f5dd: IDEA-124097 Structure tool window steals a focus 174a488: Merge branch 'master' of git.labs.intellij.net:idea/community 6a0d559: Remote libraries stored in a directory separated from skeletons 0f964da: aware of not sourcemap file adfec67: fix NPE Field is not nullable: className a0a681e: IDEA-124956 PsiShortNameCacheImpl.getClassesByName() is O(N^2) 5b4eafc: Rearranger: allow nested sections processing b638b32: Merge remote-tracking branch 'origin/master' c7bcc11: make "Unpredictable BigDecimal constructor call" inspection and quickfix more accurate 6ee9d29a: read saved index data by default + save empty indexed results ebece42: WEB-11680 chrome debugger doesn't start due to internal exceptions e2ceeec: WI-4722 Debugger: Ability to skip certain functions with step into. b63b703: continue WEB-11775 'Do Not Step Into' groupings ec71671: cleanup c309c6a: cleanup 0abe2e1: fix tests 51e2856: Merge remote-tracking branch 'origin/master' 1edc667: invalid psi range diagnostics (EA-49842 - assert: ExtendWordSelectionHandlerBase.select) dd4697d: IDEA-124379 Avoid completion lookup glitching 7732843: IDEA-85517 Option to collapse by-default manual defined (editor-fold) regions [CR-IC-5228] 095153a: set explicit locale environment: GeneralCommandLine.getCharset() and LC_CTYPE should be the same, remove dependency on project and application default encodings f4cd58b: DevKit: add InspectionDescriptionNotFoundInspectionTest 75ef7eb: fixed EA-53387 - CCE: PyElementGeneratorImpl.createParameter 440a2e5: Merge remote-tracking branch 'origin/master' f1842c2: fixed EA-56204 - IOOBE: SegmentArray.findSegmentIndex d5747a8: fixed EA-56667 - CCE: ReplaceListComprehensionWithForIntention.createForLoop f72f65c: DevKit: simplify/remove dups in DescriptionNotFoundInspectionBase, add tests 520268f: fixed EA-56673 - IAE: ChangeUtil.copyElement 6edaf52: fixed EA-56683 - NPE: PyStatementMover.moveTheSameLevel e78a1f8: Postfix cleanup af4c18c: fixed EA-56684 - NPE: PyStructuredDocstringFormatter.formatDocstring 51255dd: Merge remote-tracking branch 'origin/master' a6f328b: code cleanup available as intentions (IDEA-38653) 0e05ee3: Fixed a typo. d350749: IDEA-64312 Maven: frequent .iml changes after exclude/source folder updates a34f27b: Fix output handling. 6034330: DB: improve find usages grouping 0756490: scopes combo: filter duplicates, suppress internal-modules 2a680d2: DevKit: extract PluginDescriptorChooser, improve plugin.xml candidates list presentation, fix dumb mode error 598528c: Merge remote-tracking branch 'origin/master' c64f813: hide empty vcs scope when vcs is not configured 2e0f935: update "Select word at caret" to "Extend Selection" in tips (IDEA-124727) cc92f9a: use StringUtil.trimStart() CR-IC-5322#c26136 dfe9c11: DevKit: do not search in non-XML files for ExtensionPoint DOM usages CR-IC-5321 2fa4de5: ActionTestCase kicked 94c3177: fix puppet tests: clear extension cache on point registration 1d3458a: copy reference for directory: copy the relative path from some root (IDEA-92885) 3f1567e: enable CopyReferenceTest.testMethodOverloadCopy dffcdb3: rename "Select word at caret" to "Extend Selection" (IDEA-124727) 2dc0e67: IDEA-124935 Completion font issue on Spring b210fef: private EditorHyperlinkSupport.HyperlinkInfoTextAttributes c394f80: cleanup b3840bc: Cosmetics b05ddda: DevKitBundle: use AbstractBundle 847d67f: continue JS Debugger test framework — stepping spec 578a54b: cleanup cad6541: IDEA-125004 DevKit: support plugin.xml <depends> "config-file" 873fa26: Github: remove test a7ae56f: Github: do not catch OperationCanceledException explicitly 63ef800: EA-52410 Github: do not throw runtime exception on parse error fad9ec0: Github: fix dialog type 3c53f98: Github: remove final 0c7b058: Github: catch runtime exceptions in modal progress f08ff4c: Github: GithubFullPath case-insensitive 0204ba2: access to super members fixed (IDEA-124985) 7ab104a: IDEA-124985 74f3980: IDEA-64312 Maven: frequent .iml changes after exclude/source folder updates 11d2cc4: restore EditorHyperlinkSupport.getHyperlinks for EA 5f8a926: let EditorHyperlinkSupport.clearHyperlinks clear the hyperlinks 9219a05: Attach Debugger in Python console. e85f16d: IDEA-64312 Maven: frequent .iml changes after exclude/source folder updates c5c6471: EA-45164 - more TypeConversionUtil.getSuperClassSubstitutor diagnostics 28d0e2e: javadoc for clearHyperlinks deprecation 1fe25c5: EA-54479 - CCE: CustomFileTypeCompletionContributor$.addCompletions afba911: Emmet: use couple instead pair 3d63853: Custom templates: unify retrieving offset approach 0badce5: png optimization df16471: move generated icon classes to new generated roots 420255e: patched AbstractClassGenerator from CGLIB moved to util module because it's used from InstanceofCheckerGenerator class 92d4c4c: "Node.js Express App" doesn't relate to "Static Web" group f78f659: IDEA-124877 Run Configurations: Allow sort configurations under type or folder node (Exception fix) 7272ef6: remove copyright from intellij splash and about dialogs be38201: manually render copyright 9c0ccb4: +copyrightForeground f4cb6a8: Merge remote-tracking branch 'origin/master' 35f0811: fixed PY-12915 No project interpreter found after project creation aeb677d: Start plugins wizard #28 (be Ubuntu-friendly) ad086fe: Merge remote-tracking branch 'origin/master' c78ae5f: cleanup tool -> inspectionEP b52cf11: Fixed wrong usage. 421efd0: we must avoid use UUID.randomUUID — can cause long network calls 0ea9f4b: https://groups.google.com/forum/#!searchin/netty/netty$204.1/netty/8jf6SPFiN6g/nLg7opwUsxwJ 503f8cd: IDEA-124871 Can't build project if its name contains colon: convert project only when it is used as directory name 5bf5dd0: static methods in interfaces can't be hidden in subclasses (IDEA-124921) ec8f194: Extract execution timeout registry key f599645: fixed PY-12920 **kwargs in constructor parameters disappear after "Add super class call" quick fix 6a9db3b: Extract execution timeout registry key 26baf8f: extract superclass: pass target class in after data 838771a: code cleanup: allow to choose another profile; filter view to show cleanup tools only 2a25c8c: code cleanup (initial) 246bf70: Merge remote-tracking branch 'origin/master' 6e62c61: reverted back dc0ad20 due to PY-12904 IAE: Equal objects must have equal hashcodes 1eb6f78: - do not set databuffering if it was already set - make notifyAll in leave of StorageGuard if somebody is waiting 25ccd86: less lockStorage for put / get operations + store value file offsets in btree enumerator directly 1c8aaa4: lazier reparse range calculation b488d43: incremental reparse should happen only on nodes fully covering the changed fragment (EA-54262) 66da2af: more diagnostics and possible fixes for EA-46770: don't let code fragment documents be gc'ed 79ff607: EA-51141 - IAE: CreateFromTemplateAction$.getActionName 277ffe0: EA-54226 - simplify JavaFileManager, remove "access only after startup activity" assertion 4b22fa6: EA-54691 insert completion char in write action f4f67d2: EA-56504 - read action b515187: less lockStorage for put / get operations + store value file offsets in btree enumerator directly f0bdde8: move serialization of value container inside valuecontainer + added code for saving to use existing bitset used for large index values f0dcc74: magic number moved to registry e211432: check validity of RangeHighlighter 21afe13: [vcs] IDEA-120737 Change Merge button caption to Merge... c5bcb11: [git] IDEA-118125 change url for Detached Head information 60036d0: PY-10016 Namespace support in PyCharm 2.7.3 c02eade: DevKit: highlight <extensions> "xmlns" as deprecated 2b0d162: Devkit: remove non-stub test data 34d6a8e: include PIEAE invalidation trace as an attachment 571bdea: EA-56524 - check for virtual file validity when searching in its document d0a9cac: EA-56551 - don't expand structure view for invalid dom elements 7a6616d: EA-56593 - FindInProjectTask read action 73d32e6: move TraceableDisposable's own trace to the bottom as the least important 3fb8ff0: continue JS Debugger test framework e426c3e: Platform: redefined shortcuts for bound actions work in inherited keymaps + such shortcuts is shown in the UI tree, in spite of being 'bound' (OC-9826) 7859a0d: IDEA-124960 DevKit: "register extension" fix for EP defined in custom plugin bce419f: Devkit: rename/move RegisterExtensionFixProviderTest missed file 9359908: relaxed diagnostics 37894fc: Devkit: rename/move RegisterExtensionFixProviderTest 544ab30: CR-IU-696 - use activationComponent 0194c34: Devkit: add RegistrationProblemsInspectionCodeTest 92e0c60: extract PluginModuleTestCase d5a13c3: rename 28ea4b5: Don't evaluate debugger vars in EDT. 5529340: Fixed args. 5d4bea7: Merge remote-tracking branch 'origin/master' 7846df8: Backported more Pydev stuff including Stackless debugging support and AppEngine debugging fix. 5214a06: DevKit: add XmlRegistrationProblemsInspectionTest 3aa92a1: Merge remote-tracking branch 'origin/master' 3ea1686: Backport some minor Pydev fixes. f80095e: Rename modules. 8e6e512: [log] Don't send performAction request to the graph if below the log fad27ec: Commit document before creating callback fd61469: Allow emmet preview for xmlGenerator only abe4c7e: Fix Emmet preview for big abbreviations c8bb209: Fix emmet preview in injected fragments ddcf07c: Live templates: add diagnostic f5da90c: fix tests on linux (filename case) 5743b43: Merge remote-tracking branch 'origin/master' 48d9080e: Ability to perform force update when there are uncommitted merge added 001903a: clearer test data file name 11fdf63: Merge remote-tracking branch 'origin/master' e72fd1a: Fixed conversion. af0ae88: for CR-IC-5142 bff450e: make test light e3d5657: IDEA-124876 ("Mismatched query and update of StringBuilder" false positive with lambda method reference.) 3f9ce9c: IDEA-106749 DevKit/ComponentNotRegisteredInspection inspection does not handle optional dependencies properly 39f062f: store complete map results for snapshot supporting indices 743ae27: Remove 'Support' from settings name. d6c5e1f: Create thread on attempt to suspend on non existent thread. ad86a82: Merge remote-tracking branch 'origin/master' d42e997: We treat not started threads as alive, to support debugging of Python 3.4 threads created by start_new_thread. cfd0595: IDEA-122909 Mercurial update: clean option 6aae6da: Save all documents added before update and merge actions from branchPopUp 206d45f: SOE 51aa5b4: NPE fix bee86ca: new Pair<TypeA, TypeB>(a, b) -> Pair.create(a, b) d659703: DevKit: add ComponentNotRegisteredInspectionTest 28dce08: IDEA-124032 IDEA plugin: Can't find com.sun.xml.internal.messaging.saaj.soap.LocalStrings bundle command line property set to true by default 262d5a9: IDEA-124871 Can't build project if its name contains colon b3557e9: refactoring postfix templates 20fbada: IDEA-124032 IDEA plugin: Can't find com.sun.xml.internal.messaging.saaj.soap.LocalStrings bundle added command line property (false by default) 2d0c2f9: don't count 'runtime' dependencies when showing a warning about circular dependencies e5e5915: IDEA-124859 Runtime module dependencies are not compiled when run configuration with dependent module is launched c1980f4: check Pair types with actual parameters types 16906b2: IDEA-118714 Problem in automatically changing branch on tasks switching c177312: IDEA-123898 IntelliJ Configuration server : create/manage JetBrains account error 7370dd9: new Pair<TypeA, TypeB>(a, b) -> Pair.create(a, b) inspection for lang level < 1.7 136ade1: fixed PY-4479 Add field to class: select created field for editing, not passed to constructor parameter e7f0c33: add both add field and remove param QuickFixes for the param in __init__ method 00b00b6: Merge remote-tracking branch 'origin/master' 2d20acb: Merge remote-tracking branch 'origin/master' 3b0a609: track PsiFile invalidation trace 68ffeb6: use ensureValid (EA-55092) a51e36f: add PsiSubstitutor.ensureValid (EA-55730, EA-55738) 9f4593b: use Couple in util a249d4e: fixed PY-12825 Remove unused parameter: do not allow to remove last argument after star when refactoring function with keyword-only arguments 4ffed7a: IDEA-121171: fix failed tests c053844: convert testng -> junit e5b9d92: use Couple 5543b2f: use Couple d156a40: convert testng -> junit 43ba882: inspection and quick fix for Couple class 7411352: testdata fixed 4d5b798: restore test 74ff7fd: testAssistance -> DevKit 03640d1: EA-56450 - CCE: JavaSuppressionUtil.getInspectionIdsSuppressedInAnnotation 5455834: IPython should be enabled by default. 25a4149: Fix option label text and size. 514ddf8: Merge remote-tracking branch 'origin/master' b69e716: Return None in case of unknown source. 328fb7d: don't report ignored ioexception f0a2a53: No need to inform about failure. 541a3d3: fixed literal conversion. 11e19f2: IDEA-124877 Run Configurations: Allow sort configurations under type or folder node 6e84490: Do not delete inserted semicolon for statement-based postfix templates 7b768e0: Simplify return postfix template 9e18381: Reformat postfix templates tests f7abe52: Delete dummy semicolon after postfix template expansion 75264a6: Cleanup postfix templates 337ddff: TemplateManager -> service f6cc0f9: use ensureValid (EA-56464, EA-56467) 2b3b0f3: EA-55808 - ISE: ComponentManagerImpl.getPicoContainer c685b88: fix "already disposed" from EditorNotifications de62ac0: Devkit tests: add @TestDataPath ed97d68: PluginXmlDomStubsTest: move test data 7f98202: PostfixTemplateDescriptionNotFoundInspectionTest: rename/move test data 6d49cc4: extract ExtensionPointDocumentationProviderTest aebd2ef: PluginXmlFunctionalTest: cleanup inline test data 2bdefc7: No sets language without project (fix for ui-designer creating over default constructor) 4d0fde11: svn: Refactored SvnVcs - moved ourBusyExceptionProcessor to RepeatSvnActionThroughBusy 81f13d8: PluginXmlFunctionalTest: fix enabled inspections 8656e4d: rename/cleanup InspectionMappingConsistencyInspectionTest 2e6a042: extract DevKitImplicitUsageProviderTest 29ac0ab: PostfixTemplateInspectionTest -> LightCodeInsightFixtureTestCase 494bc38: OS X: fixed NSString and NSOpenPanelDelegate_ leaking add7462: Fix indentation to conform PEP8. 382ab4b: DOM stubs: exclude some dom extenders from stub building 72d5787: EA-56189 - ISE: LocalCanBeFinal.checkCodeBlock 788f777: EA-56308 - NPE: UnusedParametersInspection.checkElement 6dfed9d: EA-56333 - assert: PsiMethodReferenceCompatibilityConstraint.specialCase 606d8ea: EA-56286 - AIOOBE: BringVariableIntoScopeFix.invoke 659e657: svn: Refactored SvnVcs - removed old code that deletes previously used notification groups fec2014: svn: Refactored SvnVcs - removed unnecessary logging 33da31b: make dumb aware 2c7c9e1: IDEA-124663 (Refused Bequest inspection should honor @OverridingMethodsMustInvokeSuper annotation) c37b1a4: Merge remote-tracking branch 'origin/master' 08cf27c: select exact match in install package dialog 64f7299: read access d83af54: accept raw substitutors during diamonds inference (IDEA-124836) 16b6498: [git] fix some editable comboboxes 27a2e0f: [log] Fix transitivity in compareTo 7e86caa: DOM stubs: exclude some dom extenders from stub building bf339b6: Repository refresh added after pull action 8e41d7d: use Couple a285781: +getEmpty b4b6cc6: fixed PY-12660 PyCharm adds quotes in runconfiguration and fails 5f75f69: svn: Refactored SvnVcs, SvnUtil - removed unused code, warnings fixed, code simplified 85c621d: svn: Refactored SvnVcs - updated deprecated VcsListener registration 94af0a5: Merge remote-tracking branch 'origin/master' a26b016: easier logic for indentation in console and fix PY-12542 209eb14: fix for PY-12542 and improve indentation in console logic 9b97079: fill PyConsoleIndentTest with new tests and improve old 2aa7076: fix console crashing on IPython completion (PY-11645) 0dbd022: editor now save file on transition to Python Console (PY-12487) 265a763: move console startup commands to Starting script field in Python Console settings (PY-11728) 74aa6cc: add checkbox for disable IPython in Console settings (PY-7425) 5f5a2bb: restore EditorHyperlinkSupper.addHyperlink for EA binary compatibility af43068: OC-9742 e685f38: less disk operations on integrate 9db3107: fix expected wildcard types f29a167: fixed PY-12679 Remove redundant parenthesis: false negative for duplicated parenthesis in complicated and or statements cc743c3: testdata for JDK-8042508 1ab69ce: stream migration: missed qualifiers restored (IDEA-124820) 8862613: variable type by expression type suggestion (IDEA-124816) 909915f: Merge remote-tracking branch 'origin/master' dc0ad20: fixed PY-12698 Noticeable hang when selecting not yet added virtualenv as project interpreter f91fcdb: svn: Refactored SvnVcs - SVNKit related initialization logic moved to SvnKitManager b67eac0: svn: Refactored SvnVcs - encapsulate access to configured ssl protocols fa6941f: svn: Refactored SvnVcs - inner classes moved to separate files (and renamed) 0da2c6c: svn: Refactored logging logic in SvnVcs - code simplified, duplication removed 250b00c: IDEA-124250 Mercurial branches sorted in push dialog 17bf1db: ignore @org.mockito.Captor (IDEA-124802) 17e28ba: cleanup invalid fold regions (fix console leaks, IDEA-124626) d2ead52: move postfix templates to lang-impl d13a4e1: Emmet: add tests + fix existing ones f9e13cb: Emmet: fix preview for abbreviations with 's' filter 6799aa2: Emmet: move calculating template text into hint alarm b6f41b4: Emmet: parse incomplete more/climb abbreviations 3bc5114: Dispose hint on inner editor released d28aae1: Emmet: performance improvement 0ef5647: Cleanup emmet generator 88dcca7: Emmet: fix logging message 83deceb: Emmet: add new action for preview 14b4449: Fix typo b333197: Emmet: performance improvements, add segments limit. 3d5fcbc: Emmet: show new preview only if it's enabled in settings b53ff16: HTML: enable autopopup after < and & only be5c39d: Brand new emmet preview c8ec330: Extract logger 6ca1101: fixed PY-12786 Python Interpreters: extra space in configuration popup from project creation dialog 1a15783: fixed PY-12824 Remove unused parameter: usages in function calls are not updated 03a2ae2: IDEA-116630 Run/Debug button disabled sometimes after breaking make process (and hanging thread) 07613ed: svn: Refactored SvnKitManager - @NotNull, removed duplication 2c91e9a: restore deprecated EditorHyperlinkSupport.clearHyperlinks for broken plugins 7c1c4b1: fixed PY-12825 Remove unused parameter: do not allow to remove last argument after star when refactoring function with keyword-only arguments 520b4be: fixed PY-12826 Remove unused parameter: remove references in the docstrings 2311a48: some additional checks for consistency b79e433: svn: Refactored SvnVcs - methods related to SVNKit clients/repositories creation moved to separate SvnKitManager class 73c5925: better "cannot create branch from repository with no commits" problem handling it was unclear why "Create branch" option is not available for just created repo. b28779b: IDEA-124570 (diagnostic) 65e5214: merge EditorNotifications requests, update automatically on dumb mode changes 4a210b0: IdeaPluginConverter#getAllPlugins: get rid of isIdeaProject() call (DOM stubs interfering) 87b041d: move postfix templates descr to resource-en 7ef1c6d: svn: Removed unnecessary SvnUtil.formatRepresentation 4c7c29f: Run/Debug buttons stay disabled, thread hanging if external tool configured as 'before launch task' fails to start. 8b762a5: Merge branch 'svn_18_3' 7ee2226: Merge remote-tracking branch 'origin/master' b44a1bc: Removed unnecessary EP, introduced Rename Handler for Python Magic Literals: fixing some tests 6c5b423: revert IDEA-123956 Anonymous class formatting moves all args to the next line (32ae79905cb025f6bc213a925f1f4e58e9d6b8f7) 404cf1a: Merge remote-tracking branch 'origin/master' f0ac2bd: unnecessarily qualified statically imported elements: do not ignore chained calls (IDEA-124806) 6f548f2: skip tests under resources roots 51e7e1c: compiler configuration: distinguish compilable resources (IDEA-124599) 6a76a7e: ignore deleted custom repositories (IDEA-124796) a32723b: postfix template description inspection + tests 85f2823: remove alarm. Make SE faster 32ae799: IDEA-123956 Anonymous class formatting moves all args to the next line [CR-IC-5212] 6094264: Cleanup (unneeded interface) 899e7e3: [git] IDEA-124052 Support remote refs w/o a correspondent remote + test 172a3f9: [git] Don't log error in case of empty config file - it is allowed 95468ea: [log] Don't report the error too often 09f5029: [log] Fail safer if no refs were found at some head commit ea5506c: WEB-10560 Debugger: second Mute Breakpoints invocation doesn't work b967e4a: removed extra unneeded delegate 815a632: for given fragment: calc hash code once and do not use wrapper CharSequenceSubSequence for interning store CharSequence into Map of int -> CharSequence+ 06778a8: cleanup e01a5af3: initial d197ca8: reformat only changed text checkboxes are enabled (IDEA-121171) 1c6ba24: Start plugins wizard #27 cleanup 78e7840: WEB-12106 node-webkit crashes when opening developer tools 61b298f: ^461 don't create invalid range marker 5790679: getter invocation in case of object-backed scope 2a38f58: IDEA-115737 Mercurial problem on Windows when repository replaced right inside disk directory. 7d53080: Merge remote-tracking branch 'origin/master' a7bb442: Merge remote-tracking branch 'origin/master' 459d870: ignore @org.mockito.Spy (IDEA-124802) efa4d64: junit 4 library setup (IDEA-124780) 85f6ea1: IDEA-121171 Reformat only VCS changed text - does not work when editor is not opened 765dacb: cosmetics 753dd85: do not remove contents added by other subsystems, which were run independently from any compile session (e.g. ant build) 6e20824: capitalization f09a084: ensure cancel definitions search on popup closing 95e2ed2: Merge remote-tracking branch 'origin/master' 420f392: WinPty libs for Win XP. 161dd99: Removed unnecessary EP, introduced Rename Handler for Python Magic Literals: fixing some tests e593086: remove unnecessary file f585fcd: make test light 0e4f67f: IDEA-95363 ("for loop replaceable" inspection error) 1e1bb69: fix compilation 3078cb5: restore com.intellij.openapi.roots.LanguageLevelProjectExtension.reloadProjectOnLanguageLevelChange as deprecated ae82d08: IDEA-124688 Console scroll to the end - jumps on the end of the line sometimes 5348fc6: IDEA-124644 Comparison of negative zero with positive zero incorrectly marked as always false 0897305: AIOOBE (IDEA-120790) 86141d8: assertion e461a9bc: IDEA-124755 "Unnecessary {@inheritDoc} Javadoc Comment": do not warn when additional tags are present 4e13faa: Fixed IDEA-118781 Eclipse code style import: White Space: some categories are not applied b306260: Fixed IDEA-124647 Quick switch scheme > switch code style scheme does not work db2e799: don't clear cache for disposed MessageBus, fix tests 4c5d8e6: a higher output threshold for "Too much output" in console to avoid this message on long command lines 5b27a1f: navigate to function source: respect our navigation policy 1a04727: Merge remote-tracking branch 'origin/master' 288ccfa: most specific check: accept lambdas when target type is type parameter (IDEA-124725) 78e9bb7: type parameters should not cause deprecated constructor warning to appear (IDEA-124689) 91f6308: redundant suppressions: take into account alternative ids (IDEA-124690) a2a076c0: lambda -> method ref: reject anonymous class replacement (IDEA-124748) dc73135: accept static methods with body in interfaces (IDEA-124745) e4a77b6: check suspicious ConcurrentHashMap.contains (IDEA-124698) 35206e4: uncomment testdata 2ae7053: check inferred type test 4cefdc5: testdata for IDEA-124547 51f1e0e: add option to ignore specified AutoCloseable subtypes f8d2553: find field in superclasses also 747928f: fix broken readSettings() logic 83f842a: make magic checkbox work on private fields dac9905: remove faulty annotation 24bdcbf: DOM: optimize JavaMethod annotation handling dcdbc00: move MessageBusTest to platform-tests to avoid cyclic dependencies involving util 1da0bf9: save a bit memory on non-root message buses, relax test expectations a bit 0b888e5: IDEA-122914 (unclosed zip files) cf649f8: [git tests] Dump to stdout if the test failed 9214eb2: [tests] Move enable debug logging for certain categories to a common place 32597b1: better java doc 2ec1705: fixed PY-12698 Noticeable hang when selecting not yet added virtualenv as project interpreter d87049a: Merge remote-tracking branch 'origin/master' bc78e99: RunConfigurationExtensionsManager API improved 8d20600: Cleanup (obsolete logging) 4a61e1a: Show debug command line action renamed (PY-12835). 4725ae6: Merge remote-tracking branch 'origin/master' 21de2af: fixed PY-12848 Adding local Python SDK does not work c1c98cb: Merge remote-tracking branch 'origin/master' 7bd7aae: don't show hoisted variables 6de7d54: move BasicDebuggerViewSupport up 11656b8: init: don't show hoisted variables f1b95b5: ColorChooserIntentionAction.isInsideDecodeOrGetColorMethod: use constants 00ee9ed: Merge remote-tracking branch 'origin/master' ce4bafa: Until build moved forward. 2806dd8: OC-9570 OC-9663 0f15b6d: restore the ability to clear console from any thread bb44094: IDEA-124646 Option to turn off "compilation successful" popup adac2da: style: optimize if-statement and assignment c32cafb: style fcc2f18: Remove code duplication. 58be81d: Removed unnecessary EP, introduced Rename Handler for Python Magic Literals. b2c7dea: fix console highlighting of lines added one by one, dispose range marker 56beb0e: take into account buffer overflows when calculating console fragment to highlight & fold 9065b24: be less public in new EditorHyperlinkSupport API 5f39988: clarify a bit when hyperlinks & foldings are disabled in console 18fdcfd: disabling of foldings removed 84162a7: continue processing when the output stops 97474ce: revert of condition 358ff50: continue processing when the output slowed down under the threshold b19c269: more eager disabling b788f32: removed todos, no issue here. 88ead50: removed manual action e26bb7f: continue processing from last location - commit fix 15d968a: continue processing from last location 03969ac: removed myHighlighterToMessageInfoMap f60543f: disable console when there is too much added text removed EditorHyperlinkSupport#myHighlighters list which seems redundant and would need some smart clearing. 9d3bfdf: little experiment c572e4a: ^461 revert: we must create lazy range marker delegate on document changed, otherwise invalid delegate will be created b828042: background notifications: cancel on file invalidation; don't leak e429328: cache editor char width a8e14ae: relax testStaticImportInTheSameClass expected timing 676f08a: IDEA-124317 super class method is not shown in basic and smart completion, while accessible a655925: ability to serialize internal classes 51fabf9: cleanup fe4a711: rename to BasicDebuggerViewSupport 0fa7a11: rename to RemoteVmConnection a82fe46: init WEB-11968 Support debugging with embedded Chromium using projects such as CEF3 d3d775b: update editor notifications in background 2a77c0f: maven tests: tearDown more f0a6a44: IDEA-124476 (slf4j parameterized logging inspection: add option to only apply to certain log levels.) 54ae382: we must create lazy range marker delegate on document changed, otherwise invalid delegate will be created 0c7dbd9: cleanup 991f719: we must check getLine() at first, because getOffset() can cause dramatic side effects 5cdeb11: ^192 fix selection d652d59: done WEB-9842 Node debugging: Hitting a breakpoint with a local variable in scope that is a large buffer will cause the debugger to hang for a _long_ time WEB-9834 Node.js debugging: variable value calculation takes a while WEB-1892 NodeJS, Debugger: deadlock after stack frames has emptied WEB-7945 clustered view of Buffer class 73a6343: init: move getIndexedProperties to ObjectValue 3b4ad30: IDEA-124593 (Inspection "AutoCloseable used without try-with-resources" flags System.out.printf) 4290300: IDEA-124654 (replace with chained append() calls gives wrong result when the object is unknown) 4b68768: drop unused property 28c75d2: remove check covered by "Variable is assigned to itself" inspection (IDEA-124559) 5201ba0: prevent maven test initialization failing to spoil the fixture for other tests 1b55349: faster invalid line number detection in console exception filter (IDEA-124626) db58c9b: IDEA-124556 MessageBus event processing should be faster with many child buses and (almost) no listeners 77c7af8: optimization for getChars handling 48a56e0: PY-12846 ("show hidden files" configurable through FileChooserDescriptor) b0679d6: Cleanup (builder-style API; arrangement; duplicate deprecation) 4f5db0a: [git tests] fix paths comparison fa24163: [git tests] simpler method names 6d469a1: [git tests] Fix testdata for cherry-pick 600259a: [git tests] Remove the test root directory after test execution, reorder teardowns e422a99: [vcs] Fix already disposed exception 7cb3043: [git tests] turn cuke tests back on. f9d83e6: [git tests] fix GitAddSteps 49fecac: [git tests] Make sure other tear downs finish even if one fails e0847db: [tests] extract dumping log to stdout to the common test framework 07923cc: [git] Don't spam to stdout in tests unless test failed 84a8fbe: [git] Fix already disposed 90d2f45: new inference: early abort calculation by lambda return (IDEA-124276) f2256ca: calculate target type in the same way for statement/expressions lambdas 896c488: testdata to expression lambda 8096d63: inference when incomplete 6f6367d: fix layout under darcula 3d3550e: escape ampersands 3768908: IDEA-123831 (Missing Method Count Limit in Inspections under Class Metrics) 04d3283: cleaning javac nametable: synchronization added 368bb54: [git tests] Better test name not to make some buildserver Gits "insane" ea6683d: [git tests] Better test dir name not to make some buildserver Gits "insane" 41d0942: remove unused properties [CR-IC-5247] cbf1c1e: ensure pushers are run before initial indexing, don't rely on chance 4c1dc50: let PushedFilePropertiesUpdater schedule reindex itself if necessary 79a75f2: update language level without project reloading 64c452e: Merge remote-tracking branch 'origin/master' b86dec4: simplify 0c0fb61: change method name ad18e4b: make method name discoverable 5529054: IDEA-124653 Keyboard shortcut for New does not work 3b7d2e9: svn: Refactored IdeaSvnkitBasedAuthenticationCallback - inner classes moved to separate files dafd2d3: revert memory page size back d5a0f9e: svn: Moved several authentication-related classes to "org.jetbrains.idea.svn.auth" package ac99e82: svn: Refactored IdeaSvnkitBasedAuthenticationCallback - made inner classes to be static e663caa: svn: Refactored IdeaSvnkitBasedAuthenticationCallback - code simplified, duplication removed 3ac7f7d: some dumb mode logging (IDEA-124604) 10bf86c: dom vfs listeners: don't load new vfs, support cyclic symlinks, simplify 2f76fdc: svn: Refactored IdeaSvnkitBasedAuthenticationCallback.AbstractAuthenticator and inheritors - code simplified ec05ae8: svn: Refactored IdeaSvnkitBasedAuthenticationCallback.AbstractAuthenticator and inheritors - common parameters moved to base class, @NotNull faf7595: ^451 add test 326278b: extract JsDebuggerViewSupport bc771e7: show data-source name for table editors 0faa413: making sure all posted tasks are processed even after process terminated and dispose called by using sequential task executor instead of MergingUpdateQueue (IDEA-120167 Phantom eternal Ant task on make) aff053d: Increase connection timeout for JIRA integration tests ab7c4be: WEB-6584 Files opened by URL should also be able to open scripts used in the file 1461ed0: cache indexOf result 8b73b26: cleanup 62b7621: svn: Refactored IdeaSvnkitBasedAuthenticationCallback.AbstractAuthenticator - made it not generic 6d7d17b: IDEA-124580 Links in mac sheets dialogs do not work 534c2aa: Temporary solution for IDEA-124615 cebe7d9: IDEA-124535 com.sun.*.internal packages not found 21110f2: moved to core d8cf8f5: @NotNull 979de11: moved to core-impl 9dd6e0d: moved to java-psi bf77b14: OriginInfoAwareElement moved to psi to core 26ef0e3: completion-related classes moved out of psi to separate package 4fe1761: removed dependency on java-impl 46408be: method references: super methods treatment in exact check a3f190a: svn: Refactored IdeaSvnkitBasedAuthenticationCallback.AbstractAuthenticator - removed unnecessary parameters from acknowledge() method 985bef3: Start plugins wizard #26 better color for selection (Darcula), hide dialog buttons when in "Customize plugin group" mode 4f97e92: Merge remote-tracking branch 'origin/master' 0081a7f: fixed PY-12819 Unable to run GAE nosetests 782ae0b: check directories consistency only for local files 95bce93: Cleanup (common URL check code extracted) 901e60d: Cleanup (arrangement; aggressive logging) d3d0f73: CaseInsensitiveUrlHashingStrategy nullability f144943: applicability constraints by method reference (IDEA-122018) 9e9c7e3: Cancel future if alarm was disposed in TaskItemProvider to prevent deadlocks 1e352af: Update CA certificate for SSL tests. Add link to test server used for client authentication 9ebf7ca: reparse files on language level change 29a7c80: will explicit System.gc lead to less ResolveClassTest blinking? ea54399: less garbage in tests (VirtualDirectoryImpl.assertConsistency) fba7ee7: postfix templates memory leak eb639ae: Merge remote-tracking branch 'origin/master' c5d0d5e: handle hidden groups correctly 0d2fc45: Merge remote-tracking branch 'origin/master' 577cef9: cleanup d14900f: svn: Refactored IdeaSvnkitBasedAuthenticationCallback - removed unused methods related to saving proxy settings to svn config files (currently proxy settings are passed through command line arguments) dedc16a: svn: Refactored IdeaSvnkitBasedAuthenticationCallback - code simplifications, @NotNull ef226f6: restore extra space trimmed by fitInsideEditor() 779ebf1: save some invokeLater's in progress text2 updating 9c46a51: Cleanup (arrangement; warns in default logger) b02b4c1: more accurate work with IOUtil.allocReadWriteBuffer: use IOUtil.write/readUTF that have thread local buffer upon softreference: - to avoid extra allocations (1G of garbage produced for our codebase indexing) - possible several threads accessing same buffer problem bb2af75: Cleanup (locale use) 949b567: IDEA-120035 (diagnostic; log level lowered) 4f1e8e6: svn: Refactored SvnAuthenticationManager - removed unused/commented code, code simplifications, warnings fixes 2fde908: WEB-11690 TypeScript breakpoint is not hit We must respect fs case sensitivity sourcemap visualizer: ugly compiler can produce mappings for empty lines, and IDEA can then strip whitespace from line ends, but sourcemap still references to empty ranges 81f903f: remove LineSeparatorPainter 902e51a: substitute bounds (IDEA-123509) 547c62c: qualify conflicting fields name (IDEA-123969) f58394c: IDEA-124019 291c431: notification group registration fixed f9f9b39: Merge remote-tracking branch 'origin/master' 527f8db: fireModelUpdate for lazy structure building (Scala, etc) f1c3652: IDEA-93452 Implement "section" support in rearrange menu: update parent offset on new section rule insert 47d8b3d: svn: Refactored MergeFromTheirsResolver - use common client factory model (instead of direct SVNWCClient usage) fe22b91: Fixed: IDEA-87312 Custom code folding: editor-fold; Folding/unfolding behavior not available when an enum inside the block IDEA-122715 Region folding does work if contains interface definition 4f9003f: Update jediterm.jar with antialiasing restored. 118d6d9: do not process xs:include for stub building ff22ffc: Fix tests 80dfa90: IDEA-93452 Implement "section" support in rearrange menu: section rules validation fe96f39: IDEA-124461 New Module (Maven, Gradle) created in Empty project don't get the specified jdk 1b8f51f: IDEA-124400 New Module wizard: Gradle module misses options 952a2c5: IDEA-71508 Scroll with inertia (Mac os) should only work in the initial component 632d4c8: don't draw separator line if we cannot compute relationship properly for all lines (important in case of process input — nodejs for example) 2ebc871: WEB-11957 Valid JavaScript regexp marked as red ca2c75e: svn: Refactored RepositoryBrowserDialog - removed unused and commented code a15d397: cleanup after adding postfix templates preview 7a06f6d: EA-56182 59454cd: improve "Show Members" opption suppression c115690: IDEA-124435 (Fix "Collapse catch blocks" produces non-compiling code) c048414: 136 -> 138 8c562a2: bug fix postfix templates preview 2b4b915: avoid wildcard with null bound (IDEA-124377) a6db438: testdata fixed a4ac655: Gradle: IDEA-124477 sourceFolder order in iml file generated by gradle is unstable c0f3611: IDEA-115374 "Print file" truncates the code on Mac 12a8f3b: Start plugins wizard #25 get rid of scrollpanes' borders ffe6776: A typo. 9fa19ab: svn: Refactored RepositoryBrowserDialog - make "move file/folder" logic use common client factory model (instead of direct SVNCopyClient usage) 7a95352: svn: Implemented ability to move files/folders directly in repository (for CopyMoveClient) 240c396: more diagnostics on EA-56168 - assert: JavaFoldingBuilderBase.addToFold ae954a6: remove unused ExtensionsArea.unregister* 3ad5dd7: Merge remote-tracking branch 'origin/master' 10fe5a5: skip symlinks ff8a795: removing -ea from defaults because this may dramatically slow down compilation (e.g. eclipse compiler with annotations): https://jetbrains.zendesk.com/agent/#/tickets/27833 a465476: HardReference for PsiAnchors used for duplicates indexing, 2 885405f: features.xml fix 016de10: Cleanup (inner class abuse) 58734a2: svn: Refactored RepositoryBrowserDialog - make file/folder deletion logic use common client factory model (instead of direct SVNCommitClient usage) 65fd69c: svn: Implemented ability to delete files/folders directly from repository (for DeleteClient) d9fe570: HardReference for PsiAnchors used for duplicates indexing 73a17e0: follow up for commit 288dd00: make util-rt available for all agents 55614bb: svn: Refactored RepositoryBrowserDialog - make folder creation logic use common client factory model (instead of direct SVNCommitClient usage) 7265d01: cleanup 10eb4a3: StartupManager: linear-time startup activities running 4708541: IDEA-124442 Update options for newer version of YourKit (get rid of deprecated warnings) e225a23: IDEA-124503 Filter out IntellijIdeaRulezzz completion items f41cb93: svn: Refactored CreateBranchOrTagAction - removed unnecessary parameters, removed todo items 54da0af: IDEA-122732 Javadoc invalid html closing tags b8197a7: OC-9621 d19a902: svn: Removed unused classes 924f108: svn: Refactored ShareProjectAction - use common client factory model (instead of direct SVNCommitClient usage) 673351f: svn: Implemented ability to create folders directly in repository (for BrowseClient) 8bd8a4d: initialize file watcher in background 9d3c23d: avoid entering dumb mode to push properties to one file 0ab0ec6: Maven: cleanup test e98a99f: Merge remote-tracking branch 'origin/master' bdfec0f: Mantis integration: Reset repository configuration on Axis errors 67cf17d: Maven: stable order of source folders IDEA-64312 Maven: frequent .iml changes after exclude/source folder updates 5d0ce7c: enumerate items on demand c520519: Merge remote-tracking branch 'origin/master' c2957e7: anonymous -> lambda: conflicting ref in var declaration (IDEA-124525) 5dbbebe: extract superclass: process same name type parameters inside class members 84deec9: method refs: is exact should check super methods for this/super qualifiers only (IDEA-124507) 528a91b: Merge remote-tracking branch 'origin/master' 7a8f56c: fixed PY-12717 Improper replacement of the `print x, y` statement for Py3+ 33aeb0b: fixed PY-12804 PyStatementEffect inspection doesn't wrap exec argument in parenthesis 9929113: IDEA-124394 Exception while creating New File w/o extension 933194d: IDEA-124073 (avoid requesting focus manager until app is loaded) 4d43dab9: add before-after preview for postfix templates c4084ff: Merge remote-tracking branch 'origin/master' e2d7505: fixed PY-12726 PyCharm does not recognize compatibility issues when importing from __future__ 533e26d: do not reparse re-detected files in background in tests since it may cause unpleasant interference with e.g. highlighting 414c4ad: merged test classes ba23594: IDEA-124155 Performance problem on live search in long lines 6ce3483: Revert "Templates performance: run segments changes in bulk mode" c9284e0: Start plugins wizard #24 phrases 75472a5: calc hash from content once we have bytes available, don't delay its processing until we have only chars 5aac831: notnull 02a9c6e: notnull 81d2d98: cleanup 403d4f1: fix test a98abce: Templates performance: run segments changes in bulk mode 587e216: Merge remote-tracking branch 'origin/master' 66821eb: init WEB-11393 Live edit doesn't work for linked css 1429f44: IDEA-124527 Shift-drag after Shift-click starts new selection in editor d774199: IDEA-51883 IDEA prints out huge number of pages 8a80c54: Merge remote-tracking branch 'origin/master' 336be36: IDEA-98912 An Enter license button should be added to the welcome screen 72e2b3a: ensure each post-startup activity runs in smart mode (IDEA-123943) e64ae73: Merge remote-tracking branch 'origin/master' beeee74: make inspector work with modal dialogs 324ddc4: move getTokenType() up so white-spaces are skipped before rawTokenIndex() call c808c3e: integrate Grammar-Kit/pull/31 from ligasgr 984922a: tests fix afe050c: move xml lexers to gen root 2640f20: move groovy lexers to gen root eb2047f: move spi lexers to gen root cef748e: move java lexers to gen root 600887a: move reg exp lexer to gen root fc01dd0: move templates* lexers to gen root cbd64fa: move rest lexer to gen root fd2765c: move python-community lexers to gen root 90d1e84: more compact format when we serialize 8f3acde: use isjavaidentifierpart + enable trigramindex for tests because test appeared 2adc37a: IDEA-65879 "idea" protocol handler to open files directly from a URL (for Mac) 8615aa2: vfs: handling of invalid .jar files efb2fca: test framework: ability to intercept error/warn logs 0fff999: Groovy PSI doesn't depends on LookupElements anymore 92da487: some checkCanceled 876426a: magic constant b01adfe: know recursion manager deleted e9eaa2d: IDEA-124368 (diagnostic) d321dd2: vfs: JAR FS cleanup and minor optimization 4155907: revert IDEA-123049 Rearrange Code is breaking code (Code Style > Java > Arrangement) (1162eb1847279461e17d200416be100351d0c668) f62183c: IDEA-93452 Implement "section" support in rearrange menu: cleanup e833f18: Merge remote-tracking branch 'origin/master' a4f2f0e: create method from method ref: fis for type element qualifier (IDEA-124485) baccb31: diamonds: resolve conflicts based on type params from constructor and containing class (IDEA-123518) f3e1d96: IDEA-124385 1d32716: [by cdr, jeka] eliminating dependencies on utility classes in the code loaded by javac classloader to avoid NCDFE d08964e: IDEA-93452 Implement "section" support in rearrange menu. e16690a: better doctype detection 0645af6: move properties lexer to gen root e851587: svn: Refactored CreateBranchOrTagAction - do not create non-existent folders manually (as "svn copy" supports corresponding "--parents" option) 1162eb1: IDEA-123049 Rearrange Code is breaking code (Code Style > Java > Arrangement) 0c4028d: IDEA-123074 Code formatting. Closing brace. 7456ac5: svn: Use RA_ILLEGAL_URL error code for SVNException in command line info client when passed target does not exist (so CreateBranchOrTagAction.dirExists() works correctly for command line) 1dab5da: Maven: do not unexclude excludeFolders on removal of the respective physical directory related issue - IDEA-120944 Maven integration inconsistently, frequently, and spuriously changes .iml files 8be02cd: Allow to specify ranges which can't be indented by PostprocessReformattingAspect (fixes WI-22725 surround heredoc block with try catch causes parse error) [CR-IC-5135] 18bbc9d: IDEA-115374 "Print file" truncates the code on Mac 0d4ccd0: svn: Added ability to track warnings to SvnBindException 7ec9633: svn: Refactored SvnBindException - use MultiMap to track errors 820d7b2: Switcher: use TW stripe title instead of id 936b1f2: Merge remote-tracking branch 'origin/master' 0c1ec73: IDEA-124163 6fd9884: extract notification groups (IDEA-124454) 71f8a4a: IDEA-124352 b3926cf: EA-55442 - CCE: DomStubBuilder.buildStubTree - a better fix 370e06e: Merge remote-tracking branch 'origin/master' affac73: fixed PY-12731 Creating test profile from context menu unexpectedly creates django tests eed0df2: do not process xs:include for stub building 9bc7805: Revert "Do not wrap border with TitledBorder if there is not title" c255139: IDEA-119926 Completing Statements doesn't go to next line for non conditional statements 6988973: Ensure that indexes of substring are correct in YouTrackCompletionContributor ae4b79f: IDEA-112189 Change YouTrack integration default query 30c5366: MantisFilter violates comparable contact 2255513: svn: Moved execute() methods from CommandUtil to BaseSvnClient 46b8888: in django project show unittest runner and the django one in context menu 8bc1d46: Start plugins wizard #22 fix Windows-specific exceptions (non-initialized Alloy license & NPE during Darcula initialization) 42bc957: svn: Several classes moved to corresponding packages (commit related and exception classes) be188d5: Merge remote-tracking branch 'origin/master' 5e4c305: svn: "svn info" related classes moved to "org.jetbrains.idea.svn.info" package 19f9684: Start plugins wizard #20 icons for categories 93f8c1d: svn: Renamed info clients for both SVNKit and command line (interface and implementations) 063575b: svn: Make info clients (both for SVNKit and command line) satisfy common client factory model c0dd002: svn: Refactored SvnCommandLineInfoClient - do not inherit SvnkitSvnWcClient (just implement corresponding SvnWcClientI interface) 3dac920: svn: "svn status" related classes moved to "org.jetbrains.idea.svn.status" package 0d2131d: svn: Refactored CmdStatusClient - do not use CommandExecutor.myCommandLine directly (use corresponding CommandExecutor methods instead) 7b6f441: svn: Renamed status clients for both SVNKit and command line (interface and implementations) 7f65a87: fix for Kubuntu/Firefox/Gmail (IDEA-67767) 526a02f: fixed PY-12779 Configure Template directories quick-fix: open project structure settings page right away 9ee64e4: fix "go to source/type" — XSourcePosition doesn't provide column number, so, we must override createNavigatable 3f8277c: ability to invoke/reinvoke getter value a9663fe: Merge remote-tracking branch 'origin/master' 4ab4259: IDEA-123691 Minor project wizard edits: icons 16x16 b03f63d: fixed PY-12786 Python Interpreters: extra space in configuration popup from project creation dialog 546f9a5: Revert: IDEA-123160 Find Usages doing nothing in 13.1 (7371df17bd49da0804c600571671fd3a1fe90ec2) 3fbbef8: Merge remote-tracking branch 'origin/master' 2b99821: suppress project loading cancellation if in NonCancellableSection 25e6148: move PushedFilePropertiesUpdater to lang-impl 2782c66: semicolon->space in language-agnostic completion advertisement a9888bb: Merge remote-tracking branch 'origin/master' 779df06: fix messages d8f478c: changed to correct @NotNull 4f2dad1: EA-54648 - assert: PsiWalkingState.elementStarted 066ab8a: EA-55374 - NPE: DataFlowInspectionBase$.applyFix 16b53a5: EA-55442 - CCE: DomStubBuilder.buildStubTree 69fd8e8: EA-55457 - IE: CacheUpdateRunner.waitForAll 6b7c6a0: EA-55708 - NPE: FileEditorManager.getInstance d8e6415: [by cdr] optimizations d38a277: enabled by default c49c422: don't calc content hash id twice f82d4be: Merge remote-tracking branch 'origin/master' 4657702: Merge remote-tracking branch 'origin/master' b275b4e: Netty channel id init is not reliable yet 13aec5a: cleanup 5a75f21: continue WEB-9103 nodejs: show get/set functions in addition 72276e4: init WEB-9103 nodejs: Debugger not showing value of getter 029f89a: testdata fixed 03cba2c: add undo to JBTextField a344d0e: do not dbl substitution (type annotations could be lost after that) b898f91: static method reference completion (IDEA-124043) 78dafa0: dfa: just don't assume initialized final fields are notnull, it doesn't help, but hurts and slows things down (IDEA-124323) b6be84f: simplify HTML markup - updated test data (IDEA-67767) 622b745: simplify HTML markup (IDEA-67767) bcc4a00: junit category support (IDEA-88389) 99b16f3: check for project.isDisposed added to DumbService, unnecessary checks at call sites removed 3f304a3: Animator: test 36d9ddc: Animator: corereview CR-IC-5059 9709e9f: Animator: fix stuck at 0th frame during repeatable animaion 1957b85: SemVer.getParsedVersion added 8e48a46: SemVer.toString added ae2667d: Merge remote-tracking branch 'origin/master' 1be6001: fixed PY-12451 Interpreter added from project creation dialog is not set as project interpreter 5bb9964: fixed possible NPE 9359151: Platform: scrollbars survive background editors initialization 3ca40fc: fixed tests f2b136a: fixed possible NPE 8fde24a: drain file type queue 835ce62: cleanup f2fb07f: removed lang-impl dependency edb88b8: notnull 6dd81f8: NPE when application is already disposed c2545c2: unmute on session finish - spelling 223dffa: IDEA-122962 - Clouds: perform connection test in background 7cc9133: Merge remote-tracking branch 'origin/master' c5bffbd: remove duplicated code (IDEA-67767) a5a9d6a: Merge remote-tracking branch 'origin/master' 1167e96: fixed PY-12543 Project Interpreters: too big or too small details popup cbc40e9: lazy array data loading — API/UI/v8 new backend done WEB-11784 slow nodejs debug: huge array cause 100 cpu debug a892f89: add missing test data 2215544: fix another RTF background colouring issue (IDEA-67767) fd2b253: Merge remote-tracking branch 'origin/master' bc7a7ad: Merge remote-tracking branch 'origin/master' fda4b8a: Merge remote-tracking branch 'origin/master' c8474eb: fix testdata ee2a5d0: introduce: process chained method calls during same occurrences processing (IDEA-124349) 9397fb0: CR-IC-5167 (cleanup) 8dbf638: Cleanup (post-review #330) 91e3ed4: vfs: .jar refresh test extended 115aaf1: vfs: unified attributes loading between JarHandler / JarFS 10efd8b: Cleanup (duplication; readability) 8231d80: IDEA-124032 IDEA plugin: Can't find com.sun.xml.internal.messaging.saaj.soap.LocalStrings bundle: allow bootstrap resources for root IDEA classloader cb808a6: fixed PY-12723 Unable to run tests with Django 1.7b1: "RuntimeError: App registry isn't ready yet." 8c5c0ec: added project.isDisposed check to DumbService 1f2a568: code style 46881e4: Merge remote-tracking branch 'origin/master' 8401fa0: fixed windows path separator 6ae658f: fixed 'project is disposed' 6431234: Github: release editor in tests c726c53: Github: small test cleanup 6d6d583: Platform: ShowFilePathAction correctly handles non-normalized paths (e.g. with ../) 8e1f7a7: Merge remote-tracking branch 'origin/master' 916d90b: Platform: always allow writing module files (IDEA-123899) + typo in test fixed 6e063ee: IDEA-121318 ClosureParamsEnhancer now uses call.getCallVariants() instead of call.resolve(). Erasing instance parameter's types when comparing them. It helps to select the correct one when we are comparing T and Iterable<T>. 637da92: IDEA-123712 Groovy: @language annotation on method parameters with default values reflected methods now have light parameters with original modifier lists 8ffcef2: EA-52671 - assert: AbstractMappingStrategy.processFoldRegion 30d14a3: fix RTF background colouring and make it compatible with Mac (IDEA-67767) 39d7edd: svn: Make status clients (both for SVNKit and command line) satisfy common client factory model 85e3825: Merge remote-tracking branch 'origin/master' 1d31c68: svn: Refactored SvnRecursiveStatusWalker.MyItem - use common client factory model (instead of direct SVNStatusClient usage) 3a14f0a: Use tree set to store remote roots to avoid duplicates. 343ea1c: svn: Refactored SvnRecursiveStatusWalker - methods extracted, warnings fixes 3ca64b6: svn: Merged StatusWalkerPartner interface and StatusWalkerPartnerImpl implementation to single class 93ddbc2: svn: Refactored SvnChangeProvider - use common client factory model (instead of direct SVNStatusClient usage) 416463d: svn: Refactored SvnRecursiveStatusWalker.MyItem.getClient() - removed unnecessary parameters 82af9cb: svn: SvnCommitRunner moved to "checkin" package and renamed b0b7121: svn: Refactored SvnCheckinEnvironment - logic extracted to separate SVNKit and command line clients (common client factory model is used) af5ad7c: svn: Refactored SvnCommitRunner - inlined several parameters (that have same value in all code paths) beb8081: svn: Refactored SvnCheckinEnvironment - moved SVNKit related logic to corresponding SVNKit-scoped method 626d944: svn: Refactored SvnCheckinEnvironment - code simplified, unified code flows for SVNKit and command line c636313: IDEA-124057 Manually load key store set using VM options. Don't modify default context, if Certificate Manager was disabled 4729cf2: svn: Refactored OneShotMergeInfoHelper and OneRecursiveShotMergeInfoWorker - removed unnecessary fields and utilize MergeContext instead 9fe9ac9: svn: Refactored LoadRecentBranchRevisions - removed unnecessary fields and utilize MergeContext instead 562e89d: svn: Refactored MergeCalculatorTask - removed unnecessary fields and utilize data from MergeContext instead c85e79d: svn: Refactored QuickMerge - inner classes moved to separate files (and renamed) 3f10457: IDEA-122845 Add test to check that wrong credentials are recognized when testConnection() is used cf3252c: svn: Refactored QuickMerge - make task classes do not directly depend on QuickMerge instance * MergeContext and QuickMergeInteraction parameters added to all task constructors * several QuickMerge methods moved to corresponding task classes e62ad72: svn: Refactored QuickMerge - merge parameters extracted to separate MergeContext class 482733a: svn: Refactored LatestExistentSearcher - use common client factory model (instead of direct SVNLogClient usage) a260ac4: svn: Refactored DefaultConfigLoader - code simplifications, warnings fixes d899191: svn: Make sure start() method of the task queue (myBranchesLoader) in SvnBranchConfigurationManager is invoked - otherwise runnables passed to queue are not executed a95c8b4: svn: Refactored DefaultConfigLoader - use common client factory model (instead of direct SVNLogClient usage) 1d33a57: svn: Refactored FirstInBranch - use common client factory model (instead of direct SVNLogClient usage) 372a664: svn: Refactored SvnMergeInfoCache - removed inner MyState class (fields added directly to SvnMergeInfoCache) 281cc56: svn: Refactored FirstInBranch - not null, code simplifications (exception handling) ef8bad7: svn: Refactored FirstInBranch - code simplifications, warnings fixes 2104741: svn: Removed unnecessary SvnBranchPointsCalculator.Invertor interface 0b9815c: svn: Refactored SvnBranchPointsCalculator.WrapperInvertor - explicitly use SvnBranchPointsCalculator.BranchCopyData type (instead of being generic) aa5e3fd: svn: Refactored MergerFactory - removed unused methods 9d618f1: svn: Refactored SvnMergeInfoTest - methods extracted, duplication removed, warning fixes 4947243: svn: Refactored SvnMergeInfoTest - methods extracted, lots of duplication removed Change-Id: Id231a4e5444690193a99f454d027ea17f7c2845c
Diffstat (limited to 'plugins/devkit/src')
-rw-r--r--plugins/devkit/src/DevKitBundle.java30
-rw-r--r--plugins/devkit/src/DevKitBundle.properties160
-rw-r--r--plugins/devkit/src/DevKitUseScopeEnlarger.java6
-rw-r--r--plugins/devkit/src/build/PluginBuildConfiguration.java6
-rw-r--r--plugins/devkit/src/build/PluginBuildParticipant.java2
-rw-r--r--plugins/devkit/src/dom/Dependency.java7
-rw-r--r--plugins/devkit/src/dom/DependencyConfigFileConverter.java146
-rw-r--r--plugins/devkit/src/dom/ExtensionPoint.java14
-rw-r--r--plugins/devkit/src/dom/Extensions.java2
-rw-r--r--plugins/devkit/src/dom/IdeaPlugin.java1
-rw-r--r--plugins/devkit/src/dom/impl/ExtensionDomExtender.java5
-rw-r--r--plugins/devkit/src/dom/impl/ExtensionPointImpl.java10
-rw-r--r--plugins/devkit/src/dom/impl/ExtensionsImpl.java10
-rw-r--r--plugins/devkit/src/dom/impl/IdeaPluginConverter.java7
-rw-r--r--plugins/devkit/src/dom/impl/PluginXmlDomFileDescription.java15
-rw-r--r--plugins/devkit/src/inspections/DescriptionCheckerUtil.java53
-rw-r--r--plugins/devkit/src/inspections/DescriptionNotFoundInspectionBase.java128
-rw-r--r--plugins/devkit/src/inspections/DescriptionType.java51
-rw-r--r--plugins/devkit/src/inspections/DevKitInspectionBase.java54
-rw-r--r--plugins/devkit/src/inspections/InspectionDescriptionInfo.java89
-rw-r--r--plugins/devkit/src/inspections/InspectionDescriptionNotFoundInspection.java100
-rw-r--r--plugins/devkit/src/inspections/IntentionDescriptionNotFoundInspection.java111
-rw-r--r--plugins/devkit/src/inspections/PostfixTemplateDescriptionNotFoundInspection.java53
-rw-r--r--plugins/devkit/src/inspections/internal/DontUseNewPairInspection.java70
-rw-r--r--plugins/devkit/src/inspections/internal/UseCoupleInspection.java92
-rw-r--r--plugins/devkit/src/inspections/internal/UseVirtualFileEqualsInspection.java54
-rw-r--r--plugins/devkit/src/inspections/quickfix/ChangeToPairCreateQuickFix.java49
-rw-r--r--plugins/devkit/src/inspections/quickfix/CreateHtmlDescriptionFix.java122
-rw-r--r--plugins/devkit/src/inspections/quickfix/PluginDescriptorChooser.java237
-rw-r--r--plugins/devkit/src/inspections/quickfix/RegisterActionFix.java27
-rw-r--r--plugins/devkit/src/inspections/quickfix/RegisterExtensionFix.java14
-rw-r--r--plugins/devkit/src/inspections/quickfix/RegisterExtensionFixProvider.java96
-rw-r--r--plugins/devkit/src/inspections/quickfix/RegisterInspectionFix.java138
-rw-r--r--plugins/devkit/src/inspections/quickfix/UseCoupleQuickFix.java52
-rw-r--r--plugins/devkit/src/internal/DumpCleanHighlightingTestdataAction.java99
-rw-r--r--plugins/devkit/src/navigation/DescriptionTypeRelatedItemLineMarkerProvider.java159
-rw-r--r--plugins/devkit/src/navigation/ExtensionPointDeclarationRelatedItemLineMarkerProvider.java155
-rw-r--r--plugins/devkit/src/testAssistant/NavigateToTestDataAction.java132
-rw-r--r--plugins/devkit/src/testAssistant/TestCaseAsRelatedFileProvider.java56
-rw-r--r--plugins/devkit/src/testAssistant/TestDataAsRelatedFileProvider.java64
-rw-r--r--plugins/devkit/src/testAssistant/TestDataGroupEditorProvider.java62
-rw-r--r--plugins/devkit/src/testAssistant/TestDataGroupFileEditor.java130
-rw-r--r--plugins/devkit/src/testAssistant/TestDataGroupVirtualFile.java132
-rw-r--r--plugins/devkit/src/testAssistant/TestDataGuessByExistingFilesUtil.java564
-rw-r--r--plugins/devkit/src/testAssistant/TestDataHighlightingPass.java105
-rw-r--r--plugins/devkit/src/testAssistant/TestDataHighlightingPassFactory.java90
-rw-r--r--plugins/devkit/src/testAssistant/TestDataLineMarkerProvider.java147
-rw-r--r--plugins/devkit/src/testAssistant/TestDataNavigationHandler.java191
-rw-r--r--plugins/devkit/src/testAssistant/TestDataReferenceCollector.java184
-rw-r--r--plugins/devkit/src/testAssistant/TestDataReferenceContributor.java167
-rw-r--r--plugins/devkit/src/testAssistant/TestDataRelatedItem.java59
-rw-r--r--plugins/devkit/src/testAssistant/TestLocationDataRule.java149
-rw-r--r--plugins/devkit/src/util/DescriptorUtil.java2
-rw-r--r--plugins/devkit/src/util/ExtensionPointCandidate.java (renamed from plugins/devkit/src/inspections/quickfix/ExtensionPointCandidate.java)24
-rw-r--r--plugins/devkit/src/util/ExtensionPointLocator.java122
-rw-r--r--plugins/devkit/src/util/PsiUtil.java44
56 files changed, 4186 insertions, 662 deletions
diff --git a/plugins/devkit/src/DevKitBundle.java b/plugins/devkit/src/DevKitBundle.java
index 8dfec4be8ee9..013fd2f43e6b 100644
--- a/plugins/devkit/src/DevKitBundle.java
+++ b/plugins/devkit/src/DevKitBundle.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2013 JetBrains s.r.o.
+ * Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,37 +15,21 @@
*/
package org.jetbrains.idea.devkit;
-import com.intellij.CommonBundle;
+import com.intellij.AbstractBundle;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.PropertyKey;
-import java.lang.ref.Reference;
-import java.lang.ref.SoftReference;
-import java.util.ResourceBundle;
-
-/**
- * User: anna
- * Date: Aug 11, 2005
- */
-public class DevKitBundle {
+public class DevKitBundle extends AbstractBundle {
public static String message(@NotNull @PropertyKey(resourceBundle = BUNDLE) String key, @NotNull Object... params) {
- return CommonBundle.message(getBundle(), key, params);
+ return ourInstance.getMessage(key, params);
}
- private static Reference<ResourceBundle> ourBundle;
- @NonNls private static final String BUNDLE = "org.jetbrains.idea.devkit.DevKitBundle";
+ @NonNls private static final String BUNDLE = "DevKitBundle";
+ private static final DevKitBundle ourInstance = new DevKitBundle();
private DevKitBundle() {
- }
-
- private static ResourceBundle getBundle() {
- ResourceBundle bundle = com.intellij.reference.SoftReference.dereference(ourBundle);
- if (bundle == null) {
- bundle = ResourceBundle.getBundle(BUNDLE);
- ourBundle = new SoftReference<ResourceBundle>(bundle);
- }
- return bundle;
+ super(BUNDLE);
}
}
diff --git a/plugins/devkit/src/DevKitBundle.properties b/plugins/devkit/src/DevKitBundle.properties
deleted file mode 100644
index 07d3b2914baf..000000000000
--- a/plugins/devkit/src/DevKitBundle.properties
+++ /dev/null
@@ -1,160 +0,0 @@
-#module type
-module.title=IntelliJ Platform Plugin
-module.description=Plugin modules are used for developing plugins for <b>IntelliJ Platform</b>-based IDEs.<br>\
- They provide <b>IntelliJ Platform Plugin SDK</b> and <b>Run Configuration</b> for running and debugging plugins inside an IDE.
-plugin.descriptor=IntelliJ Platform Plugin Descriptor
-
-#Module Editor - Deployment
-deployment.title=Plugin Deployment
-deployment.cleanup=Clean up {0} directory
-deployment.directory.location=Select {0} Directory Location
-deployment.view.select=Select {0}
-manifest.settings=Manifest Settings
-manifest.selection=Selected {0} will be included in resulting distribution
-manifest.use.user.defined=Use user manifest:
-deployment.view.delete=Delete {0}?
-deployment.view.meta-inf.label=Path to {0}
-suggest.to.delete=Do you want to delete ''{0}''?
-vm.parameters=&VM Options
-program.parameters=&Program Arguments
-
-#Prepare for deployment action
-select.plugin.modules.title=Select modules
-select.plugin.modules.description=Select modules to be prepared for deployment
-prepare.for.deployment.common=Preparing For Deployment
-prepare.for.deployment=Prepare Plugin Module ''{0}'' For Deployment
-prepare.for.deployment.all=Prepare All Plugin Modules For Deployment
-saved.message.common={0} will be saved in selected directory
-saved.message= {0, choice, 1#Zip|2#Jar} for module ''{1}'' was saved to {2}
-success.deployment.message=Plugin Module ''{0}'' Successfully Prepared For Deployment
-success.deployment.message.all=All Plugins Successfully Prepared For Deployment
-
-##Idea Sdk
-sdk.title=IntelliJ Platform Plugin SDK
-sandbox.specification=Please configure the sandbox
-sandbox.home=Sandbox Home
-sandbox.home.label=Sandbox Home:
-sandbox.no.configured=No sandbox specified for IntelliJ Platform Plugin SDK
-sandbox.purpose=Browse folder to put config, system and plugins for target IDE
-
-#errors
-error.occurred=Error Occurred
-sdk.no.specified=No SDK specified for plugin module ''{0}''
-sdk.type.incorrect=Wrong SDK type for plugin module ''{0}''.
-sdk.type.incorrect.common=Wrong SDK type for plugin module
-incorrect.dependency.non-plugin-module=The non-plugin module ''{0}'' cannot depend on plugin module ''{1}''.
-incorrect.dependency.not-declared=The plugin module ''{0}'' doesn''t declare the dependency on ''{1}'' in its plugin.xml.
-error.file.not.found=File not Found
-error.file.not.found.message=File ''{0}'' not found
-error.no.plugin.xml=No plugin.xml file found
-error.plugin.xml.readonly=The plugin.xml file is read-only
-
-#run configurations
-run.configuration.classpath.from.module.choose=Use Classpath and JDK from Module:
-run.configuration.no.module.specified=No plugin module specified for configuration
-run.configuration.title=Plugin
-run.configuration.type.description=Plugin Sandbox Environment
-idea.log.tab.title=IDEA LOG
-
-#Misc
-info.message=Info
-create.smth=Create {0}
-show.smth=&Show {0}
-presentable.plugin.module.name=Plugin Module ''{0}''
-
-action.MakeJarAction.text=Prepare To Deploy
-action.MakeAllJarsAction.text=Prepare All Plugins To Deploy
-
-dont.add.idea.libs.to.classpath=IDE-related libraries ({0}) must not be added to the module classpath. Please add them to the IntelliJ Platform Plugin SDK instead.
-new.action.id=&Action ID:
-new.action.description=&Description:
-new.action.class.name=&Class Name:
-new.action.text=&Name:
-new.action.add.to.group=Add to Group
-new.action.group.actions=Act&ions:
-new.action.group.anchor=Anchor:
-new.action.group.anchor.first=&First
-new.action.group.anchor.=&Last
-new.action.group.anchor.before=&Before
-new.action.group.anchor.after=Af&ter
-new.action.group.groups=&Groups:
-new.action.keyboard.shortcuts=Keyboard Shortcuts
-new.action.keyboard.first=First:
-new.action.keyboard.second=Second:
-new.action.keyboard.clear=X
-new.action.keyboard.clear.tooltip=Clear shortcut
-command.implement.externalizable=Implement Externalizable
-new.menu.action.text=Action
-new.menu.action.description=Create New Action
-new.action.error=Cannot create action
-new.action.command=Create Action
-new.action.action.name=Creating new action: {0}.{1}
-new.action.dialog.title=New Action
-new.menu.application.component.text=Application Component
-new.menu.application.component.description=Create New Application Component
-new.application.component.error=Cannot create application component
-new.application.component.command=Create Application Component
-new.application.component.prompt.title=New Application Component
-new.application.component.prompt=Enter new application component name:
-new.application.component.action.name=Creating new application component: {0}.{1}
-new.menu.module.component.text=Module Component
-new.menu.module.component.description=Create New Module Component
-new.module.component.error=Cannot create module component
-new.module.component.command=Create Module Component
-new.module.component.prompt.title=New Module Component
-new.module.component.prompt=Enter new module component name:
-new.module.component.action.name=Creating new module component: {0}.{1}
-new.menu.project.component.text=Project Component
-new.menu.project.component.description=Create New Project Component
-new.project.component.error=Cannot create project component
-new.project.component.command=Create Project Component
-new.project.component.prompt.title=New Project Component
-new.project.component.prompt=Enter new project component name:
-new.project.component.action.name=Creating new project component: {0}.{1}
-
-select.plugin.modules.to.patch=Select Plugin Modules to Patch
-
-keyword.extend=extend
-keyword.implement=implement
-class.action=action
-class.interface=interface
-class.implementation=implementation
-
-inspections.group.name=Plugin DevKit
-inspections.registration.problems.name=Component type mismatch
-inspections.registration.problems.option.check.plugin.xml=Check Plugin Descriptor (plugin.xml)
-inspections.registration.problems.option.check.java.actions=Check Java Actions
-inspections.registration.problems.option.check.java.code=Check Java Code
-inspections.registration.problems.quickfix.read-only=Class ''{0}'' is read-only
-inspections.registration.problems.quickfix.make.public=Make {0} public
-inspections.registration.problems.quickfix.create.constructor=Create no-argument constructor
-
-inspections.registration.problems.incompatible.message=According to its registration in plugin.xml, the class should {0} ''{1}''
-inspections.registration.problems.abstract=Plugin component class must not be abstract
-inspections.registration.problems.missing.noarg.ctor=Action class must have a no-argument constructor
-
-inspections.registration.problems.missing.implementation.class=Missing implementation-class
-inspections.registration.problems.cannot.resolve.class=Cannot resolve {0} class
-inspections.registration.problems.component.should.implement=Component class must implement ''{0}''
-inspections.registration.problems.component.incompatible.interface=Component class is not assignable to its interface-class ''{0}''
-inspections.registration.problems.component.duplicate.interface=Multiple components with the same interface-class are not allowed
-inspections.registration.problems.action.incompatible.class=Action class must extend ''{0}''
-
-inspections.component.not.registered.name=Component/Action not registered
-inspections.component.not.registered.message={0} is not registered in plugin.xml
-inspections.component.not.registered.option.check.actions=Check Actions
-inspections.component.not.registered.option.ignore.non.public=Ignore non-public classes
-inspections.component.not.registered.quickfix.family=Register Component
-inspections.component.not.registered.quickfix.name=Register {0}
-inspections.component.not.registered.quickfix.error=Cannot Register {0}
-InspectionUseGrayColor=Use Gray
-ant.build.jar.comment=Build archive for plugin ''{0}''
-ant.build.jar.description=Build plugin archive for module ''{0}''
-project.title=Plugin Project
-no.java.sdk.for.idea.sdk.found=No Java SDK of appropriate version found. In addition to the IntelliJ Platform Plugin SDK, you need to define a JDK with the same Java version ({0}).
-no.idea.sdk.version.found=Failed to detect JDK version required for IntelliJ Platform Plugin SDK.
-group.PluginDeployActions.text=Plugin Deployment Actions
-
-error.cannot.resolve.plugin=Cannot resolve plugin {0}
-create.description.file=Create Description File
-select.target.location.of.description=Select target location of {0}
diff --git a/plugins/devkit/src/DevKitUseScopeEnlarger.java b/plugins/devkit/src/DevKitUseScopeEnlarger.java
index f34c40e57e89..6b55133eb251 100644
--- a/plugins/devkit/src/DevKitUseScopeEnlarger.java
+++ b/plugins/devkit/src/DevKitUseScopeEnlarger.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2011 JetBrains s.r.o.
+ * Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,6 +15,7 @@
*/
package org.jetbrains.idea.devkit;
+import com.intellij.ide.highlighter.XmlFileType;
import com.intellij.pom.PomTarget;
import com.intellij.pom.PomTargetPsiElement;
import com.intellij.psi.PsiElement;
@@ -37,7 +38,8 @@ public class DevKitUseScopeEnlarger extends UseScopeEnlarger {
if (target instanceof DomTarget) {
DomElement domElement = ((DomTarget)target).getDomElement();
if (domElement instanceof ExtensionPoint) {
- return GlobalSearchScope.allScope(element.getProject());
+ return GlobalSearchScope.getScopeRestrictedByFileTypes(GlobalSearchScope.allScope(element.getProject()),
+ XmlFileType.INSTANCE);
}
}
}
diff --git a/plugins/devkit/src/build/PluginBuildConfiguration.java b/plugins/devkit/src/build/PluginBuildConfiguration.java
index 5b5dc7bf8394..3604b0f0d13a 100644
--- a/plugins/devkit/src/build/PluginBuildConfiguration.java
+++ b/plugins/devkit/src/build/PluginBuildConfiguration.java
@@ -39,6 +39,7 @@ import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.TestOnly;
import org.jetbrains.idea.devkit.DevKitBundle;
import org.jetbrains.idea.devkit.module.PluginDescriptorConstants;
@@ -110,6 +111,11 @@ public class PluginBuildConfiguration implements ModuleComponent, JDOMExternaliz
return myPluginXmlContainer.getConfigFile(PluginDescriptorConstants.META_DATA);
}
+ @TestOnly
+ public void setPluginXmlFromVirtualFile(VirtualFile virtualFile) {
+ myPluginXmlContainer.getConfiguration().replaceConfigFile(PluginDescriptorConstants.META_DATA, virtualFile.getUrl());
+ }
+
private void createDescriptor(final String url) {
final ConfigFileInfo descriptor = new ConfigFileInfo(PluginDescriptorConstants.META_DATA, url);
myPluginXmlContainer.getConfiguration().addConfigFile(descriptor);
diff --git a/plugins/devkit/src/build/PluginBuildParticipant.java b/plugins/devkit/src/build/PluginBuildParticipant.java
index a5998966dba6..75f6c7cf3046 100644
--- a/plugins/devkit/src/build/PluginBuildParticipant.java
+++ b/plugins/devkit/src/build/PluginBuildParticipant.java
@@ -106,7 +106,7 @@ public class PluginBuildParticipant extends BuildParticipant {
final DomElement domElement = DomManager.getDomManager(xmlFile.getProject()).getDomElement(document.getRootTag());
if (domElement instanceof IdeaPlugin) {
for(Dependency dependency: ((IdeaPlugin)domElement).getDependencies()) {
- final String file = dependency.getConfigFile().getValue();
+ final String file = dependency.getConfigFile().getStringValue();
if (file != null) {
final VirtualFile virtualFile = configFile.getVirtualFile();
assert virtualFile != null;
diff --git a/plugins/devkit/src/dom/Dependency.java b/plugins/devkit/src/dom/Dependency.java
index 5b3a90617a08..b48384d0cfcb 100644
--- a/plugins/devkit/src/dom/Dependency.java
+++ b/plugins/devkit/src/dom/Dependency.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2013 JetBrains s.r.o.
+ * Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@
package org.jetbrains.idea.devkit.dom;
+import com.intellij.openapi.paths.PathReference;
import com.intellij.util.xml.Convert;
import com.intellij.util.xml.GenericAttributeValue;
import com.intellij.util.xml.GenericDomValue;
@@ -29,9 +30,11 @@ import org.jetbrains.idea.devkit.dom.impl.IdeaPluginConverter;
@Stubbed
@Convert(IdeaPluginConverter.class)
public interface Dependency extends GenericDomValue<IdeaPlugin> {
+
@NotNull
GenericAttributeValue<Boolean> getOptional();
@NotNull
- GenericAttributeValue<String> getConfigFile();
+ @Convert(DependencyConfigFileConverter.class)
+ GenericAttributeValue<PathReference> getConfigFile();
}
diff --git a/plugins/devkit/src/dom/DependencyConfigFileConverter.java b/plugins/devkit/src/dom/DependencyConfigFileConverter.java
new file mode 100644
index 000000000000..214d21e6e819
--- /dev/null
+++ b/plugins/devkit/src/dom/DependencyConfigFileConverter.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.dom;
+
+import com.intellij.ide.highlighter.XmlFileType;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleUtilCore;
+import com.intellij.openapi.paths.PathReference;
+import com.intellij.openapi.paths.PathReferenceManager;
+import com.intellij.openapi.paths.PathReferenceProvider;
+import com.intellij.openapi.paths.StaticPathReferenceProvider;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.util.Condition;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiFileSystemItem;
+import com.intellij.psi.PsiReference;
+import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReferenceSet;
+import com.intellij.psi.xml.XmlElement;
+import com.intellij.psi.xml.XmlFile;
+import com.intellij.util.Processor;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.xml.ConvertContext;
+import com.intellij.util.xml.DomFileElement;
+import com.intellij.util.xml.DomUtil;
+import com.intellij.util.xml.GenericAttributeValue;
+import com.intellij.util.xml.converters.PathReferenceConverter;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.devkit.util.DescriptorUtil;
+import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes;
+
+import java.util.*;
+
+public class DependencyConfigFileConverter extends PathReferenceConverter {
+
+ private static final PathReferenceProvider ourProvider = new StaticPathReferenceProvider(null) {
+
+ @Override
+ public boolean createReferences(@NotNull final PsiElement psiElement,
+ int offset,
+ String text,
+ @NotNull List<PsiReference> references,
+ boolean soft) {
+ FileReferenceSet set = new FileReferenceSet(text, psiElement, offset, null,
+ true, true,
+ new FileType[]{XmlFileType.INSTANCE}) {
+
+ private final Condition<PsiFileSystemItem> PLUGIN_XML_CONDITION = new Condition<PsiFileSystemItem>() {
+ @Override
+ public boolean value(PsiFileSystemItem item) {
+ return !item.isDirectory() &&
+ !item.equals(getContainingFile()) &&
+ (item instanceof XmlFile && DescriptorUtil.isPluginXml((PsiFile)item)) &&
+ !isAlreadyUsed((XmlFile)item);
+ }
+ };
+
+ private boolean isAlreadyUsed(final XmlFile xmlFile) {
+ final PsiFile file = getContainingFile();
+ if (!(file instanceof XmlFile)) return false;
+ final DomFileElement<IdeaPlugin> ideaPlugin = DescriptorUtil.getIdeaPlugin((XmlFile)file);
+ if (ideaPlugin == null) return false;
+ return !ContainerUtil.process(ideaPlugin.getRootElement().getDependencies(), new Processor<Dependency>() {
+ @Override
+ public boolean process(Dependency dependency) {
+ final GenericAttributeValue<PathReference> configFileAttribute = dependency.getConfigFile();
+ if (!DomUtil.hasXml(configFileAttribute)) return true;
+ final PathReference pathReference = configFileAttribute.getValue();
+ if (pathReference == null) return true;
+ return !xmlFile.equals(pathReference.resolve());
+ }
+ });
+ }
+
+ @NotNull
+ @Override
+ public Collection<PsiFileSystemItem> computeDefaultContexts() {
+ final PsiFile containingFile = getContainingFile();
+ if (containingFile == null) {
+ return super.getDefaultContexts();
+ }
+
+ final Module module = ModuleUtilCore.findModuleForPsiElement(getElement());
+ if (module == null) {
+ return super.getDefaultContexts();
+ }
+
+ final Set<VirtualFile> roots = new HashSet<VirtualFile>();
+ final VirtualFile parent = containingFile.getVirtualFile().getParent();
+ roots.add(parent);
+
+ for (VirtualFile sourceRoot : ModuleRootManager.getInstance(module).getSourceRoots(JavaModuleSourceRootTypes.PRODUCTION)) {
+ roots.add(sourceRoot.findChild("META-INF"));
+ }
+ return toFileSystemItems(roots);
+ }
+
+ @Override
+ protected boolean isSoft() {
+ return true;
+ }
+
+ @Override
+ protected Condition<PsiFileSystemItem> getReferenceCompletionFilter() {
+ return PLUGIN_XML_CONDITION;
+ }
+ };
+ Collections.addAll(references, set.getAllReferences());
+ return true;
+ }
+ };
+
+ @Override
+ public PathReference fromString(@Nullable String s, ConvertContext context) {
+ final XmlElement element = context.getXmlElement();
+ final Module module = context.getModule();
+ if (s == null || element == null || module == null) {
+ return null;
+ }
+ return PathReferenceManager.getInstance().getCustomPathReference(s, module, element, ourProvider);
+ }
+
+ @NotNull
+ @Override
+ public PsiReference[] createReferences(@NotNull PsiElement psiElement, boolean soft) {
+ return PathReferenceManager.getInstance().createCustomReferences(psiElement,
+ soft,
+ ourProvider);
+ }
+}
diff --git a/plugins/devkit/src/dom/ExtensionPoint.java b/plugins/devkit/src/dom/ExtensionPoint.java
index 6a5074f7ce02..ddd2573497a7 100644
--- a/plugins/devkit/src/dom/ExtensionPoint.java
+++ b/plugins/devkit/src/dom/ExtensionPoint.java
@@ -65,6 +65,20 @@ public interface ExtensionPoint extends DomElement {
With addWith();
+ /**
+ * Returns the fully qualified EP name
+ *
+ * @return {@code PluginID.name} or {@code qualifiedName}.
+ * @since 14
+ */
+ @NotNull
+ String getEffectiveQualifiedName();
+
+ /**
+ * Returns the actually defined name.
+ *
+ * @return {@link #getName()} if defined, {@link #getQualifiedName()} otherwise.
+ */
@NotNull
String getEffectiveName();
diff --git a/plugins/devkit/src/dom/Extensions.java b/plugins/devkit/src/dom/Extensions.java
index f07357e56fe4..738a3a792079 100644
--- a/plugins/devkit/src/dom/Extensions.java
+++ b/plugins/devkit/src/dom/Extensions.java
@@ -42,7 +42,7 @@ public interface Extensions extends DomElement {
Extension addExtension();
- Extension addExtension(String name);
+ Extension addExtension(String qualifiedEPName);
@NotNull
String getEpPrefix();
diff --git a/plugins/devkit/src/dom/IdeaPlugin.java b/plugins/devkit/src/dom/IdeaPlugin.java
index f1dae2fab0d1..d1d3c355f93f 100644
--- a/plugins/devkit/src/dom/IdeaPlugin.java
+++ b/plugins/devkit/src/dom/IdeaPlugin.java
@@ -105,7 +105,6 @@ public interface IdeaPlugin extends DomElement {
@NotNull
@SubTagList("extensions")
- @Stubbed
List<Extensions> getExtensions();
Extensions addExtensions();
diff --git a/plugins/devkit/src/dom/impl/ExtensionDomExtender.java b/plugins/devkit/src/dom/impl/ExtensionDomExtender.java
index 7fdfdeecc632..f5c83ccb66bc 100644
--- a/plugins/devkit/src/dom/impl/ExtensionDomExtender.java
+++ b/plugins/devkit/src/dom/impl/ExtensionDomExtender.java
@@ -147,6 +147,11 @@ public class ExtensionDomExtender extends DomExtender<Extensions> {
}
}
+ @Override
+ public boolean supportsStubs() {
+ return false;
+ }
+
private static Set<IdeaPlugin> getVisiblePlugins(IdeaPlugin ideaPlugin) {
Set<IdeaPlugin> result = ContainerUtil.newHashSet();
MultiMap<String, IdeaPlugin> byId = getPluginMap(ideaPlugin.getManager().getProject());
diff --git a/plugins/devkit/src/dom/impl/ExtensionPointImpl.java b/plugins/devkit/src/dom/impl/ExtensionPointImpl.java
index f4eb409e6164..1287948c2752 100644
--- a/plugins/devkit/src/dom/impl/ExtensionPointImpl.java
+++ b/plugins/devkit/src/dom/impl/ExtensionPointImpl.java
@@ -45,4 +45,14 @@ public abstract class ExtensionPointImpl implements ExtensionPoint {
return StringUtil.notNullize(plugin.getPluginId(), "com.intellij");
}
+
+ @NotNull
+ @Override
+ public String getEffectiveQualifiedName() {
+ if (DomUtil.hasXml(getQualifiedName())) {
+ return getQualifiedName().getRawText();
+ }
+
+ return getNamePrefix() + "." + getName().getRawText();
+ }
}
diff --git a/plugins/devkit/src/dom/impl/ExtensionsImpl.java b/plugins/devkit/src/dom/impl/ExtensionsImpl.java
index e52cdca34c2f..bf915b31edbd 100644
--- a/plugins/devkit/src/dom/impl/ExtensionsImpl.java
+++ b/plugins/devkit/src/dom/impl/ExtensionsImpl.java
@@ -15,10 +15,12 @@
*/
package org.jetbrains.idea.devkit.dom.impl;
+import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.xml.XmlTag;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.idea.devkit.dom.Extension;
import org.jetbrains.idea.devkit.dom.Extensions;
+import org.jetbrains.idea.devkit.dom.IdeaPlugin;
/**
* @author Dmitry Avdeev
@@ -27,16 +29,20 @@ import org.jetbrains.idea.devkit.dom.Extensions;
public abstract class ExtensionsImpl implements Extensions {
@Override
- public Extension addExtension(String name) {
+ public Extension addExtension(String qualifiedEPName) {
Extension extension = addExtension();
XmlTag tag = extension.getXmlTag();
- tag.setName(name.substring(getDefaultExtensionNs().getStringValue().length() + 1));
+ tag.setName(StringUtil.trimStart(qualifiedEPName, getEpPrefix()));
return extension;
}
@NotNull
public String getEpPrefix() {
String prefix = getDefaultExtensionNs().getStringValue();
+ if (prefix == null) {
+ final IdeaPlugin ideaPlugin = getParentOfType(IdeaPlugin.class, true);
+ prefix = ideaPlugin == null ? null : StringUtil.notNullize(ideaPlugin.getPluginId(), "com.intellij");
+ }
if (prefix == null) prefix = getXmlns().getStringValue();
return prefix != null ? prefix + "." : "";
}
diff --git a/plugins/devkit/src/dom/impl/IdeaPluginConverter.java b/plugins/devkit/src/dom/impl/IdeaPluginConverter.java
index 82b6b0dbecd1..1020f250d1c2 100644
--- a/plugins/devkit/src/dom/impl/IdeaPluginConverter.java
+++ b/plugins/devkit/src/dom/impl/IdeaPluginConverter.java
@@ -21,6 +21,7 @@ import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Condition;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.GlobalSearchScopesCore;
+import com.intellij.psi.search.ProjectScope;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.xml.ConvertContext;
@@ -34,7 +35,6 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.devkit.DevKitBundle;
import org.jetbrains.idea.devkit.dom.IdeaPlugin;
import org.jetbrains.idea.devkit.dom.PluginModule;
-import org.jetbrains.idea.devkit.util.PsiUtil;
import java.util.Collection;
import java.util.Collections;
@@ -108,8 +108,9 @@ public class IdeaPluginConverter extends ResolvingConverter<IdeaPlugin> {
public static Collection<IdeaPlugin> getAllPlugins(final Project project) {
if (DumbService.isDumb(project)) return Collections.emptyList();
- GlobalSearchScope scope = PsiUtil.isIdeaProject(project) ?
- GlobalSearchScopesCore.projectProductionScope(project) : GlobalSearchScope.allScope(project);
+
+ GlobalSearchScope scope = GlobalSearchScopesCore.projectProductionScope(project).
+ union(ProjectScope.getLibrariesScope(project));
List<DomFileElement<IdeaPlugin>> files = DomService.getInstance().getFileElements(IdeaPlugin.class, project, scope);
return ContainerUtil.map(files, new Function<DomFileElement<IdeaPlugin>, IdeaPlugin>() {
public IdeaPlugin fun(DomFileElement<IdeaPlugin> ideaPluginDomFileElement) {
diff --git a/plugins/devkit/src/dom/impl/PluginXmlDomFileDescription.java b/plugins/devkit/src/dom/impl/PluginXmlDomFileDescription.java
index f0dd66b5f8c4..80a3aabce8f3 100644
--- a/plugins/devkit/src/dom/impl/PluginXmlDomFileDescription.java
+++ b/plugins/devkit/src/dom/impl/PluginXmlDomFileDescription.java
@@ -49,6 +49,19 @@ public class PluginXmlDomFileDescription extends DomFileDescription<IdeaPlugin>
else if (element instanceof IdeaVersion) {
annotateIdeaVersion((IdeaVersion)element, holder);
}
+ else if (element instanceof Extensions) {
+ annotateExtensions((Extensions)element, holder);
+ }
+ }
+
+ private void annotateExtensions(Extensions extensions, DomElementAnnotationHolder holder) {
+ final GenericAttributeValue<IdeaPlugin> xmlnsAttribute = extensions.getXmlns();
+ if (!DomUtil.hasXml(xmlnsAttribute)) return;
+
+ final Annotation annotation = holder.createAnnotation(xmlnsAttribute,
+ HighlightSeverity.WARNING,
+ "Use defaultExtensionNs instead");
+ annotation.setHighlightType(ProblemHighlightType.LIKE_DEPRECATED);
}
private void annotateIdeaVersion(IdeaVersion ideaVersion, DomElementAnnotationHolder holder) {
@@ -106,6 +119,6 @@ public class PluginXmlDomFileDescription extends DomFileDescription<IdeaPlugin>
@Override
public int getStubVersion() {
- return 2;
+ return 3;
}
}
diff --git a/plugins/devkit/src/inspections/DescriptionCheckerUtil.java b/plugins/devkit/src/inspections/DescriptionCheckerUtil.java
new file mode 100644
index 000000000000..d23e95cf148d
--- /dev/null
+++ b/plugins/devkit/src/inspections/DescriptionCheckerUtil.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.inspections;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.JavaPsiFacade;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiDirectory;
+import com.intellij.psi.PsiPackage;
+import com.intellij.psi.search.GlobalSearchScope;
+import org.jetbrains.annotations.Nullable;
+
+public class DescriptionCheckerUtil {
+
+ public static PsiDirectory[] getDescriptionsDirs(Module module,
+ DescriptionType descriptionType) {
+ final JavaPsiFacade javaPsiFacade = JavaPsiFacade.getInstance(module.getProject());
+ final PsiPackage psiPackage = javaPsiFacade.findPackage(descriptionType.getDescriptionFolder());
+ if (psiPackage != null) {
+ return psiPackage.getDirectories(GlobalSearchScope.moduleWithDependenciesScope(module));
+ }
+ return PsiDirectory.EMPTY_ARRAY;
+ }
+
+ @Nullable
+ public static String getDescriptionDirName(PsiClass aClass) {
+ String descriptionDir = "";
+ PsiClass each = aClass;
+ while (each != null) {
+ String name = each.getName();
+ if (StringUtil.isEmptyOrSpaces(name)) {
+ return null;
+ }
+ descriptionDir = name + descriptionDir;
+ each = each.getContainingClass();
+ }
+ return descriptionDir;
+ }
+}
diff --git a/plugins/devkit/src/inspections/DescriptionNotFoundInspectionBase.java b/plugins/devkit/src/inspections/DescriptionNotFoundInspectionBase.java
new file mode 100644
index 000000000000..a5074f0ef6e0
--- /dev/null
+++ b/plugins/devkit/src/inspections/DescriptionNotFoundInspectionBase.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.inspections;
+
+
+import com.intellij.codeInspection.InspectionManager;
+import com.intellij.codeInspection.LocalQuickFix;
+import com.intellij.codeInspection.ProblemDescriptor;
+import com.intellij.codeInspection.ProblemHighlightType;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleUtilCore;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.*;
+import com.intellij.psi.search.GlobalSearchScope;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.idea.devkit.inspections.quickfix.CreateHtmlDescriptionFix;
+import org.jetbrains.idea.devkit.util.PsiUtil;
+
+abstract class DescriptionNotFoundInspectionBase extends DevKitInspectionBase {
+
+ private final DescriptionType myDescriptionType;
+
+ protected DescriptionNotFoundInspectionBase(DescriptionType descriptionType) {
+ myDescriptionType = descriptionType;
+ }
+
+ @Override
+ public ProblemDescriptor[] checkClass(@NotNull PsiClass aClass, @NotNull InspectionManager manager, boolean isOnTheFly) {
+ final Project project = aClass.getProject();
+ final PsiIdentifier nameIdentifier = aClass.getNameIdentifier();
+ final Module module = ModuleUtilCore.findModuleForPsiElement(aClass);
+
+ if (nameIdentifier == null || module == null || !PsiUtil.isInstantiable(aClass)) return null;
+
+ final PsiClass base = JavaPsiFacade.getInstance(project).findClass(getClassName(), GlobalSearchScope.allScope(project));
+ if (base == null || !aClass.isInheritor(base, true)) return null;
+
+ String descriptionDir = DescriptionCheckerUtil.getDescriptionDirName(aClass);
+ if (StringUtil.isEmptyOrSpaces(descriptionDir)) {
+ return null;
+ }
+
+ for (PsiDirectory description : getDescriptionsDirs(module)) {
+ PsiDirectory dir = description.findSubdirectory(descriptionDir);
+ if (dir == null) continue;
+ final PsiFile descr = dir.findFile("description.html");
+ if (descr != null) {
+ if (!hasBeforeAndAfterTemplate(dir.getVirtualFile())) {
+ PsiElement problem = aClass.getNameIdentifier();
+ ProblemDescriptor problemDescriptor = manager.createProblemDescriptor(problem == null ? nameIdentifier : problem,
+ getHasNotBeforeAfterError(),
+ isOnTheFly,
+ ProblemHighlightType.GENERIC_ERROR_OR_WARNING, false);
+ return new ProblemDescriptor[]{problemDescriptor};
+ }
+
+ return null;
+ }
+ }
+
+
+ final PsiElement problem = aClass.getNameIdentifier();
+ final ProblemDescriptor problemDescriptor = manager
+ .createProblemDescriptor(problem == null ? nameIdentifier : problem,
+ getHasNotDescriptionError(), isOnTheFly, new LocalQuickFix[]{getFix(module, descriptionDir)},
+ ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
+ return new ProblemDescriptor[]{problemDescriptor};
+ }
+
+ protected CreateHtmlDescriptionFix getFix(Module module, String descriptionDir) {
+ return new CreateHtmlDescriptionFix(descriptionDir, module, myDescriptionType);
+ }
+
+ private static boolean hasBeforeAndAfterTemplate(@NotNull VirtualFile dir) {
+ boolean hasBefore = false;
+ boolean hasAfter = false;
+
+ for (VirtualFile file : dir.getChildren()) {
+ String name = file.getName();
+ if (name.endsWith(".template")) {
+ if (name.startsWith("before.")) {
+ hasBefore = true;
+ }
+ else if (name.startsWith("after.")) {
+ hasAfter = true;
+ }
+ }
+ }
+
+ return hasBefore && hasAfter;
+ }
+
+ @Override
+ public boolean isEnabledByDefault() {
+ return true;
+ }
+
+ @NotNull
+ protected String getClassName() {
+ return myDescriptionType.getClassName();
+ }
+
+ @NotNull
+ protected PsiDirectory[] getDescriptionsDirs(@NotNull Module module) {
+ return DescriptionCheckerUtil.getDescriptionsDirs(module, myDescriptionType);
+ }
+
+ @NotNull
+ protected abstract String getHasNotDescriptionError();
+
+ @NotNull
+ protected abstract String getHasNotBeforeAfterError();
+}
diff --git a/plugins/devkit/src/inspections/DescriptionType.java b/plugins/devkit/src/inspections/DescriptionType.java
new file mode 100644
index 000000000000..ea5a88977b1f
--- /dev/null
+++ b/plugins/devkit/src/inspections/DescriptionType.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.inspections;
+
+import com.intellij.codeInsight.intention.IntentionAction;
+import com.intellij.codeInsight.template.postfix.templates.PostfixTemplate;
+import com.intellij.codeInspection.InspectionProfileEntry;
+
+public enum DescriptionType {
+
+ INTENTION(IntentionAction.class.getName(), "intentionDescriptions", true),
+ INSPECTION(InspectionProfileEntry.class.getName(), "inspectionDescriptions", false),
+ POSTFIX_TEMPLATES(PostfixTemplate.class.getName(), "postfixTemplates", true);
+
+ private final String myClassName;
+ private final String myDescriptionFolder;
+ private final boolean myFixedDescriptionFilename;
+
+ DescriptionType(String className,
+ String descriptionFolder,
+ boolean fixedDescriptionFilename) {
+ myFixedDescriptionFilename = fixedDescriptionFilename;
+ myClassName = className;
+ myDescriptionFolder = descriptionFolder;
+ }
+
+ public String getClassName() {
+ return myClassName;
+ }
+
+ public String getDescriptionFolder() {
+ return myDescriptionFolder;
+ }
+
+ public boolean isFixedDescriptionFilename() {
+ return myFixedDescriptionFilename;
+ }
+}
diff --git a/plugins/devkit/src/inspections/DevKitInspectionBase.java b/plugins/devkit/src/inspections/DevKitInspectionBase.java
index 9ded8e216fc0..c27c77800c10 100644
--- a/plugins/devkit/src/inspections/DevKitInspectionBase.java
+++ b/plugins/devkit/src/inspections/DevKitInspectionBase.java
@@ -20,15 +20,21 @@ import com.intellij.openapi.actionSystem.ActionGroup;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtil;
+import com.intellij.openapi.paths.PathReference;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.xml.*;
import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.xml.DomFileElement;
+import com.intellij.util.xml.DomUtil;
+import com.intellij.util.xml.GenericAttributeValue;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.devkit.DevKitBundle;
+import org.jetbrains.idea.devkit.dom.Dependency;
+import org.jetbrains.idea.devkit.dom.IdeaPlugin;
import org.jetbrains.idea.devkit.module.PluginModuleType;
import org.jetbrains.idea.devkit.util.ActionType;
import org.jetbrains.idea.devkit.util.ComponentType;
@@ -62,7 +68,8 @@ public abstract class DevKitInspectionBase extends BaseJavaLocalInspectionTool {
if (PluginModuleType.isOfType(module)) {
return checkModule(module, psiClass, null, includeActions);
- } else {
+ }
+ else {
Set<PsiClass> types = null;
final List<Module> modules = PluginModuleType.getCandidateModules(module);
for (Module m : modules) {
@@ -78,20 +85,28 @@ public abstract class DevKitInspectionBase extends BaseJavaLocalInspectionTool {
if (!DescriptorUtil.isPluginXml(pluginXml)) return types;
assert pluginXml != null;
- final XmlDocument document = pluginXml.getDocument();
- assert document != null;
-
- final XmlTag rootTag = document.getRootTag();
- assert rootTag != null;
-
final String qualifiedName = psiClass.getQualifiedName();
if (qualifiedName != null) {
final RegistrationTypeFinder finder = new RegistrationTypeFinder(psiClass, types);
- DescriptorUtil.processComponents(rootTag, finder);
-
- if (includeActions) {
- DescriptorUtil.processActions(rootTag, finder);
+ // "main" plugin.xml
+ processPluginXml(pluginXml, finder, includeActions);
+
+ // <depends> plugin.xml files
+ final DomFileElement<IdeaPlugin> fileElement = DescriptorUtil.getIdeaPlugin(pluginXml);
+ for (Dependency dependency : fileElement.getRootElement().getDependencies()) {
+ final GenericAttributeValue<PathReference> configFileAttribute = dependency.getConfigFile();
+ if (!DomUtil.hasXml(configFileAttribute)) continue;
+
+ final PathReference configFile = configFileAttribute.getValue();
+ if (configFile != null) {
+ final PsiElement resolve = configFile.resolve();
+ if (!(resolve instanceof XmlFile)) continue;
+ final XmlFile depPluginXml = (XmlFile)resolve;
+ if (DescriptorUtil.isPluginXml(depPluginXml)) {
+ processPluginXml(depPluginXml, finder, includeActions);
+ }
+ }
}
types = finder.getTypes();
@@ -100,6 +115,18 @@ public abstract class DevKitInspectionBase extends BaseJavaLocalInspectionTool {
return types;
}
+ private static void processPluginXml(XmlFile xmlFile, RegistrationTypeFinder finder, boolean includeActions) {
+ final XmlDocument document = xmlFile.getDocument();
+ if (document == null) return;
+ final XmlTag rootTag = document.getRootTag();
+ if (rootTag == null) return;
+
+ DescriptorUtil.processComponents(rootTag, finder);
+ if (includeActions) {
+ DescriptorUtil.processActions(rootTag, finder);
+ }
+ }
+
@Nullable
protected static PsiElement getAttValueToken(@NotNull XmlAttribute attribute) {
final XmlAttributeValue valueElement = attribute.getValueElement();
@@ -132,13 +159,14 @@ public abstract class DevKitInspectionBase extends BaseJavaLocalInspectionTool {
return false;
}
- static class RegistrationTypeFinder implements ComponentType.Processor, ActionType.Processor {
+
+ private static class RegistrationTypeFinder implements ComponentType.Processor, ActionType.Processor {
private Set<PsiClass> myTypes;
private final String myQualifiedName;
private final PsiManager myManager;
private final GlobalSearchScope myScope;
- public RegistrationTypeFinder(PsiClass psiClass, Set<PsiClass> types) {
+ private RegistrationTypeFinder(PsiClass psiClass, Set<PsiClass> types) {
myTypes = types;
myQualifiedName = psiClass.getQualifiedName();
myManager = psiClass.getManager();
diff --git a/plugins/devkit/src/inspections/InspectionDescriptionInfo.java b/plugins/devkit/src/inspections/InspectionDescriptionInfo.java
new file mode 100644
index 000000000000..9720322fed1b
--- /dev/null
+++ b/plugins/devkit/src/inspections/InspectionDescriptionInfo.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.inspections;
+
+import com.intellij.codeInspection.InspectionProfileEntry;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.*;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.devkit.util.PsiUtil;
+
+public class InspectionDescriptionInfo {
+
+ private final String myFilename;
+ private final PsiMethod myMethod;
+ private final PsiFile myDescriptionFile;
+
+ private InspectionDescriptionInfo(String filename, @Nullable PsiMethod method, @Nullable PsiFile descriptionFile) {
+ myFilename = filename;
+ myMethod = method;
+ myDescriptionFile = descriptionFile;
+ }
+
+ public static InspectionDescriptionInfo create(Module module, PsiClass psiClass) {
+ PsiMethod method = PsiUtil.findNearestMethod("getShortName", psiClass);
+ if (method != null &&
+ DescriptionType.INSPECTION.getClassName().equals(method.getContainingClass().getQualifiedName())) {
+ method = null;
+ }
+ final String filename = method == null ?
+ InspectionProfileEntry.getShortName(psiClass.getName()) :
+ PsiUtil.getReturnedLiteral(method, psiClass);
+
+ PsiFile descriptionFile = resolveInspectionDescriptionFile(module, filename);
+ return new InspectionDescriptionInfo(filename, method, descriptionFile);
+ }
+
+ @Nullable
+ private static PsiFile resolveInspectionDescriptionFile(Module module, @Nullable String filename) {
+ if (filename == null) return null;
+
+ for (PsiDirectory description : DescriptionCheckerUtil.getDescriptionsDirs(module, DescriptionType.INSPECTION)) {
+ final PsiFile file = description.findFile(filename + ".html");
+ if (file == null) continue;
+ final VirtualFile vf = file.getVirtualFile();
+ if (vf == null) continue;
+ if (vf.getNameWithoutExtension().equals(filename)) {
+ return PsiManager.getInstance(module.getProject()).findFile(vf);
+ }
+ }
+ return null;
+ }
+
+ public boolean isValid() {
+ return myFilename != null;
+ }
+
+ public String getFilename() {
+ assert isValid();
+ return myFilename;
+ }
+
+ @Nullable
+ public PsiMethod getShortNameMethod() {
+ return myMethod;
+ }
+
+ @Nullable
+ public PsiFile getDescriptionFile() {
+ return myDescriptionFile;
+ }
+
+ public boolean hasDescriptionFile() {
+ return getDescriptionFile() != null;
+ }
+}
diff --git a/plugins/devkit/src/inspections/InspectionDescriptionNotFoundInspection.java b/plugins/devkit/src/inspections/InspectionDescriptionNotFoundInspection.java
index fb8b520dd989..1271f0a9bdcc 100644
--- a/plugins/devkit/src/inspections/InspectionDescriptionNotFoundInspection.java
+++ b/plugins/devkit/src/inspections/InspectionDescriptionNotFoundInspection.java
@@ -16,11 +16,13 @@
package org.jetbrains.idea.devkit.inspections;
-import com.intellij.codeInspection.*;
+import com.intellij.codeInspection.InspectionManager;
+import com.intellij.codeInspection.LocalQuickFix;
+import com.intellij.codeInspection.ProblemDescriptor;
+import com.intellij.codeInspection.ProblemHighlightType;
import com.intellij.openapi.module.Module;
-import com.intellij.openapi.module.ModuleUtil;
+import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import org.jetbrains.annotations.Nls;
@@ -33,95 +35,59 @@ import org.jetbrains.idea.devkit.util.PsiUtil;
/**
* @author Konstantin Bulenkov
*/
-public class InspectionDescriptionNotFoundInspection extends DevKitInspectionBase{
- @NonNls static final String INSPECTION_PROFILE_ENTRY = InspectionProfileEntry.class.getName();
- @NonNls private static final String INSPECTION_DESCRIPTIONS = "inspectionDescriptions";
+public class InspectionDescriptionNotFoundInspection extends DevKitInspectionBase {
+ @NonNls private static final String INSPECTION_PROFILE_ENTRY = DescriptionType.INSPECTION.getClassName();
@Override
- public ProblemDescriptor[] checkClass(@NotNull PsiClass aClass, @NotNull InspectionManager manager, boolean isOnTheFly) {
- final Project project = aClass.getProject();
- final PsiIdentifier nameIdentifier = aClass.getNameIdentifier();
- final Module module = ModuleUtil.findModuleForPsiElement(aClass);
+ public ProblemDescriptor[] checkClass(@NotNull PsiClass psiClass, @NotNull InspectionManager manager, boolean isOnTheFly) {
+ final Project project = psiClass.getProject();
+ final PsiIdentifier nameIdentifier = psiClass.getNameIdentifier();
+ final Module module = ModuleUtilCore.findModuleForPsiElement(psiClass);
- if (nameIdentifier == null || module == null || !PsiUtil.isInstantiable(aClass)) return null;
+ if (nameIdentifier == null || module == null || !PsiUtil.isInstantiable(psiClass)) return null;
final PsiClass base = JavaPsiFacade.getInstance(project).findClass(INSPECTION_PROFILE_ENTRY, GlobalSearchScope.allScope(project));
+ if (base == null || !psiClass.isInheritor(base, true) || isPathMethodsAreOverridden(psiClass)) return null;
- if (base == null || ! aClass.isInheritor(base, true) || isPathMethodsAreOverridden(aClass)) return null;
+ final InspectionDescriptionInfo info = InspectionDescriptionInfo.create(module, psiClass);
+ if (!info.isValid() || info.hasDescriptionFile()) return null;
- PsiMethod method = findNearestMethod("getShortName", aClass);
- if (method != null && method.getContainingClass().getQualifiedName().equals(INSPECTION_PROFILE_ENTRY)) {
- method = null;
- }
- final String filename = method == null ? InspectionProfileEntry.getShortName(aClass.getName()) : PsiUtil.getReturnedLiteral(method, aClass);
- if (filename == null) return null;
-
-
- for (PsiDirectory description : getInspectionDescriptionsDirs(module)) {
- final PsiFile file = description.findFile(filename + ".html");
- if (file == null) continue;
- final VirtualFile vf = file.getVirtualFile();
- if (vf == null) continue;
- if (vf.getNameWithoutExtension().equals(filename)) {
- return null;
- }
- }
-
-
- final PsiElement problem = getProblemElement(aClass, method);
+ final PsiElement problemElement = getProblemElement(psiClass, info.getShortNameMethod());
final ProblemDescriptor problemDescriptor = manager
- .createProblemDescriptor(problem == null ? nameIdentifier : problem,
- "Inspection does not have a description", isOnTheFly, new LocalQuickFix[]{new CreateHtmlDescriptionFix(filename, module, false)},
+ .createProblemDescriptor(problemElement == null ? nameIdentifier : problemElement,
+ "Inspection does not have a description", isOnTheFly,
+ new LocalQuickFix[]{new CreateHtmlDescriptionFix(info.getFilename(), module, DescriptionType.INSPECTION)},
ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
return new ProblemDescriptor[]{problemDescriptor};
}
@Nullable
- private static PsiElement getProblemElement(PsiClass aClass, @Nullable PsiMethod method) {
- if (method != null && method.getContainingClass() == aClass) {
+ private static PsiElement getProblemElement(PsiClass psiClass, @Nullable PsiMethod method) {
+ if (method != null && method.getContainingClass() == psiClass) {
return PsiUtil.getReturnedExpression(method);
- } else {
- return aClass.getNameIdentifier();
}
+ return psiClass.getNameIdentifier();
}
- private static boolean isPathMethodsAreOverridden(PsiClass aClass) {
- return! ( isLastMethodDefinitionIn("getStaticDescription", INSPECTION_PROFILE_ENTRY, aClass)
- && isLastMethodDefinitionIn("getDescriptionUrl", INSPECTION_PROFILE_ENTRY, aClass)
- && isLastMethodDefinitionIn("getDescriptionContextClass", INSPECTION_PROFILE_ENTRY, aClass)
- && isLastMethodDefinitionIn("getDescriptionFileName", INSPECTION_PROFILE_ENTRY, aClass));
+ private static boolean isPathMethodsAreOverridden(PsiClass psiClass) {
+ return !(isLastMethodDefinitionIn("getStaticDescription", INSPECTION_PROFILE_ENTRY, psiClass)
+ && isLastMethodDefinitionIn("getDescriptionUrl", INSPECTION_PROFILE_ENTRY, psiClass)
+ && isLastMethodDefinitionIn("getDescriptionContextClass", INSPECTION_PROFILE_ENTRY, psiClass)
+ && isLastMethodDefinitionIn("getDescriptionFileName", INSPECTION_PROFILE_ENTRY, psiClass));
}
- private static boolean isLastMethodDefinitionIn(@NotNull String methodName, @NotNull String classFQN, PsiClass cls) {
- if (cls == null) return false;
- for (PsiMethod method : cls.getMethods()) {
+ private static boolean isLastMethodDefinitionIn(@NotNull String methodName,
+ @NotNull String classFQN,
+ @Nullable PsiClass psiClass) {
+ if (psiClass == null) return false;
+ for (PsiMethod method : psiClass.getMethods()) {
if (method.getName().equals(methodName)) {
final PsiClass containingClass = method.getContainingClass();
if (containingClass == null) return false;
return classFQN.equals(containingClass.getQualifiedName());
}
}
- return isLastMethodDefinitionIn(methodName, classFQN, cls.getSuperClass());
- }
-
- public static PsiDirectory[] getInspectionDescriptionsDirs(Module module) {
- final PsiPackage aPackage = JavaPsiFacade.getInstance(module.getProject()).findPackage(INSPECTION_DESCRIPTIONS);
- if (aPackage != null) {
- return aPackage.getDirectories(GlobalSearchScope.moduleWithDependenciesScope(module));
- } else {
- return PsiDirectory.EMPTY_ARRAY;
- }
- }
-
- @Nullable
- private static PsiMethod findNearestMethod(String name, @Nullable PsiClass cls) {
- if (cls == null) return null;
- for (PsiMethod method : cls.getMethods()) {
- if (method.getParameterList().getParametersCount() == 0 && method.getName().equals(name)) {
- return method.getModifierList().hasModifierProperty(PsiModifier.ABSTRACT) ? null : method;
- }
- }
- return findNearestMethod(name, cls.getSuperClass());
+ return isLastMethodDefinitionIn(methodName, classFQN, psiClass.getSuperClass());
}
@Nls
diff --git a/plugins/devkit/src/inspections/IntentionDescriptionNotFoundInspection.java b/plugins/devkit/src/inspections/IntentionDescriptionNotFoundInspection.java
index e424c70f1106..209c5720ce48 100644
--- a/plugins/devkit/src/inspections/IntentionDescriptionNotFoundInspection.java
+++ b/plugins/devkit/src/inspections/IntentionDescriptionNotFoundInspection.java
@@ -15,116 +15,32 @@
*/
package org.jetbrains.idea.devkit.inspections;
-import com.intellij.codeInspection.InspectionManager;
-import com.intellij.codeInspection.LocalQuickFix;
-import com.intellij.codeInspection.ProblemDescriptor;
-import com.intellij.codeInspection.ProblemHighlightType;
import com.intellij.openapi.module.Module;
-import com.intellij.openapi.module.ModuleUtil;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.psi.*;
-import com.intellij.psi.search.GlobalSearchScope;
import org.jetbrains.annotations.Nls;
-import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.devkit.inspections.quickfix.CreateHtmlDescriptionFix;
-import org.jetbrains.idea.devkit.util.PsiUtil;
/**
* @author Konstantin Bulenkov
*/
-public class IntentionDescriptionNotFoundInspection extends DevKitInspectionBase{
- @NonNls private static final String INTENTION = "com.intellij.codeInsight.intention.IntentionAction";
- @NonNls private static final String INSPECTION_DESCRIPTIONS = "intentionDescriptions";
+public class IntentionDescriptionNotFoundInspection extends DescriptionNotFoundInspectionBase {
- @Override
- public ProblemDescriptor[] checkClass(@NotNull PsiClass aClass, @NotNull InspectionManager manager, boolean isOnTheFly) {
- final Project project = aClass.getProject();
- final PsiIdentifier nameIdentifier = aClass.getNameIdentifier();
- final Module module = ModuleUtil.findModuleForPsiElement(aClass);
-
- if (nameIdentifier == null || module == null || !PsiUtil.isInstantiable(aClass)) return null;
-
- final PsiClass base = JavaPsiFacade.getInstance(project).findClass(INTENTION, GlobalSearchScope.allScope(project));
-
- if (base == null || ! aClass.isInheritor(base, true)) return null;
-
- String descriptionDir = getDescriptionDirName(aClass);
- if (StringUtil.isEmptyOrSpaces(descriptionDir)) {
- return null;
- }
-
- for (PsiDirectory description : getIntentionDescriptionsDirs(module)) {
- PsiDirectory dir = description.findSubdirectory(descriptionDir);
- if (dir == null) continue;
- final PsiFile descr = dir.findFile("description.html");
- if (descr != null) {
- if (!hasBeforeAndAfterTemplate(dir.getVirtualFile())) {
- PsiElement problem = aClass.getNameIdentifier();
- ProblemDescriptor problemDescriptor = manager.createProblemDescriptor(problem == null ? nameIdentifier : problem,
- "Intention must have 'before.*.template' and 'after.*.template' beside 'description.html'",
- isOnTheFly,
- ProblemHighlightType.GENERIC_ERROR_OR_WARNING, false);
- return new ProblemDescriptor[]{problemDescriptor};
- }
-
- return null;
- }
- }
-
-
- final PsiElement problem = aClass.getNameIdentifier();
- final ProblemDescriptor problemDescriptor = manager
- .createProblemDescriptor(problem == null ? nameIdentifier : problem,
- "Intention does not have a description", isOnTheFly, new LocalQuickFix[]{new CreateHtmlDescriptionFix(descriptionDir, module, true)},
- ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
- return new ProblemDescriptor[]{problemDescriptor};
+ public IntentionDescriptionNotFoundInspection() {
+ super(DescriptionType.INTENTION);
}
- @Nullable
- private static String getDescriptionDirName(PsiClass aClass) {
- String descriptionDir = "";
- PsiClass each = aClass;
- while (each != null) {
- String name = each.getName();
- if (StringUtil.isEmptyOrSpaces(name)) {
- return null;
- }
- descriptionDir = name + descriptionDir;
- each = each.getContainingClass();
- }
- return descriptionDir;
+ protected CreateHtmlDescriptionFix getFix(Module module, String descriptionDir) {
+ return new CreateHtmlDescriptionFix(descriptionDir, module, DescriptionType.INTENTION);
}
- private static boolean hasBeforeAndAfterTemplate(@NotNull VirtualFile dir) {
- boolean hasBefore = false;
- boolean hasAfter = false;
-
- for (VirtualFile file : dir.getChildren()) {
- String name = file.getName();
- if (name.endsWith(".template")) {
- if (name.startsWith("before.")) {
- hasBefore = true;
- }
- else if (name.startsWith("after.")) {
- hasAfter = true;
- }
- }
- }
-
- return hasBefore && hasAfter;
+ @NotNull
+ protected String getHasNotDescriptionError() {
+ return "Intention does not have a description";
}
- public static PsiDirectory[] getIntentionDescriptionsDirs(Module module) {
- final PsiPackage aPackage = JavaPsiFacade.getInstance(module.getProject()).findPackage(INSPECTION_DESCRIPTIONS);
- if (aPackage != null) {
- return aPackage.getDirectories(GlobalSearchScope.moduleWithDependenciesScope(module));
- } else {
- return PsiDirectory.EMPTY_ARRAY;
- }
+ @NotNull
+ protected String getHasNotBeforeAfterError() {
+ return "Intention must have 'before.*.template' and 'after.*.template' beside 'description.html'";
}
@Nls
@@ -137,9 +53,4 @@ public class IntentionDescriptionNotFoundInspection extends DevKitInspectionBase
public String getShortName() {
return "IntentionDescriptionNotFoundInspection";
}
-
- @Override
- public boolean isEnabledByDefault() {
- return true;
- }
}
diff --git a/plugins/devkit/src/inspections/PostfixTemplateDescriptionNotFoundInspection.java b/plugins/devkit/src/inspections/PostfixTemplateDescriptionNotFoundInspection.java
new file mode 100644
index 000000000000..fed345f22b25
--- /dev/null
+++ b/plugins/devkit/src/inspections/PostfixTemplateDescriptionNotFoundInspection.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.inspections;
+
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.idea.devkit.DevKitBundle;
+
+
+public class PostfixTemplateDescriptionNotFoundInspection extends DescriptionNotFoundInspectionBase {
+
+ public PostfixTemplateDescriptionNotFoundInspection() {
+ super(DescriptionType.POSTFIX_TEMPLATES);
+ }
+
+ @NotNull
+ @Override
+ protected String getHasNotDescriptionError() {
+ return "Postfix template does not have a description";
+ }
+
+ @NotNull
+ @Override
+ protected String getHasNotBeforeAfterError() {
+ return "Postfix template must have 'before.*.template' and 'after.*.template' beside 'description.html'";
+ }
+
+ @Nls
+ @NotNull
+ @Override
+ public String getDisplayName() {
+ return DevKitBundle.message("inspections.component.postfix.template.not.found.description.name");
+ }
+
+ @NotNull
+ @Override
+ public String getShortName() {
+ return "PostfixTemplateDescriptionNotFound";
+ }
+}
diff --git a/plugins/devkit/src/inspections/internal/DontUseNewPairInspection.java b/plugins/devkit/src/inspections/internal/DontUseNewPairInspection.java
new file mode 100644
index 000000000000..9e6fea3f6071
--- /dev/null
+++ b/plugins/devkit/src/inspections/internal/DontUseNewPairInspection.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.inspections.internal;
+
+import com.intellij.codeInspection.ProblemHighlightType;
+import com.intellij.codeInspection.ProblemsHolder;
+import com.intellij.psi.*;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.idea.devkit.inspections.quickfix.ChangeToPairCreateQuickFix;
+
+import java.util.Arrays;
+
+/**
+ * @author Konstantin Bulenkov
+ */
+public class DontUseNewPairInspection extends InternalInspection {
+ private static final String PAIR_FQN = "com.intellij.openapi.util.Pair";
+
+ @Override
+ public PsiElementVisitor buildInternalVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
+ return new JavaElementVisitor() {
+ @Override
+ public void visitNewExpression(PsiNewExpression expression) {
+ final PsiType type = expression.getType();
+ final PsiExpressionList params = expression.getArgumentList();
+ if (type instanceof PsiClassType
+ && ((PsiClassType)type).rawType().equalsToText(PAIR_FQN)
+ && params != null
+ && expression.getArgumentList() != null
+ //&& !PsiUtil.getLanguageLevel(expression).isAtLeast(LanguageLevel.JDK_1_7) //diamonds
+ ) {
+ final PsiType[] types = ((PsiClassType)type).getParameters();
+ if (Arrays.equals(types, params.getExpressionTypes())) {
+ holder.registerProblem(expression, "Replace to Pair.create()", ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
+ new ChangeToPairCreateQuickFix());
+ }
+ }
+ super.visitNewExpression(expression);
+ }
+ };
+ }
+
+ @Nls
+ @NotNull
+ @Override
+ public String getDisplayName() {
+ return "Don't use Pair constructor";
+ }
+
+ @NotNull
+ @Override
+ public String getShortName() {
+ return "DontUsePairConstructor";
+ }
+}
+
diff --git a/plugins/devkit/src/inspections/internal/UseCoupleInspection.java b/plugins/devkit/src/inspections/internal/UseCoupleInspection.java
new file mode 100644
index 000000000000..4b1a97f59aaf
--- /dev/null
+++ b/plugins/devkit/src/inspections/internal/UseCoupleInspection.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.inspections.internal;
+
+import com.intellij.codeInspection.ProblemHighlightType;
+import com.intellij.codeInspection.ProblemsHolder;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.*;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.idea.devkit.inspections.quickfix.UseCoupleQuickFix;
+
+import java.util.List;
+
+/**
+ * @author Konstantin Bulenkov
+ */
+public class UseCoupleInspection extends InternalInspection {
+ private static final String PAIR_FQN = "com.intellij.openapi.util.Pair";
+
+ @Override
+ public PsiElementVisitor buildInternalVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
+ return new JavaElementVisitor() {
+
+ @Override
+ public void visitTypeElement(PsiTypeElement type) {
+ final String canonicalText = type.getType().getCanonicalText();
+ if (canonicalText.startsWith(PAIR_FQN)) {
+ if (canonicalText.contains("<") && canonicalText.endsWith(">")) {
+ String genericTypes = canonicalText.substring(canonicalText.indexOf('<') + 1, canonicalText.length() - 1);
+ final List<String> types = StringUtil.split(genericTypes, ",");
+ if (types.size() == 2 && StringUtil.equals(types.get(0), types.get(1))) {
+ final List<String> parts = StringUtil.split(types.get(0), ".");
+ String typeName = parts.get(parts.size() - 1);
+ final String name = "Change to Couple<" + typeName + ">";
+ holder.registerProblem(type, name, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, new UseCoupleQuickFix(name));
+ }
+ }
+
+ }
+ super.visitTypeElement(type);
+ }
+
+ @Override
+ public void visitMethodCallExpression(PsiMethodCallExpression expression) {
+ if (expression.getText().startsWith("Pair.create")) {
+ final PsiReference reference = expression.getMethodExpression().getReference();
+ if (reference != null) {
+ final PsiElement method = reference.resolve();
+ if (method instanceof PsiMethod) {
+ final PsiClass psiClass = ((PsiMethod)method).getContainingClass();
+ if (psiClass != null && PAIR_FQN.equals(psiClass.getQualifiedName())) {
+ final PsiType[] types = expression.getArgumentList().getExpressionTypes();
+ if (types.length == 2 && types[0].equals(types[1])) {
+ final String name = "Change to Couple.newOne";
+ holder.registerProblem(expression, name, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, new UseCoupleQuickFix(name));
+ }
+ }
+ }
+ }
+ }
+ super.visitMethodCallExpression(expression);
+ }
+ };
+ }
+
+ @Nls
+ @NotNull
+ @Override
+ public String getDisplayName() {
+ return "Use Couple<T> instead of Pair<T, T>";
+ }
+
+ @NotNull
+ @Override
+ public String getShortName() {
+ return "UseCouple";
+ }
+}
diff --git a/plugins/devkit/src/inspections/internal/UseVirtualFileEqualsInspection.java b/plugins/devkit/src/inspections/internal/UseVirtualFileEqualsInspection.java
new file mode 100644
index 000000000000..ddbedb4e559f
--- /dev/null
+++ b/plugins/devkit/src/inspections/internal/UseVirtualFileEqualsInspection.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.inspections.internal;
+
+import com.intellij.codeInspection.ProblemHighlightType;
+import com.intellij.codeInspection.ProblemsHolder;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.*;
+import com.intellij.psi.util.InheritanceUtil;
+import com.siyeh.ig.psiutils.ComparisonUtils;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author peter
+ */
+public class UseVirtualFileEqualsInspection extends InternalInspection {
+ @Override
+ public PsiElementVisitor buildInternalVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
+ return new JavaElementVisitor() {
+ @Override
+ public void visitBinaryExpression(@NotNull PsiBinaryExpression expression) {
+ super.visitBinaryExpression(expression);
+ if (!ComparisonUtils.isEqualityComparison(expression)) {
+ return;
+ }
+ final PsiExpression lhs = expression.getLOperand();
+ final PsiExpression rhs = expression.getROperand();
+ if (rhs == null ||
+ lhs.textMatches(PsiKeyword.NULL) || rhs.textMatches(PsiKeyword.NULL) ||
+ lhs.textMatches(PsiKeyword.THIS) || rhs.textMatches(PsiKeyword.THIS)) {
+ return;
+ }
+
+ if (InheritanceUtil.isInheritor(lhs.getType(), VirtualFile.class.getName()) || InheritanceUtil.isInheritor(rhs.getType(), VirtualFile.class.getName())) {
+ holder.registerProblem(expression, "VirtualFile objects should be compared by equals(), not ==", ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
+
+ }
+ }
+ };
+ }
+}
diff --git a/plugins/devkit/src/inspections/quickfix/ChangeToPairCreateQuickFix.java b/plugins/devkit/src/inspections/quickfix/ChangeToPairCreateQuickFix.java
new file mode 100644
index 000000000000..a8462a002486
--- /dev/null
+++ b/plugins/devkit/src/inspections/quickfix/ChangeToPairCreateQuickFix.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.inspections.quickfix;
+
+import com.intellij.codeInspection.LocalQuickFixBase;
+import com.intellij.codeInspection.ProblemDescriptor;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.JavaPsiFacade;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiElementFactory;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.codeStyle.JavaCodeStyleManager;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Konstantin Bulenkov
+ */
+public class ChangeToPairCreateQuickFix extends LocalQuickFixBase {
+ public ChangeToPairCreateQuickFix() {
+ super("Change to Pair.create(..., ...)");
+ }
+
+ @Override
+ public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
+ PsiElement element = descriptor.getPsiElement();
+ if (element == null) {
+ return;
+ }
+ String text = element.getText();
+ String newText = "com.intellij.openapi.util.Pair.create(" + text.substring(text.indexOf('(') + 1);
+ PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
+ PsiExpression expression = factory.createExpressionFromText(newText, element.getContext());
+ PsiElement newElement = element.replace(expression);
+ JavaCodeStyleManager.getInstance(project).shortenClassReferences(newElement);
+ }
+}
diff --git a/plugins/devkit/src/inspections/quickfix/CreateHtmlDescriptionFix.java b/plugins/devkit/src/inspections/quickfix/CreateHtmlDescriptionFix.java
index e8f498dd28ea..67db267ea30a 100644
--- a/plugins/devkit/src/inspections/quickfix/CreateHtmlDescriptionFix.java
+++ b/plugins/devkit/src/inspections/quickfix/CreateHtmlDescriptionFix.java
@@ -31,7 +31,7 @@ import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.Iconable;
-import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElement;
@@ -43,8 +43,8 @@ import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.idea.devkit.DevKitBundle;
-import org.jetbrains.idea.devkit.inspections.InspectionDescriptionNotFoundInspection;
-import org.jetbrains.idea.devkit.inspections.IntentionDescriptionNotFoundInspection;
+import org.jetbrains.idea.devkit.inspections.DescriptionCheckerUtil;
+import org.jetbrains.idea.devkit.inspections.DescriptionType;
import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes;
import org.jetbrains.jps.model.java.JavaResourceRootType;
@@ -57,15 +57,45 @@ import java.util.List;
* @author Konstantin Bulenkov
*/
public class CreateHtmlDescriptionFix implements LocalQuickFix, Iconable {
+
+ @NonNls
+ private static final String TEMPLATE_NAME = "InspectionDescription.html";
+
private final String myFilename;
- private final Module myModule;
- @NonNls private static final String TEMPLATE_NAME = "InspectionDescription.html";
- private final boolean isIntention;
+ private final Module myModule;
+ private final DescriptionType myDescriptionType;
- public CreateHtmlDescriptionFix(String filename, Module module, boolean isIntention) {
+ public CreateHtmlDescriptionFix(String filename, Module module, DescriptionType descriptionType) {
myModule = module;
- this.isIntention = isIntention;
- myFilename = isIntention ? filename : filename + ".html";
+ myDescriptionType = descriptionType;
+ myFilename = getNormalizedFileName(filename);
+ }
+
+ private boolean isFixedDescriptionFilename() {
+ return myDescriptionType.isFixedDescriptionFilename();
+ }
+
+ private static List<VirtualFile> getPotentialRoots(Module module, PsiDirectory[] dirs) {
+ if (dirs.length != 0) {
+ final List<VirtualFile> result = new ArrayList<VirtualFile>();
+ for (PsiDirectory dir : dirs) {
+ final PsiDirectory parent = dir.getParentDirectory();
+ if (parent != null) result.add(parent.getVirtualFile());
+ }
+ return result;
+ }
+ else {
+ ModuleRootManager rootManager = ModuleRootManager.getInstance(module);
+ List<VirtualFile> resourceRoots = rootManager.getSourceRoots(JavaResourceRootType.RESOURCE);
+ if (!resourceRoots.isEmpty()) {
+ return resourceRoots;
+ }
+ return rootManager.getSourceRoots(JavaModuleSourceRootTypes.SOURCES);
+ }
+ }
+
+ private String getNormalizedFileName(String filename) {
+ return myDescriptionType.isFixedDescriptionFilename() ? filename : filename + ".html";
}
@NotNull
@@ -79,30 +109,20 @@ public class CreateHtmlDescriptionFix implements LocalQuickFix, Iconable {
}
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
- final PsiDirectory[] dirs;
- if (isIntention) {
- dirs = IntentionDescriptionNotFoundInspection.getIntentionDescriptionsDirs(myModule);
- }
- else {
- dirs = InspectionDescriptionNotFoundInspection.getInspectionDescriptionsDirs(myModule);
- }
+ final PsiDirectory[] dirs = getDirectories();
final List<VirtualFile> virtualFiles = getPotentialRoots(myModule, dirs);
- final VirtualFile[] roots = prepare(VfsUtil.toVirtualFileArray(virtualFiles));
+ final VirtualFile[] roots = prepare(VfsUtilCore.toVirtualFileArray(virtualFiles));
if (roots.length == 1) {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
public void run() {
createDescription(roots[0]);
}
});
-
}
else {
List<String> options = new ArrayList<String>();
for (VirtualFile file : roots) {
- String path = file.getPresentableUrl() + File.separator + getDescriptionFolderName() + File.separator + myFilename;
- if (isIntention) {
- path += File.separator + "description.html";
- }
+ String path = getPath(file);
options.add(path);
}
final JBList files = new JBList(ArrayUtil.toStringArray(options));
@@ -111,40 +131,33 @@ public class CreateHtmlDescriptionFix implements LocalQuickFix, Iconable {
.createListPopupBuilder(files)
.setTitle(DevKitBundle.message("select.target.location.of.description", myFilename))
.setItemChoosenCallback(new Runnable() {
- public void run() {
- final int index = files.getSelectedIndex();
- if (0 <= index && index < roots.length) {
- ApplicationManager.getApplication().runWriteAction(new Runnable() {
- public void run() {
- createDescription(roots[index]);
- }
- });
+ public void run() {
+ final int index = files.getSelectedIndex();
+ if (0 <= index && index < roots.length) {
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ public void run() {
+ createDescription(roots[index]);
+ }
+ });
+ }
}
- }
- }).createPopup();
+ }).createPopup();
final Editor editor = FileEditorManager.getInstance(myModule.getProject()).getSelectedTextEditor();
if (editor == null) return;
popup.showInBestPositionFor(editor);
}
}
- private static List<VirtualFile> getPotentialRoots(Module module, PsiDirectory[] dirs) {
- if (dirs.length != 0) {
- final List<VirtualFile> result = new ArrayList<VirtualFile>();
- for (PsiDirectory dir : dirs) {
- final PsiDirectory parent = dir.getParentDirectory();
- if (parent != null) result.add(parent.getVirtualFile());
- }
- return result;
- }
- else {
- ModuleRootManager rootManager = ModuleRootManager.getInstance(module);
- List<VirtualFile> resourceRoots = rootManager.getSourceRoots(JavaResourceRootType.RESOURCE);
- if (!resourceRoots.isEmpty()) {
- return resourceRoots;
- }
- return rootManager.getSourceRoots(JavaModuleSourceRootTypes.SOURCES);
+ private String getPath(VirtualFile file) {
+ String path = file.getPresentableUrl() + File.separator + getDescriptionFolderName() + File.separator + myFilename;
+ if (isFixedDescriptionFilename()) {
+ path += File.separator + "description.html";
}
+ return path;
+ }
+
+ private PsiDirectory[] getDirectories() {
+ return DescriptionCheckerUtil.getDescriptionsDirs(myModule, myDescriptionType);
}
private void createDescription(VirtualFile root) {
@@ -162,14 +175,15 @@ public class CreateHtmlDescriptionFix implements LocalQuickFix, Iconable {
try {
descrRoot = descrRoot == null ? psiRoot.createSubdirectory(getDescriptionFolderName()) : descrRoot;
- if (isIntention) {
+ if (isFixedDescriptionFilename()) {
PsiDirectory dir = descrRoot.findSubdirectory(myFilename);
if (dir == null) {
descrRoot = descrRoot.createSubdirectory(myFilename);
}
}
final FileTemplate descrTemplate = FileTemplateManager.getInstance().getJ2eeTemplate(TEMPLATE_NAME);
- final PsiElement template = FileTemplateUtil.createFromTemplate(descrTemplate, isIntention? "description.html" : myFilename, null, descrRoot);
+ final PsiElement template =
+ FileTemplateUtil.createFromTemplate(descrTemplate, getNewFileName(), null, descrRoot);
if (template instanceof PsiFile) {
final VirtualFile file = ((PsiFile)template).getVirtualFile();
if (file != null) {
@@ -181,6 +195,10 @@ public class CreateHtmlDescriptionFix implements LocalQuickFix, Iconable {
}
}
+ private String getNewFileName() {
+ return isFixedDescriptionFilename() ? "description.html" : myFilename;
+ }
+
public Icon getIcon(int flags) {
return new LayeredIcon(AllIcons.FileTypes.Html, AllIcons.Actions.New);
}
@@ -192,7 +210,7 @@ public class CreateHtmlDescriptionFix implements LocalQuickFix, Iconable {
found.add(root);
}
}
- return found.size() > 0 ? VfsUtil.toVirtualFileArray(found) : roots;
+ return found.size() > 0 ? VfsUtilCore.toVirtualFileArray(found) : roots;
}
private boolean containsDescriptionDir(VirtualFile root) {
@@ -206,6 +224,6 @@ public class CreateHtmlDescriptionFix implements LocalQuickFix, Iconable {
}
private String getDescriptionFolderName() {
- return isIntention ? "intentionDescriptions" : "inspectionDescriptions";
+ return myDescriptionType.getDescriptionFolder();
}
}
diff --git a/plugins/devkit/src/inspections/quickfix/PluginDescriptorChooser.java b/plugins/devkit/src/inspections/quickfix/PluginDescriptorChooser.java
new file mode 100644
index 000000000000..f46429fcf44f
--- /dev/null
+++ b/plugins/devkit/src/inspections/quickfix/PluginDescriptorChooser.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.inspections.quickfix;
+
+import com.google.common.collect.ImmutableMap;
+import com.intellij.codeInsight.hint.HintManager;
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleUtilCore;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.roots.ui.configuration.ModulesAlphaComparator;
+import com.intellij.openapi.ui.popup.JBPopupFactory;
+import com.intellij.openapi.ui.popup.ListSeparator;
+import com.intellij.openapi.ui.popup.PopupStep;
+import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.Condition;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiFile;
+import com.intellij.util.Consumer;
+import com.intellij.util.Function;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.ui.EmptyIcon;
+import com.intellij.util.xml.DomFileElement;
+import com.intellij.util.xml.DomService;
+import com.intellij.xml.util.IncludedXmlTag;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.devkit.dom.Extensions;
+import org.jetbrains.idea.devkit.dom.IdeaPlugin;
+
+import javax.swing.*;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+public class PluginDescriptorChooser {
+
+ private static final ImmutableMap<String, String> INTELLIJ_MODULES = ImmutableMap.<String, String>builder()
+ .put("platform-api", "PlatformExtensions.xml")
+ .put("platform-impl", "PlatformExtensions.xml")
+ .put("lang-api", "LangExtensions.xml")
+ .put("lang-impl", "LangExtensions.xml")
+ .put("vcs-api", "VcsExtensions.xml")
+ .put("vcs-impl", "VcsExtensions.xml")
+ .put("openapi", "IdeaPlugin.xml")
+ .put("java-impl", "IdeaPlugin.xml")
+ .build();
+
+ public static void show(final Project project,
+ final Editor editor,
+ final PsiFile file,
+ final Consumer<DomFileElement<IdeaPlugin>> consumer) {
+ final Module module = ModuleUtilCore.findModuleForPsiElement(file);
+ assert module != null;
+ List<DomFileElement<IdeaPlugin>> elements =
+ DomService.getInstance().getFileElements(IdeaPlugin.class,
+ project,
+ module.getModuleContentWithDependenciesScope());
+
+ elements = ContainerUtil.filter(elements, new Condition<DomFileElement<IdeaPlugin>>() {
+ @Override
+ public boolean value(DomFileElement<IdeaPlugin> element) {
+ VirtualFile virtualFile = element.getFile().getVirtualFile();
+ return virtualFile != null && ProjectRootManager.getInstance(project).getFileIndex().isInContent(virtualFile);
+ }
+ });
+
+ elements = findAppropriateIntelliJModule(module.getName(), elements);
+
+ if (elements.isEmpty()) {
+ HintManager.getInstance().showErrorHint(editor, "Cannot find plugin descriptor");
+ return;
+ }
+
+ if (elements.size() == 1) {
+ consumer.consume(elements.get(0));
+ return;
+ }
+
+ final BaseListPopupStep<PluginDescriptorCandidate> popupStep =
+ new BaseListPopupStep<PluginDescriptorCandidate>("Choose Plugin Descriptor",
+ createCandidates(module, elements)) {
+
+ @Override
+ public boolean isSpeedSearchEnabled() {
+ return true;
+ }
+
+ @Override
+ public Icon getIconFor(PluginDescriptorCandidate candidate) {
+ return candidate.getIcon();
+ }
+
+ @NotNull
+ @Override
+ public String getTextFor(PluginDescriptorCandidate candidate) {
+ return candidate.getText();
+ }
+
+ @Nullable
+ @Override
+ public ListSeparator getSeparatorAbove(PluginDescriptorCandidate candidate) {
+ final String separatorText = candidate.getSeparatorText();
+ if (separatorText != null) {
+ return new ListSeparator(separatorText);
+ }
+ return null;
+ }
+
+ @Override
+ public PopupStep onChosen(PluginDescriptorCandidate selectedValue, boolean finalChoice) {
+ consumer.consume(selectedValue.myDomFileElement);
+ return FINAL_CHOICE;
+ }
+ };
+ JBPopupFactory.getInstance().createListPopup(popupStep).showInBestPositionFor(editor);
+ }
+
+ @NotNull
+ public static Extensions findOrCreateExtensionsForEP(DomFileElement<IdeaPlugin> domFileElement, String epName) {
+ final IdeaPlugin ideaPlugin = domFileElement.getRootElement();
+ final List<Extensions> extensionsList = ideaPlugin.getExtensions();
+ for (Extensions extensions : extensionsList) {
+ if (extensions.getXmlTag() instanceof IncludedXmlTag) {
+ continue;
+ }
+ String s = extensions.getDefaultExtensionNs().getStringValue();
+ if (s != null && epName.startsWith(s)) {
+ return extensions;
+ }
+ }
+
+ Extensions extensions = ideaPlugin.addExtensions();
+ final String epPrefix = StringUtil.getPackageName(epName);
+ extensions.getDefaultExtensionNs().setStringValue(epPrefix);
+ return extensions;
+ }
+
+ private static List<PluginDescriptorCandidate> createCandidates(final Module currentModule,
+ List<DomFileElement<IdeaPlugin>> elements) {
+ Collections.sort(elements, new Comparator<DomFileElement<IdeaPlugin>>() {
+ @Override
+ public int compare(DomFileElement<IdeaPlugin> o1, DomFileElement<IdeaPlugin> o2) {
+ // current module = first group
+ final Module module1 = o1.getModule();
+ if (currentModule.equals(module1)) return -1;
+ final Module module2 = o2.getModule();
+ if (currentModule.equals(module2)) return 1;
+
+ return ModulesAlphaComparator.INSTANCE.compare(module1, module2);
+ }
+ });
+ Collections.sort(elements, new Comparator<DomFileElement<IdeaPlugin>>() {
+ @Override
+ public int compare(DomFileElement<IdeaPlugin> o1, DomFileElement<IdeaPlugin> o2) {
+ if (!Comparing.equal(o1.getModule(), o2.getModule())) return 0;
+ return o1.getFile().getName().compareTo(o2.getFile().getName());
+ }
+ });
+
+ return ContainerUtil.map(elements, new Function<DomFileElement<IdeaPlugin>, PluginDescriptorCandidate>() {
+
+ private Module myLastModule = currentModule;
+
+ @Override
+ public PluginDescriptorCandidate fun(DomFileElement<IdeaPlugin> element) {
+ final Module module = element.getModule();
+ boolean startsNewGroup = !myLastModule.equals(module);
+ myLastModule = module;
+ return new PluginDescriptorCandidate(element, startsNewGroup);
+ }
+ });
+ }
+
+ private static List<DomFileElement<IdeaPlugin>> findAppropriateIntelliJModule(String moduleName,
+ List<DomFileElement<IdeaPlugin>> elements) {
+ String extensionsFile = INTELLIJ_MODULES.get(moduleName);
+ if (extensionsFile != null) {
+ for (DomFileElement<IdeaPlugin> element : elements) {
+ if (element.getFile().getName().equals(extensionsFile)) {
+ return Collections.singletonList(element);
+ }
+ }
+ }
+ return elements;
+ }
+
+
+ private static class PluginDescriptorCandidate {
+ private final DomFileElement<IdeaPlugin> myDomFileElement;
+ private final boolean myStartsNewGroup;
+
+ private PluginDescriptorCandidate(DomFileElement<IdeaPlugin> domFileElement,
+ boolean startsNewGroup) {
+ myDomFileElement = domFileElement;
+ myStartsNewGroup = startsNewGroup;
+ }
+
+ public String getText() {
+ final String name = myDomFileElement.getFile().getName();
+ final String pluginId = getPluginId();
+ return pluginId != null ? name + " [" + pluginId + "]" : name;
+ }
+
+ public Icon getIcon() {
+ return getPluginId() != null ? AllIcons.Nodes.Plugin : EmptyIcon.create(AllIcons.Nodes.Plugin);
+ }
+
+ public String getSeparatorText() {
+ if (!myStartsNewGroup) return null;
+
+ final Module module = myDomFileElement.getModule();
+ return module == null ? null : module.getName();
+ }
+
+ private String getPluginId() {
+ return myDomFileElement.getRootElement().getPluginId();
+ }
+ }
+}
diff --git a/plugins/devkit/src/inspections/quickfix/RegisterActionFix.java b/plugins/devkit/src/inspections/quickfix/RegisterActionFix.java
index 061deef3d7b3..3eb7ef22ca46 100644
--- a/plugins/devkit/src/inspections/quickfix/RegisterActionFix.java
+++ b/plugins/devkit/src/inspections/quickfix/RegisterActionFix.java
@@ -16,14 +16,17 @@
package org.jetbrains.idea.devkit.inspections.quickfix;
import com.intellij.codeInspection.ProblemDescriptor;
+import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiClass;
import com.intellij.psi.xml.XmlFile;
import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.TestOnly;
import org.jetbrains.idea.devkit.DevKitBundle;
import org.jetbrains.idea.devkit.actions.NewActionDialog;
+import org.jetbrains.idea.devkit.util.ActionData;
import org.jetbrains.idea.devkit.util.ActionType;
-import org.jetbrains.annotations.NotNull;
public class RegisterActionFix extends AbstractRegisterFix {
private NewActionDialog myDialog;
@@ -37,6 +40,11 @@ public class RegisterActionFix extends AbstractRegisterFix {
}
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
+ if (ApplicationManager.getApplication().isUnitTestMode()) {
+ super.applyFix(project, descriptor);
+ return;
+ }
+
try {
myDialog = new NewActionDialog(myClass);
myDialog.show();
@@ -52,9 +60,20 @@ public class RegisterActionFix extends AbstractRegisterFix {
public void patchPluginXml(XmlFile pluginXml, PsiClass aClass) throws IncorrectOperationException {
if (ActionType.GROUP.isOfType(aClass)) {
- ActionType.GROUP.patchPluginXml(pluginXml, aClass, myDialog);
- } else {
- ActionType.ACTION.patchPluginXml(pluginXml, aClass, myDialog);
+ ActionType.GROUP.patchPluginXml(pluginXml, aClass, getActionData());
+ }
+ else {
+ ActionType.ACTION.patchPluginXml(pluginXml, aClass, getActionData());
+ }
+ }
+
+ @TestOnly
+ public static ActionData ourTestActionData = null;
+
+ private ActionData getActionData() {
+ if (ApplicationManager.getApplication().isUnitTestMode()) {
+ return ourTestActionData;
}
+ return myDialog;
}
}
diff --git a/plugins/devkit/src/inspections/quickfix/RegisterExtensionFix.java b/plugins/devkit/src/inspections/quickfix/RegisterExtensionFix.java
index 65392a815e93..e51616977ec6 100644
--- a/plugins/devkit/src/inspections/quickfix/RegisterExtensionFix.java
+++ b/plugins/devkit/src/inspections/quickfix/RegisterExtensionFix.java
@@ -23,6 +23,7 @@ import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.extensions.KeyedFactoryEPBean;
import com.intellij.openapi.fileTypes.FileTypeExtensionPoint;
+import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.ui.popup.PopupStep;
@@ -42,6 +43,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.idea.devkit.dom.Extension;
import org.jetbrains.idea.devkit.dom.Extensions;
import org.jetbrains.idea.devkit.dom.IdeaPlugin;
+import org.jetbrains.idea.devkit.util.ExtensionPointCandidate;
import java.util.List;
@@ -71,12 +73,12 @@ public class RegisterExtensionFix implements IntentionAction {
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
- return true;
+ return !DumbService.isDumb(project);
}
@Override
public void invoke(@NotNull Project project, final Editor editor, PsiFile file) throws IncorrectOperationException {
- RegisterInspectionFix.choosePluginDescriptor(project, editor, file, new Consumer<DomFileElement<IdeaPlugin>>() {
+ PluginDescriptorChooser.show(project, editor, file, new Consumer<DomFileElement<IdeaPlugin>>() {
@Override
public void consume(DomFileElement<IdeaPlugin> element) {
doFix(editor, element);
@@ -101,11 +103,11 @@ public class RegisterExtensionFix implements IntentionAction {
}
}
- private void registerExtension(final DomFileElement<IdeaPlugin> element, final ExtensionPointCandidate candidate) {
- PsiElement navTarget = new WriteCommandAction<PsiElement>(element.getFile().getProject(), element.getFile()) {
+ private void registerExtension(final DomFileElement<IdeaPlugin> selectedValue, final ExtensionPointCandidate candidate) {
+ PsiElement navTarget = new WriteCommandAction<PsiElement>(selectedValue.getFile().getProject(), selectedValue.getFile()) {
@Override
- protected void run(Result<PsiElement> result) throws Throwable {
- Extensions extensions = RegisterInspectionFix.getExtension(element.getRootElement(), candidate.epName);
+ protected void run(@NotNull Result<PsiElement> result) throws Throwable {
+ Extensions extensions = PluginDescriptorChooser.findOrCreateExtensionsForEP(selectedValue, candidate.epName);
Extension extension = extensions.addExtension(candidate.epName);
XmlTag tag = extension.getXmlTag();
PsiElement navTarget = null;
diff --git a/plugins/devkit/src/inspections/quickfix/RegisterExtensionFixProvider.java b/plugins/devkit/src/inspections/quickfix/RegisterExtensionFixProvider.java
index ba02d703c93c..815d597442c5 100644
--- a/plugins/devkit/src/inspections/quickfix/RegisterExtensionFixProvider.java
+++ b/plugins/devkit/src/inspections/quickfix/RegisterExtensionFixProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2012 JetBrains s.r.o.
+ * Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,19 +21,14 @@ import com.intellij.codeInspection.InspectionEP;
import com.intellij.codeInspection.LocalInspectionEP;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.reference.UnusedDeclarationFixProvider;
-import com.intellij.ide.highlighter.XmlFileType;
-import com.intellij.psi.*;
-import com.intellij.psi.search.GlobalSearchScope;
-import com.intellij.psi.search.ProjectScope;
-import com.intellij.psi.search.PsiNonJavaFileReferenceProcessor;
-import com.intellij.psi.search.PsiSearchHelper;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiIdentifier;
import com.intellij.psi.util.InheritanceUtil;
-import com.intellij.psi.util.PsiTreeUtil;
-import com.intellij.psi.xml.XmlTag;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.idea.devkit.util.ExtensionPointCandidate;
+import org.jetbrains.idea.devkit.util.ExtensionPointLocator;
-import java.util.ArrayList;
-import java.util.HashSet;
import java.util.List;
/**
@@ -48,79 +43,20 @@ public class RegisterExtensionFixProvider implements UnusedDeclarationFixProvide
if (!(element instanceof PsiIdentifier)) return IntentionAction.EMPTY_ARRAY;
PsiElement parent = element.getParent();
if (!(parent instanceof PsiClass)) return IntentionAction.EMPTY_ARRAY;
- PsiClass parentClass = (PsiClass)parent;
- if (InheritanceUtil.isInheritor(parentClass, LocalInspectionTool.class.getName())) {
- return new IntentionAction[] { new RegisterInspectionFix(parentClass, LocalInspectionEP.LOCAL_INSPECTION) };
- }
- if (InheritanceUtil.isInheritor(parentClass, GlobalInspectionTool.class.getName())) {
- return new IntentionAction[] { new RegisterInspectionFix(parentClass, InspectionEP.GLOBAL_INSPECTION) };
- }
- List<ExtensionPointCandidate> candidateList = new ArrayList<ExtensionPointCandidate>();
- findExtensionPointCandidatesInHierarchy(parentClass, candidateList, new HashSet<PsiClass>());
- if (!candidateList.isEmpty()) {
- return new IntentionAction[] { new RegisterExtensionFix(parentClass, candidateList) };
- }
- return IntentionAction.EMPTY_ARRAY;
- }
-
- private static void findExtensionPointCandidatesInHierarchy(PsiClass aClass,
- List<ExtensionPointCandidate> list,
- HashSet<PsiClass> processed) {
- for (PsiClass superClass : aClass.getSupers()) {
- if (!processed.add(superClass) || CommonClassNames.JAVA_LANG_OBJECT.equals(superClass.getQualifiedName())) {
- continue;
- }
- findExtensionPointCandidates(superClass, list);
- findExtensionPointCandidatesInHierarchy(superClass, list, processed);
- }
- }
-
- private static void findExtensionPointCandidates(PsiClass aClass, final List<ExtensionPointCandidate> list) {
- String name = aClass.getQualifiedName();
- if (name == null) {
- return;
- }
- GlobalSearchScope scope = GlobalSearchScope.getScopeRestrictedByFileTypes(ProjectScope.getAllScope(aClass.getProject()), XmlFileType.INSTANCE);
- PsiSearchHelper.SERVICE.getInstance(aClass.getProject()).processUsagesInNonJavaFiles(name, new PsiNonJavaFileReferenceProcessor() {
- @Override
- public boolean process(PsiFile file, int startOffset, int endOffset) {
- PsiElement element = file.findElementAt(startOffset);
- processExtensionPointCandidate(element, list);
- return true;
- }
- }, scope);
- }
- private static void processExtensionPointCandidate(PsiElement element, List<ExtensionPointCandidate> list) {
- XmlTag tag = PsiTreeUtil.getParentOfType(element, XmlTag.class);
- if (tag == null) return;
- if ("extensionPoint".equals(tag.getName())) {
- String epName = getEPName(tag);
- if (epName != null) {
- list.add(new ExtensionPointCandidate(epName));
- }
+ PsiClass psiClass = (PsiClass)parent;
+ if (InheritanceUtil.isInheritor(psiClass, LocalInspectionTool.class.getName())) {
+ return new IntentionAction[]{new RegisterInspectionFix(psiClass, LocalInspectionEP.LOCAL_INSPECTION)};
}
- else if ("with".equals(tag.getName())) {
- XmlTag extensionPointTag = tag.getParentTag();
- if (!"extensionPoint".equals(extensionPointTag.getName())) return;
- String attrName = tag.getAttributeValue("attribute");
- String tagName = tag.getAttributeValue("tag");
- String epName = getEPName(extensionPointTag);
- String beanClassName = extensionPointTag.getAttributeValue("beanClass");
- if ((attrName == null && tagName == null) || epName == null) return;
- list.add(new ExtensionPointCandidate(epName, attrName, tagName, beanClassName));
+ if (InheritanceUtil.isInheritor(psiClass, GlobalInspectionTool.class.getName())) {
+ return new IntentionAction[]{new RegisterInspectionFix(psiClass, InspectionEP.GLOBAL_INSPECTION)};
}
- }
- private static String getEPName(XmlTag tag) {
- String qName = tag.getAttributeValue("qualifiedName");
- if (qName != null) {
- return qName;
- }
- String name = tag.getAttributeValue("name");
- if (name != null) {
- return "com.intellij." + name;
+ ExtensionPointLocator extensionPointLocator = new ExtensionPointLocator(psiClass);
+ List<ExtensionPointCandidate> candidateList = extensionPointLocator.findSuperCandidates();
+ if (!candidateList.isEmpty()) {
+ return new IntentionAction[]{new RegisterExtensionFix(psiClass, candidateList)};
}
- return null;
+ return IntentionAction.EMPTY_ARRAY;
}
}
diff --git a/plugins/devkit/src/inspections/quickfix/RegisterInspectionFix.java b/plugins/devkit/src/inspections/quickfix/RegisterInspectionFix.java
index 2c1aec6f72c7..fb4fa6893f8d 100644
--- a/plugins/devkit/src/inspections/quickfix/RegisterInspectionFix.java
+++ b/plugins/devkit/src/inspections/quickfix/RegisterInspectionFix.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2013 JetBrains s.r.o.
+ * Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,48 +15,30 @@
*/
package org.jetbrains.idea.devkit.inspections.quickfix;
-import com.google.common.collect.ImmutableMap;
-import com.intellij.codeInsight.hint.HintManager;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInspection.InspectionEP;
-import com.intellij.ide.TypePresentationService;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.extensions.ExtensionPointName;
-import com.intellij.openapi.module.Module;
-import com.intellij.openapi.module.ModuleUtil;
+import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.roots.ProjectRootManager;
-import com.intellij.openapi.ui.popup.JBPopupFactory;
-import com.intellij.openapi.ui.popup.PopupStep;
-import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
-import com.intellij.openapi.util.Condition;
-import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.Consumer;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.PsiNavigateUtil;
-import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.xml.DomFileElement;
-import com.intellij.util.xml.DomService;
-import com.intellij.xml.util.IncludedXmlTag;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.idea.devkit.DevKitBundle;
import org.jetbrains.idea.devkit.dom.Extension;
import org.jetbrains.idea.devkit.dom.Extensions;
import org.jetbrains.idea.devkit.dom.IdeaPlugin;
-import javax.swing.*;
-import java.util.Collections;
-import java.util.List;
-
/**
-* @author Dmitry Avdeev
-* Date: 1/20/12
-*/
+ * @author Dmitry Avdeev
+ */
class RegisterInspectionFix implements IntentionAction {
private final PsiClass myPsiClass;
@@ -81,12 +63,12 @@ class RegisterInspectionFix implements IntentionAction {
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
- return true;
+ return !DumbService.isDumb(project);
}
@Override
public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file) throws IncorrectOperationException {
- choosePluginDescriptor(project, editor, file, new Consumer<DomFileElement<IdeaPlugin>>() {
+ PluginDescriptorChooser.show(project, editor, file, new Consumer<DomFileElement<IdeaPlugin>>() {
@Override
public void consume(DomFileElement<IdeaPlugin> element) {
doFix(element, project, file);
@@ -94,94 +76,12 @@ class RegisterInspectionFix implements IntentionAction {
});
}
- public static void choosePluginDescriptor(final Project project, Editor editor, final PsiFile file,
- final Consumer<DomFileElement<IdeaPlugin>> consumer) {
- Module module = ModuleUtil.findModuleForPsiElement(file);
- assert module != null;
- List<DomFileElement<IdeaPlugin>> elements =
- DomService.getInstance().getFileElements(IdeaPlugin.class, project, module.getModuleContentWithDependenciesScope());
-
- elements = ContainerUtil.filter(elements, new Condition<DomFileElement<IdeaPlugin>>() {
- @Override
- public boolean value(DomFileElement<IdeaPlugin> element) {
- VirtualFile virtualFile = element.getFile().getVirtualFile();
- return virtualFile != null && ProjectRootManager.getInstance(project).getFileIndex().isInContent(virtualFile);
- }
- });
-
- elements = findAppropriateIntelliJModule(module.getName(), elements);
-
- if (elements.isEmpty()) {
- HintManager.getInstance().showErrorHint(editor, "Cannot find plugin descriptor");
- return;
- }
-
- if (elements.size() == 1) {
- consumer.consume(elements.get(0));
- return;
- }
-
- final BaseListPopupStep<DomFileElement<IdeaPlugin>> popupStep =
- new BaseListPopupStep<DomFileElement<IdeaPlugin>>("Choose Plugin Descriptor", elements) {
-
- @Override
- public boolean isSpeedSearchEnabled() {
- return true;
- }
-
- @Override
- public Icon getIconFor(DomFileElement<IdeaPlugin> aValue) {
- return TypePresentationService.getService().getIcon(aValue);
- }
-
- @NotNull
- @Override
- public String getTextFor(DomFileElement<IdeaPlugin> value) {
- final String name = value.getFile().getName();
- final Module module = value.getModule();
- return module != null ? name + " [" + module.getName() + "]" : name;
- }
-
- @Override
- public PopupStep onChosen(DomFileElement<IdeaPlugin> selectedValue, boolean finalChoice) {
- consumer.consume(selectedValue);
- return FINAL_CHOICE;
- }
- };
- JBPopupFactory.getInstance().createListPopup(popupStep)
- .showInBestPositionFor(editor);
- }
-
- private static final ImmutableMap<String, String> INTELLIJ_MODULES = ImmutableMap.<String, String>builder()
- .put("platform-api", "PlatformExtensions.xml")
- .put("platform-impl", "PlatformExtensions.xml")
- .put("lang-api", "LangExtensions.xml")
- .put("lang-impl", "LangExtensions.xml")
- .put("vcs-api", "VcsExtensions.xml")
- .put("vcs-impl", "VcsExtensions.xml")
- .put("openapi", "IdeaPlugin.xml")
- .put("java-impl", "IdeaPlugin.xml")
- .build();
-
- private static List<DomFileElement<IdeaPlugin>> findAppropriateIntelliJModule(String name, List<DomFileElement<IdeaPlugin>> elements) {
- String extensionsFile = INTELLIJ_MODULES.get(name);
- if (extensionsFile != null) {
- for (DomFileElement<IdeaPlugin> element : elements) {
- if (element.getFile().getName().equals(extensionsFile)) {
- return Collections.singletonList(element);
- }
- }
- }
- return elements;
- }
-
- private void doFix(DomFileElement<IdeaPlugin> selectedValue, final Project project, final PsiFile file) {
- final IdeaPlugin plugin = selectedValue.getRootElement();
+ private void doFix(final DomFileElement<IdeaPlugin> selectedValue, final Project project, final PsiFile file) {
Extension extension = new WriteCommandAction<Extension>(project, file) {
@Override
- protected void run(Result<Extension> result) throws Throwable {
- final Extensions extensions = getExtension(plugin, myEp.getName());
+ protected void run(@NotNull Result<Extension> result) throws Throwable {
+ final Extensions extensions = PluginDescriptorChooser.findOrCreateExtensionsForEP(selectedValue, myEp.getName());
Extension extension = extensions.addExtension(myEp.getName());
XmlTag tag = extension.getXmlTag();
tag.setAttribute("implementationClass", myPsiClass.getQualifiedName());
@@ -191,26 +91,6 @@ class RegisterInspectionFix implements IntentionAction {
PsiNavigateUtil.navigate(extension.getXmlTag());
}
- public static Extensions getExtension(IdeaPlugin plugin, String epName) {
- final List<Extensions> extensionsList = plugin.getExtensions();
- Extensions extensions = null;
- for (Extensions e : extensionsList) {
- if (e.getXmlTag() instanceof IncludedXmlTag) {
- continue;
- }
- String s = e.getDefaultExtensionNs().getStringValue();
- if (s != null && epName.startsWith(s)) {
- extensions = e;
- break;
- }
- }
- if (extensions == null) {
- extensions = plugin.addExtensions();
- extensions.getDefaultExtensionNs().setStringValue("com.intellij");
- }
- return extensions;
- }
-
@Override
public boolean startInWriteAction() {
return false;
diff --git a/plugins/devkit/src/inspections/quickfix/UseCoupleQuickFix.java b/plugins/devkit/src/inspections/quickfix/UseCoupleQuickFix.java
new file mode 100644
index 000000000000..74850b937a5c
--- /dev/null
+++ b/plugins/devkit/src/inspections/quickfix/UseCoupleQuickFix.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.inspections.quickfix;
+
+import com.intellij.codeInspection.LocalQuickFixBase;
+import com.intellij.codeInspection.ProblemDescriptor;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.*;
+import com.intellij.psi.codeStyle.JavaCodeStyleManager;
+import org.jetbrains.annotations.NotNull;
+
+/**
+* @author Konstantin Bulenkov
+*/
+public class UseCoupleQuickFix extends LocalQuickFixBase {
+ private static final String COUPLE_FQN = "com.intellij.openapi.util.Couple";
+
+ public UseCoupleQuickFix(String text) {
+ super(text);
+ }
+
+ @Override
+ public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
+ final PsiElement element = descriptor.getPsiElement();
+ final PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
+ final PsiElement newElement;
+ if (element instanceof PsiTypeElement) {
+ final String canonicalText = ((PsiTypeElement)element).getType().getCanonicalText();
+ final String type = canonicalText.substring(canonicalText.indexOf('<') + 1, canonicalText.indexOf(','));
+ final PsiTypeElement newType = factory.createTypeElementFromText(COUPLE_FQN + "<" + type + ">", element.getContext());
+ newElement = element.replace(newType);
+ } else {
+ final String text = COUPLE_FQN + ".newOne" + element.getText().substring("Pair.create".length());
+ final PsiExpression expression = factory.createExpressionFromText(text, element.getContext());
+ newElement = element.replace(expression);
+ }
+ JavaCodeStyleManager.getInstance(project).shortenClassReferences(newElement);
+ }
+}
diff --git a/plugins/devkit/src/internal/DumpCleanHighlightingTestdataAction.java b/plugins/devkit/src/internal/DumpCleanHighlightingTestdataAction.java
new file mode 100644
index 000000000000..aeee529d9935
--- /dev/null
+++ b/plugins/devkit/src/internal/DumpCleanHighlightingTestdataAction.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2000-2013 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.internal;
+
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.fileChooser.FileChooser;
+import com.intellij.openapi.fileChooser.FileChooserDescriptor;
+import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiFile;
+import com.intellij.testFramework.ExpectedHighlightingData;
+
+import java.io.File;
+import java.io.IOException;
+
+public class DumpCleanHighlightingTestdataAction extends AnAction implements DumbAware {
+ private static final Logger LOG = Logger.getInstance("#" + DumpCleanHighlightingTestdataAction.class);
+
+ public DumpCleanHighlightingTestdataAction() {
+ super("Dump highlighting-markup-free data");
+ }
+
+ @Override
+ public void actionPerformed(final AnActionEvent event) {
+ final DataContext dataContext = event.getDataContext();
+ final Project project = CommonDataKeys.PROJECT.getData(dataContext);
+ final PsiFile psiFile = CommonDataKeys.PSI_FILE.getData(dataContext);
+ if (psiFile != null) {
+ final VirtualFile virtualFile = psiFile.getVirtualFile();
+ if (virtualFile != null) {
+ final Document document = FileDocumentManager.getInstance().getDocument(virtualFile);
+ if (document != null) {
+ final ExpectedHighlightingData data = new ExpectedHighlightingData(document, true, true);
+ data.init();
+ }
+ return;
+ }
+ }
+ final FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor();
+ descriptor.setTitle("Choose Directory");
+ descriptor.setDescription("Directory containing highlighting test data");
+ final VirtualFile dirToProcess = FileChooser.chooseFile(descriptor, project, null);
+ if (dirToProcess != null) {
+ LOG.assertTrue(project != null);
+ final FileChooserDescriptor targetDescriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor();
+ targetDescriptor.setTitle("Choose Directory");
+ targetDescriptor.setDescription("Directory where highlighting-markup-free copies would be placed");
+ final VirtualFile destinationFolder = FileChooser.chooseFile(targetDescriptor, project, null);
+ if (dirToProcess.equals(destinationFolder)) {
+ Messages.showErrorDialog(project, "Source and destination roots should differ", "Reject to Proceed");
+ return;
+ }
+ if (destinationFolder != null) {
+ final File destination = VfsUtilCore.virtualToIoFile(destinationFolder);
+ final VirtualFile[] files = dirToProcess.getChildren();
+ for (VirtualFile virtualFile : files) {
+ final Document document = FileDocumentManager.getInstance().getDocument(virtualFile);
+ if (document != null) {
+ final ExpectedHighlightingData data = new ExpectedHighlightingData(document, true, true);
+ data.init();
+ final File file = new File(destination, virtualFile.getName());
+ try {
+ FileUtil.writeToFile(file, document.getText());
+ }
+ catch (IOException e) {
+ LOG.error(e);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public void update(AnActionEvent e) {
+ e.getPresentation().setEnabled(CommonDataKeys.PROJECT.getData(e.getDataContext()) != null);
+ }
+} \ No newline at end of file
diff --git a/plugins/devkit/src/navigation/DescriptionTypeRelatedItemLineMarkerProvider.java b/plugins/devkit/src/navigation/DescriptionTypeRelatedItemLineMarkerProvider.java
new file mode 100644
index 000000000000..1d759b6a8669
--- /dev/null
+++ b/plugins/devkit/src/navigation/DescriptionTypeRelatedItemLineMarkerProvider.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.navigation;
+
+import com.intellij.codeInsight.daemon.RelatedItemLineMarkerInfo;
+import com.intellij.codeInsight.daemon.RelatedItemLineMarkerProvider;
+import com.intellij.codeInsight.navigation.NavigationGutterIconBuilder;
+import com.intellij.icons.AllIcons;
+import com.intellij.navigation.GotoRelatedItem;
+import com.intellij.openapi.editor.markup.GutterIconRenderer;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleUtilCore;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.*;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.util.InheritanceUtil;
+import com.intellij.util.NotNullFunction;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.SortedList;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.idea.devkit.inspections.DescriptionCheckerUtil;
+import org.jetbrains.idea.devkit.inspections.DescriptionType;
+import org.jetbrains.idea.devkit.inspections.InspectionDescriptionInfo;
+import org.jetbrains.idea.devkit.util.PsiUtil;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+public class DescriptionTypeRelatedItemLineMarkerProvider extends RelatedItemLineMarkerProvider {
+
+ private static final NotNullFunction<PsiFile, Collection<? extends PsiElement>> CONVERTER =
+ new NotNullFunction<PsiFile, Collection<? extends PsiElement>>() {
+ @NotNull
+ @Override
+ public Collection<? extends PsiElement> fun(PsiFile psiFile) {
+ return ContainerUtil.createMaybeSingletonList(psiFile);
+ }
+ };
+
+ private static final NotNullFunction<PsiFile, Collection<? extends GotoRelatedItem>> RELATED_ITEM_PROVIDER =
+ new NotNullFunction<PsiFile, Collection<? extends GotoRelatedItem>>() {
+ @NotNull
+ @Override
+ public Collection<? extends GotoRelatedItem> fun(PsiFile psiFile) {
+ return GotoRelatedItem.createItems(Collections.singleton(psiFile), "DevKit");
+ }
+ };
+
+ @Override
+ protected void collectNavigationMarkers(@NotNull PsiElement element, Collection<? super RelatedItemLineMarkerInfo> result) {
+ if (element instanceof PsiClass) {
+ process((PsiClass)element, result);
+ }
+ }
+
+ private static void process(PsiClass psiClass, Collection<? super RelatedItemLineMarkerInfo> result) {
+ if (!PsiUtil.isInstantiable(psiClass)) return;
+
+ Module module = ModuleUtilCore.findModuleForPsiElement(psiClass);
+ if (module == null) return;
+
+ final GlobalSearchScope scope = GlobalSearchScope.moduleRuntimeScope(module, false);
+ final PsiClass actionClass = JavaPsiFacade.getInstance(psiClass.getProject())
+ .findClass(DescriptionType.INSPECTION.getClassName(), scope);
+ if (actionClass == null) return;
+
+ PsiElement highlightingElement = psiClass.getNameIdentifier();
+ if (highlightingElement == null) return;
+
+ for (DescriptionType type : DescriptionType.values()) {
+ if (!InheritanceUtil.isInheritor(psiClass, type.getClassName())) {
+ continue;
+ }
+
+ String descriptionDirName = DescriptionCheckerUtil.getDescriptionDirName(psiClass);
+ if (StringUtil.isEmptyOrSpaces(descriptionDirName)) {
+ return;
+ }
+
+ if (type == DescriptionType.INSPECTION) {
+ final InspectionDescriptionInfo info = InspectionDescriptionInfo.create(module, psiClass);
+ if (info.hasDescriptionFile()) {
+ addDescriptionFileGutterIcon(highlightingElement, info.getDescriptionFile(), result);
+ }
+ return;
+ }
+
+ for (PsiDirectory descriptionDir : DescriptionCheckerUtil.getDescriptionsDirs(module, type)) {
+ PsiDirectory dir = descriptionDir.findSubdirectory(descriptionDirName);
+ if (dir == null) continue;
+ final PsiFile descriptionFile = dir.findFile("description.html");
+ if (descriptionFile != null) {
+ addDescriptionFileGutterIcon(highlightingElement, descriptionFile, result);
+
+ addBeforeAfterTemplateFilesGutterIcon(highlightingElement, dir, result);
+ return;
+ }
+ }
+ return;
+ }
+ }
+
+ private static void addDescriptionFileGutterIcon(PsiElement highlightingElement,
+ PsiFile descriptionFile,
+ Collection<? super RelatedItemLineMarkerInfo> result) {
+ final RelatedItemLineMarkerInfo<PsiElement> info = NavigationGutterIconBuilder
+ .create(AllIcons.FileTypes.Html, CONVERTER, RELATED_ITEM_PROVIDER)
+ .setTarget(descriptionFile)
+ .setTooltipText("Description")
+ .setAlignment(GutterIconRenderer.Alignment.RIGHT)
+ .createLineMarkerInfo(highlightingElement);
+ result.add(info);
+ }
+
+ private static void addBeforeAfterTemplateFilesGutterIcon(PsiElement highlightingElement,
+ PsiDirectory descriptionDirectory,
+ Collection<? super RelatedItemLineMarkerInfo> result) {
+ final List<PsiFile> templateFiles = new SortedList<PsiFile>(new Comparator<PsiFile>() {
+ @Override
+ public int compare(PsiFile o1, PsiFile o2) {
+ return o1.getName().compareTo(o2.getName());
+ }
+ });
+ for (PsiFile file : descriptionDirectory.getFiles()) {
+ final String fileName = file.getName();
+ if (fileName.endsWith(".template")) {
+ if (fileName.startsWith("after.") ||
+ fileName.startsWith("before.")) {
+ templateFiles.add(file);
+ }
+ }
+ }
+ if (templateFiles.isEmpty()) return;
+
+ final RelatedItemLineMarkerInfo<PsiElement> info = NavigationGutterIconBuilder
+ .create(AllIcons.Actions.Diff, CONVERTER, RELATED_ITEM_PROVIDER)
+ .setTargets(templateFiles)
+ .setPopupTitle("Select Template")
+ .setTooltipText("Before/After Templates")
+ .setAlignment(GutterIconRenderer.Alignment.RIGHT)
+ .createLineMarkerInfo(highlightingElement);
+ result.add(info);
+ }
+}
diff --git a/plugins/devkit/src/navigation/ExtensionPointDeclarationRelatedItemLineMarkerProvider.java b/plugins/devkit/src/navigation/ExtensionPointDeclarationRelatedItemLineMarkerProvider.java
new file mode 100644
index 000000000000..deb75fda4354
--- /dev/null
+++ b/plugins/devkit/src/navigation/ExtensionPointDeclarationRelatedItemLineMarkerProvider.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.navigation;
+
+import com.intellij.codeInsight.daemon.RelatedItemLineMarkerInfo;
+import com.intellij.codeInsight.daemon.RelatedItemLineMarkerProvider;
+import com.intellij.codeInsight.navigation.NavigationGutterIconBuilder;
+import com.intellij.icons.AllIcons;
+import com.intellij.navigation.GotoRelatedItem;
+import com.intellij.openapi.editor.markup.GutterIconRenderer;
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.util.Condition;
+import com.intellij.psi.*;
+import com.intellij.psi.util.PsiTypesUtil;
+import com.intellij.util.NotNullFunction;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.devkit.util.ExtensionPointCandidate;
+import org.jetbrains.idea.devkit.util.ExtensionPointLocator;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+public class ExtensionPointDeclarationRelatedItemLineMarkerProvider extends RelatedItemLineMarkerProvider {
+
+ private static final NotNullFunction<ExtensionPointCandidate, Collection<? extends PsiElement>> CONVERTER =
+ new NotNullFunction<ExtensionPointCandidate, Collection<? extends PsiElement>>() {
+ @NotNull
+ @Override
+ public Collection<? extends PsiElement> fun(ExtensionPointCandidate candidate) {
+ return Collections.singleton(candidate.pointer.getElement());
+ }
+ };
+
+ private static final NotNullFunction<ExtensionPointCandidate, Collection<? extends GotoRelatedItem>> RELATED_ITEM_PROVIDER =
+ new NotNullFunction<ExtensionPointCandidate, Collection<? extends GotoRelatedItem>>() {
+ @NotNull
+ @Override
+ public Collection<? extends GotoRelatedItem> fun(ExtensionPointCandidate candidate) {
+ return GotoRelatedItem.createItems(Collections.singleton(candidate.pointer.getElement()), "DevKit");
+ }
+ };
+
+ @Override
+ protected void collectNavigationMarkers(@NotNull PsiElement element, Collection<? super RelatedItemLineMarkerInfo> result) {
+ if (element instanceof PsiField) {
+ process((PsiField)element, result);
+ }
+ }
+
+ private static void process(PsiField psiField, Collection<? super RelatedItemLineMarkerInfo> result) {
+ if (!isExtensionPointNameDeclarationField(psiField)) return;
+
+ final PsiClass epClass = resolveExtensionPointClass(psiField);
+ if (epClass == null) return;
+
+ final String epName = resolveEpName(psiField);
+ if (epName == null) return;
+
+
+ ExtensionPointLocator locator = new ExtensionPointLocator(epClass);
+ List<ExtensionPointCandidate> targets =
+ ContainerUtil.filter(locator.findDirectCandidates(), new Condition<ExtensionPointCandidate>() {
+ @Override
+ public boolean value(ExtensionPointCandidate candidate) {
+ return epName.equals(candidate.epName);
+ }
+ });
+
+ final RelatedItemLineMarkerInfo<PsiElement> info = NavigationGutterIconBuilder
+ .create(AllIcons.Nodes.Plugin, CONVERTER, RELATED_ITEM_PROVIDER)
+ .setTargets(targets)
+ .setPopupTitle("Choose Extension Point")
+ .setTooltipText("Extension Point Declaration")
+ .setAlignment(GutterIconRenderer.Alignment.RIGHT)
+ .createLineMarkerInfo(psiField.getNameIdentifier());
+ result.add(info);
+ }
+
+ @Nullable
+ private static PsiClass resolveExtensionPointClass(PsiField psiField) {
+ final PsiClassType type = (PsiClassType)psiField.getType();
+ final PsiClassType.ClassResolveResult resolveResult = type.resolveGenerics();
+ final PsiClass psiClass = resolveResult.getElement();
+ if (psiClass == null) return null;
+ final PsiTypeParameter[] parameters = psiClass.getTypeParameters();
+ if (parameters.length != 1) return null;
+ final PsiTypeParameter parameter = parameters[0];
+ final PsiSubstitutor substitutor = resolveResult.getSubstitutor();
+ final PsiType substituteType = substitutor.substitute(parameter);
+ return PsiTypesUtil.getPsiClass(substituteType);
+ }
+
+ private static String resolveEpName(PsiField psiField) {
+ final PsiExpression initializer = psiField.getInitializer();
+
+ PsiExpressionList expressionList = null;
+ if (initializer instanceof PsiMethodCallExpression) {
+ expressionList = ((PsiMethodCallExpression)initializer).getArgumentList();
+ }
+ else if (initializer instanceof PsiNewExpression) {
+ expressionList = ((PsiNewExpression)initializer).getArgumentList();
+ }
+ if (expressionList == null) return null;
+
+ final PsiExpression[] expressions = expressionList.getExpressions();
+ if (expressions.length != 1) return null;
+
+ final PsiExpression epNameExpression = expressions[0];
+ final PsiConstantEvaluationHelper helper = JavaPsiFacade.getInstance(psiField.getProject()).getConstantEvaluationHelper();
+ final Object o = helper.computeConstantExpression(epNameExpression);
+ return o instanceof String ? (String)o : null;
+ }
+
+ private static boolean isExtensionPointNameDeclarationField(PsiField psiField) {
+ // *do* allow non-public
+ if (!psiField.hasModifierProperty(PsiModifier.FINAL) ||
+ !psiField.hasModifierProperty(PsiModifier.STATIC) ||
+ psiField.hasModifierProperty(PsiModifier.ABSTRACT)) {
+ return false;
+ }
+
+ if (!psiField.hasInitializer()) {
+ return false;
+ }
+
+ final PsiExpression initializer = psiField.getInitializer();
+ if (!(initializer instanceof PsiMethodCallExpression) &&
+ !(initializer instanceof PsiNewExpression)) {
+ return false;
+ }
+
+ final PsiClass fieldClass = PsiTypesUtil.getPsiClass(psiField.getType());
+ if (fieldClass == null) {
+ return false;
+ }
+
+ return ExtensionPointName.class.getName().equals(fieldClass.getQualifiedName());
+ }
+}
diff --git a/plugins/devkit/src/testAssistant/NavigateToTestDataAction.java b/plugins/devkit/src/testAssistant/NavigateToTestDataAction.java
new file mode 100644
index 000000000000..89e10e019632
--- /dev/null
+++ b/plugins/devkit/src/testAssistant/NavigateToTestDataAction.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2000-2013 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.testAssistant;
+
+import com.intellij.codeInsight.AnnotationUtil;
+import com.intellij.execution.Location;
+import com.intellij.execution.junit.JUnitUtil;
+import com.intellij.execution.junit2.PsiMemberParameterizedLocation;
+import com.intellij.notification.Notification;
+import com.intellij.notification.NotificationType;
+import com.intellij.notification.Notifications;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.actionSystem.DataContext;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ProjectFileIndex;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.ui.popup.JBPopupFactory;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.*;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.testFramework.Parameterized;
+import com.intellij.ui.awt.RelativePoint;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author yole
+ */
+public class NavigateToTestDataAction extends AnAction {
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ final DataContext dataContext = e.getDataContext();
+ final Project project = CommonDataKeys.PROJECT.getData(dataContext);
+ List<String> fileNames = findTestDataFiles(dataContext);
+ if (fileNames == null || fileNames.isEmpty()) {
+ String message = "Cannot find testdata files for class";
+ final Notification notification = new Notification("testdata", "Found no testdata files", message, NotificationType.INFORMATION);
+ Notifications.Bus.notify(notification, project);
+ }
+ else {
+ final Editor editor = e.getData(CommonDataKeys.EDITOR);
+ final JBPopupFactory popupFactory = JBPopupFactory.getInstance();
+ final RelativePoint point = editor != null ? popupFactory.guessBestPopupLocation(editor) : popupFactory.guessBestPopupLocation(dataContext);
+
+ TestDataNavigationHandler.navigate(point, fileNames, project);
+ }
+ }
+
+ @Nullable
+ public static List<String> findTestDataFiles(@NotNull DataContext context) {
+ final PsiMethod method = findTargetMethod(context);
+ if (method == null) {
+ return null;
+ }
+ final String name = method.getName();
+
+ if (name.startsWith("test")) {
+ String testDataPath = TestDataLineMarkerProvider.getTestDataBasePath(method.getContainingClass());
+ final TestDataReferenceCollector collector = new TestDataReferenceCollector(testDataPath, name.substring(4));
+ return collector.collectTestDataReferences(method);
+ }
+
+ final Location<?> location = Location.DATA_KEY.getData(context);
+ if (location instanceof PsiMemberParameterizedLocation) {
+ PsiClass containingClass = ((PsiMemberParameterizedLocation)location).getContainingClass();
+ if (containingClass == null) {
+ containingClass = PsiTreeUtil.getParentOfType(location.getPsiElement(), PsiClass.class, false);
+ }
+ if (containingClass != null) {
+ final PsiAnnotation annotation = AnnotationUtil.findAnnotationInHierarchy(containingClass, Collections.singleton(JUnitUtil.RUN_WITH));
+ if (annotation != null) {
+ final PsiAnnotationMemberValue memberValue = annotation.findAttributeValue("value");
+ if (memberValue instanceof PsiClassObjectAccessExpression) {
+ final PsiTypeElement operand = ((PsiClassObjectAccessExpression)memberValue).getOperand();
+ if (operand.getType().equalsToText(Parameterized.class.getName())) {
+ final String testDataPath = TestDataLineMarkerProvider.getTestDataBasePath(containingClass);
+ final String paramSetName = ((PsiMemberParameterizedLocation)location).getParamSetName();
+ final String baseFileName = StringUtil.trimEnd(StringUtil.trimStart(paramSetName, "["), "]");
+ final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(containingClass.getProject()).getFileIndex();
+ return TestDataGuessByExistingFilesUtil.suggestTestDataFiles(fileIndex, baseFileName, testDataPath, containingClass);
+ }
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public void update(AnActionEvent e) {
+ e.getPresentation().setEnabled(findTargetMethod(e.getDataContext()) != null);
+ }
+
+ @Nullable
+ private static PsiMethod findTargetMethod(@NotNull DataContext context) {
+ final Editor editor = CommonDataKeys.EDITOR.getData(context);
+ final PsiFile file = CommonDataKeys.PSI_FILE.getData(context);
+ if (file != null && editor != null) {
+ PsiElement element = file.findElementAt(editor.getCaretModel().getOffset());
+ return PsiTreeUtil.getParentOfType(element, PsiMethod.class);
+ }
+
+ final Location<?> location = Location.DATA_KEY.getData(context);
+ if (location != null) {
+ final PsiElement element = location.getPsiElement();
+ if (element instanceof PsiMethod) {
+ return (PsiMethod)element;
+ }
+ }
+ return null;
+ }
+}
diff --git a/plugins/devkit/src/testAssistant/TestCaseAsRelatedFileProvider.java b/plugins/devkit/src/testAssistant/TestCaseAsRelatedFileProvider.java
new file mode 100644
index 000000000000..bbe3e54899f7
--- /dev/null
+++ b/plugins/devkit/src/testAssistant/TestCaseAsRelatedFileProvider.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.testAssistant;
+
+import com.intellij.execution.Location;
+import com.intellij.navigation.GotoRelatedItem;
+import com.intellij.navigation.GotoRelatedProvider;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.actionSystem.DataContext;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.Function;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collections;
+import java.util.List;
+
+public class TestCaseAsRelatedFileProvider extends GotoRelatedProvider {
+ @NotNull
+ @Override
+ public List<? extends GotoRelatedItem> getItems(@NotNull DataContext context) {
+ final Editor editor = CommonDataKeys.EDITOR.getData(context);
+ final Project project = CommonDataKeys.PROJECT.getData(context);
+ final VirtualFile file = CommonDataKeys.VIRTUAL_FILE.getData(context);
+ if (editor == null || file == null || project == null) {
+ return Collections.emptyList();
+ }
+
+ final List<Location> locations = TestLocationDataRule.collectRelativeLocations(project, file);
+ if (locations.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ return ContainerUtil.map(locations, new Function<Location, GotoRelatedItem>() {
+ @Override
+ public GotoRelatedItem fun(Location location) {
+ return new GotoRelatedItem(location.getPsiElement());
+ }
+ });
+ }
+}
diff --git a/plugins/devkit/src/testAssistant/TestDataAsRelatedFileProvider.java b/plugins/devkit/src/testAssistant/TestDataAsRelatedFileProvider.java
new file mode 100644
index 000000000000..f4cb2cf2f608
--- /dev/null
+++ b/plugins/devkit/src/testAssistant/TestDataAsRelatedFileProvider.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2000-2013 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.testAssistant;
+
+import com.intellij.navigation.GotoRelatedItem;
+import com.intellij.navigation.GotoRelatedProvider;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.actionSystem.DataContext;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiMethod;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author Denis Zhdanov
+ * @since 5/24/11 4:52 PM
+ */
+public class TestDataAsRelatedFileProvider extends GotoRelatedProvider {
+
+ @NotNull
+ @Override
+ public List<? extends GotoRelatedItem> getItems(@NotNull DataContext context) {
+ final Editor editor = CommonDataKeys.EDITOR.getData(context);
+ final Project project = CommonDataKeys.PROJECT.getData(context);
+ final PsiElement element = CommonDataKeys.PSI_ELEMENT.getData(context);
+ if (editor == null || element == null || project == null) {
+ return Collections.emptyList();
+ }
+
+ PsiMethod method = null;
+ for (PsiElement e = element; e != null; e = e.getParent()) {
+ if (e instanceof PsiMethod) {
+ method = (PsiMethod)e;
+ break;
+ }
+ }
+ if (method == null) {
+ return Collections.emptyList();
+ }
+
+ final List<String> testDataFiles = NavigateToTestDataAction.findTestDataFiles(context);
+ if (testDataFiles == null || testDataFiles.isEmpty()) {
+ return Collections.emptyList();
+ }
+ return Collections.singletonList(new TestDataRelatedItem(method, editor, testDataFiles));
+ }
+}
diff --git a/plugins/devkit/src/testAssistant/TestDataGroupEditorProvider.java b/plugins/devkit/src/testAssistant/TestDataGroupEditorProvider.java
new file mode 100644
index 000000000000..75b17e33dcfc
--- /dev/null
+++ b/plugins/devkit/src/testAssistant/TestDataGroupEditorProvider.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2000-2010 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.testAssistant;
+
+import com.intellij.openapi.fileEditor.FileEditor;
+import com.intellij.openapi.fileEditor.FileEditorPolicy;
+import com.intellij.openapi.fileEditor.FileEditorProvider;
+import com.intellij.openapi.fileEditor.FileEditorState;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jdom.Element;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author yole
+ */
+public class TestDataGroupEditorProvider implements FileEditorProvider {
+ public boolean accept(@NotNull Project project, @NotNull VirtualFile file) {
+ return file instanceof TestDataGroupVirtualFile;
+ }
+
+ @NotNull
+ public FileEditor createEditor(@NotNull Project project, @NotNull VirtualFile file) {
+ return new TestDataGroupFileEditor(project, (TestDataGroupVirtualFile) file);
+ }
+
+ public void disposeEditor(@NotNull FileEditor editor) {
+ Disposer.dispose(editor);
+ }
+
+ @NotNull
+ public FileEditorState readState(@NotNull Element sourceElement, @NotNull Project project, @NotNull VirtualFile file) {
+ return FileEditorState.INSTANCE;
+ }
+
+ public void writeState(@NotNull FileEditorState state, @NotNull Project project, @NotNull Element targetElement) {
+ }
+
+ @NotNull
+ public String getEditorTypeId() {
+ return "TestDataGroup";
+ }
+
+ @NotNull
+ public FileEditorPolicy getPolicy() {
+ return FileEditorPolicy.HIDE_DEFAULT_EDITOR;
+ }
+}
diff --git a/plugins/devkit/src/testAssistant/TestDataGroupFileEditor.java b/plugins/devkit/src/testAssistant/TestDataGroupFileEditor.java
new file mode 100644
index 000000000000..70d3cdec18bb
--- /dev/null
+++ b/plugins/devkit/src/testAssistant/TestDataGroupFileEditor.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2000-2010 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.testAssistant;
+
+import com.intellij.codeHighlighting.BackgroundEditorHighlighter;
+import com.intellij.ide.structureView.StructureViewBuilder;
+import com.intellij.openapi.fileEditor.FileEditor;
+import com.intellij.openapi.fileEditor.FileEditorLocation;
+import com.intellij.openapi.fileEditor.FileEditorState;
+import com.intellij.openapi.fileEditor.FileEditorStateLevel;
+import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Splitter;
+import com.intellij.openapi.util.UserDataHolderBase;
+import com.intellij.reference.SoftReference;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.awt.*;
+import java.beans.PropertyChangeListener;
+import java.lang.ref.WeakReference;
+
+/**
+ * @author yole
+ */
+public class TestDataGroupFileEditor extends UserDataHolderBase implements FileEditor {
+ private WeakReference<JComponent> myComponent;
+ private final TestDataGroupVirtualFile myFile;
+ private final FileEditor myBeforeEditor;
+ private final FileEditor myAfterEditor;
+
+ public TestDataGroupFileEditor(Project project, TestDataGroupVirtualFile file) {
+ myFile = file;
+ myBeforeEditor = TextEditorProvider.getInstance().createEditor(project, file.getBeforeFile());
+ myAfterEditor = TextEditorProvider.getInstance().createEditor(project, file.getAfterFile());
+ }
+
+ @NotNull
+ public JComponent getComponent() {
+ JComponent result = SoftReference.dereference(myComponent);
+ if (result == null) {
+ myComponent = new WeakReference<JComponent>(result = createComponent());
+ }
+ return result;
+ }
+
+ private JComponent createComponent() {
+ Splitter splitter = new Splitter(false);
+ splitter.setFirstComponent(wrapWithTitle(myFile.getBeforeFile().getName(), myBeforeEditor));
+ splitter.setSecondComponent(wrapWithTitle(myFile.getAfterFile().getName(), myAfterEditor));
+ return splitter;
+ }
+
+ private static JComponent wrapWithTitle(String name, final FileEditor beforeEditor) {
+ JPanel panel = new JPanel(new BorderLayout());
+ final JLabel label = new JLabel(name);
+ label.setBorder(BorderFactory.createEmptyBorder(1, 4, 2, 0));
+ label.setFont(label.getFont().deriveFont(Font.BOLD));
+ panel.add(BorderLayout.NORTH, label);
+ panel.add(BorderLayout.CENTER, beforeEditor.getComponent());
+ return panel;
+ }
+
+ public JComponent getPreferredFocusedComponent() {
+ return null;
+ }
+
+ @NotNull
+ public String getName() {
+ return myFile.getName();
+ }
+
+ @NotNull
+ public FileEditorState getState(@NotNull FileEditorStateLevel level) {
+ return FileEditorState.INSTANCE;
+ }
+
+ public void setState(@NotNull FileEditorState state) {
+ }
+
+ public boolean isModified() {
+ return myBeforeEditor.isModified() || myAfterEditor.isModified();
+ }
+
+ public boolean isValid() {
+ return myBeforeEditor.isValid() && myAfterEditor.isValid();
+ }
+
+ public void selectNotify() {
+ }
+
+ public void deselectNotify() {
+ }
+
+ public void addPropertyChangeListener(@NotNull PropertyChangeListener listener) {
+ }
+
+ public void removePropertyChangeListener(@NotNull PropertyChangeListener listener) {
+ }
+
+ public BackgroundEditorHighlighter getBackgroundHighlighter() {
+ return null;
+ }
+
+ public FileEditorLocation getCurrentLocation() {
+ return null;
+ }
+
+ public StructureViewBuilder getStructureViewBuilder() {
+ return null;
+ }
+
+ public void dispose() {
+ TextEditorProvider.getInstance().disposeEditor(myBeforeEditor);
+ TextEditorProvider.getInstance().disposeEditor(myAfterEditor);
+ }
+}
diff --git a/plugins/devkit/src/testAssistant/TestDataGroupVirtualFile.java b/plugins/devkit/src/testAssistant/TestDataGroupVirtualFile.java
new file mode 100644
index 000000000000..14b7ab3f0a53
--- /dev/null
+++ b/plugins/devkit/src/testAssistant/TestDataGroupVirtualFile.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.testAssistant;
+
+import com.intellij.ide.presentation.Presentation;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileSystem;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * @author yole
+ */
+@Presentation(icon = "AllIcons.Nodes.TestSourceFolder")
+public class TestDataGroupVirtualFile extends VirtualFile {
+ private final VirtualFile myBeforeFile;
+ private final VirtualFile myAfterFile;
+
+ public TestDataGroupVirtualFile(VirtualFile beforeFile, VirtualFile afterFile) {
+ myBeforeFile = beforeFile;
+ myAfterFile = afterFile;
+ }
+
+ @NotNull
+ @Override
+ public String getName() {
+ final String prefix = StringUtil.commonPrefix(myBeforeFile.getName(), myAfterFile.getName());
+ if (prefix.isEmpty()) {
+ return StringUtil.commonSuffix(myBeforeFile.getName(), myAfterFile.getName());
+ }
+ return prefix + "." + myBeforeFile.getExtension();
+ }
+
+ public VirtualFile getBeforeFile() {
+ return myBeforeFile;
+ }
+
+ public VirtualFile getAfterFile() {
+ return myAfterFile;
+ }
+
+ @NotNull
+ @Override
+ public VirtualFileSystem getFileSystem() {
+ return LocalFileSystem.getInstance();
+ }
+
+ @NotNull
+ @Override
+ public String getPath() {
+ return myBeforeFile.getPath();
+ }
+
+ @Override
+ public boolean isWritable() {
+ return true;
+ }
+
+ @Override
+ public boolean isDirectory() {
+ return false;
+ }
+
+ @Override
+ public boolean isValid() {
+ return myBeforeFile.isValid() && myAfterFile.isValid();
+ }
+
+ @Override
+ public VirtualFile getParent() {
+ return null;
+ }
+
+ @Override
+ public VirtualFile[] getChildren() {
+ return EMPTY_ARRAY;
+ }
+
+ @NotNull
+ @Override
+ public OutputStream getOutputStream(Object requestor, long newModificationStamp, long newTimeStamp) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @NotNull
+ @Override
+ public byte[] contentsToByteArray() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public long getTimeStamp() {
+ return 0;
+ }
+
+ @Override
+ public long getLength() {
+ return 0;
+ }
+
+ @Override
+ public long getModificationStamp() {
+ return 0;
+ }
+
+ @Override
+ public void refresh(boolean asynchronous, boolean recursive, Runnable postRunnable) {
+ }
+
+ @Override
+ public InputStream getInputStream() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/plugins/devkit/src/testAssistant/TestDataGuessByExistingFilesUtil.java b/plugins/devkit/src/testAssistant/TestDataGuessByExistingFilesUtil.java
new file mode 100644
index 000000000000..770e8e89c83f
--- /dev/null
+++ b/plugins/devkit/src/testAssistant/TestDataGuessByExistingFilesUtil.java
@@ -0,0 +1,564 @@
+package org.jetbrains.idea.devkit.testAssistant;
+
+import com.intellij.codeInsight.AnnotationUtil;
+import com.intellij.ide.util.gotoByName.GotoFileModel;
+import com.intellij.openapi.extensions.Extensions;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.roots.ProjectFileIndex;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.Trinity;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.codeStyle.NameUtil;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.testIntegration.TestFramework;
+import com.intellij.util.PathUtil;
+import com.intellij.util.containers.ConcurrentHashMap;
+import com.intellij.util.containers.HashSet;
+import com.intellij.util.containers.LinkedMultiMap;
+import com.intellij.util.containers.MultiMap;
+import com.intellij.util.text.Matcher;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * There is a possible case that particular test class is not properly configured with test annotations but uses test data files.
+ * This class contains utility methods for guessing test data files location and name patterns from existing one.
+ *
+ * @author Denis Zhdanov
+ * @since 5/24/11 2:28 PM
+ */
+public class TestDataGuessByExistingFilesUtil {
+
+ private static final long CACHE_ENTRY_TTL_MS = TimeUnit.MILLISECONDS.convert(5, TimeUnit.MINUTES);
+
+ private static final Map<String, Pair<TestDataDescriptor, Long>> CACHE = new ConcurrentHashMap<String, Pair<TestDataDescriptor, Long>>();
+ private static final Set<String> CLASSES_WITHOUT_TEST_DATA = new java.util.HashSet<String>();
+
+ private TestDataGuessByExistingFilesUtil() {
+ }
+
+ /**
+ * Tries to guess what test data files match to the given method if it's test method and there are existing test data
+ * files for the target test class.
+ *
+ * @param method test method candidate
+ * @return collection of paths to the test data files for the given test if it's possible to guess them;
+ * <code>null</code> otherwise
+ */
+ @Nullable
+ static List<String> collectTestDataByExistingFiles(@NotNull PsiMethod method) {
+ if (getTestName(method) == null) {
+ return null;
+ }
+ PsiFile psiFile = getParent(method, PsiFile.class);
+ if (psiFile == null) {
+ return null;
+ }
+ return collectTestDataByExistingFiles(psiFile, getTestName(method.getName()));
+ }
+
+ @Nullable
+ private static <T extends PsiElement> T getParent(@NotNull PsiElement element, Class<T> clazz) {
+ for (PsiElement e = element; e != null; e = e.getParent()) {
+ if (clazz.isAssignableFrom(e.getClass())) {
+ return clazz.cast(e);
+ }
+ }
+ return null;
+ }
+
+ @Nullable
+ static List<String> collectTestDataByExistingFiles(@NotNull PsiFile psiFile, @NotNull String testName) {
+ TestDataDescriptor descriptor = buildDescriptorFromExistingTestData(psiFile);
+ if (descriptor == null || !descriptor.isComplete()) {
+ return null;
+ }
+
+ return descriptor.generate(testName);
+ }
+
+ @Nullable
+ private static String getTestName(@NotNull PsiMethod method) {
+ final PsiClass psiClass = getParent(method, PsiClass.class);
+ if (psiClass == null) {
+ return null;
+ }
+
+ TestFramework[] frameworks = Extensions.getExtensions(TestFramework.EXTENSION_NAME);
+ TestFramework framework = null;
+ for (TestFramework each : frameworks) {
+ if (each.isTestClass(psiClass)) {
+ framework = each;
+ break;
+ }
+ }
+
+ if (framework == null || isUtilityMethod(method, psiClass, framework)) {
+ return null;
+ }
+
+ return getTestName(method.getName());
+ }
+
+ private static boolean isUtilityMethod(@NotNull PsiMethod method, @NotNull PsiClass psiClass, @NotNull TestFramework framework) {
+ if (method == framework.findSetUpMethod(psiClass) || method == framework.findTearDownMethod(psiClass)) {
+ return true;
+ }
+
+ // JUnit3
+ if (framework.getClass().getName().contains("JUnit3")) {
+ return !method.getName().startsWith("test");
+ }
+
+ // JUnit4
+ else if (framework.getClass().getName().contains("JUnit4")) {
+ return !AnnotationUtil.isAnnotated(method, "org.junit.Test", false);
+ }
+ return false;
+ }
+
+ @NotNull
+ public static String getTestName(@NotNull String methodName) {
+ return methodName.startsWith("test") ? methodName.substring("test".length()) : methodName;
+ }
+
+ @Nullable
+ private static TestDataDescriptor buildDescriptorFromExistingTestData(@NotNull PsiFile file) {
+ final PsiClass psiClass = PsiTreeUtil.getChildOfType(file, PsiClass.class);
+ if (psiClass == null) {
+ return null;
+ }
+
+ final String qualifiedName = psiClass.getQualifiedName();
+ if (CLASSES_WITHOUT_TEST_DATA.contains(qualifiedName)) {
+ return null;
+ }
+ final Pair<TestDataDescriptor, Long> cached = CACHE.get(qualifiedName);
+ if (cached != null) {
+ if (cached.first.isComplete()) {
+ return cached.first;
+ }
+ if (cached.second > System.currentTimeMillis()) {
+ return null;
+ }
+ }
+
+ TestFramework[] frameworks = Extensions.getExtensions(TestFramework.EXTENSION_NAME);
+ TestFramework framework = null;
+ for (TestFramework each : frameworks) {
+ if (each.isTestClass(psiClass)) {
+ framework = each;
+ break;
+ }
+ }
+ if (framework == null) {
+ return null;
+ }
+
+ final PsiElement setUpMethod = framework.findSetUpMethod(psiClass);
+ final PsiElement tearDownMethod = framework.findTearDownMethod(psiClass);
+ List<String> testNames = new ArrayList<String>();
+ for (PsiMethod method : psiClass.getMethods()) {
+ final String name = getTestName(method.getName());
+ if (StringUtil.isEmpty(name) || method == setUpMethod || method == tearDownMethod || name.equals(psiClass.getName())
+ || isUtilityMethod(method, psiClass, framework))
+ {
+ continue;
+ }
+ testNames.add(name);
+ }
+
+ ProjectFileIndex fileIndex = ProjectRootManager.getInstance(psiClass.getProject()).getFileIndex();
+ final TestDataDescriptor descriptor = buildDescriptor(fileIndex, testNames, psiClass);
+ if (isClassWithoutTestData(descriptor, testNames, psiClass)) {
+ CLASSES_WITHOUT_TEST_DATA.add(qualifiedName);
+ return null;
+ }
+ CACHE.put(qualifiedName, new Pair<TestDataDescriptor, Long>(descriptor, System.currentTimeMillis() + CACHE_ENTRY_TTL_MS));
+ return descriptor;
+ }
+
+ private static boolean isClassWithoutTestData(@NotNull TestDataDescriptor descriptor, @NotNull List<String> testNames,
+ @NotNull PsiClass psiClass) {
+ if (testNames.size() <= 1) {
+ // There is a possible case that the test class is just created.
+ return false;
+ }
+
+ if (!descriptor.isComplete()) {
+ return true;
+ }
+
+ boolean tooGenericNames = true;
+ genericNamesLoop:
+ for (String testName : testNames) {
+ for (int i = 0; i < testName.length(); i++) {
+ if (!Character.isDigit(testName.charAt(i))) {
+ tooGenericNames = false;
+ break genericNamesLoop;
+ }
+ }
+ }
+
+ final String simpleClassName = getSimpleClassName(psiClass);
+ if (tooGenericNames
+ && (simpleClassName == null || !descriptor.myDescriptors.get(0).dir.toLowerCase().contains(simpleClassName.toLowerCase())))
+ {
+ return true;
+ }
+
+ // We assume that test has test data if max(2; half of tests) tests already have test data.
+ int toMatch = Math.max(2, testNames.size() / 2);
+ for (String testName : testNames) {
+ if (toMatch <= 0) {
+ return false;
+ }
+ final List<String> testDataFiles = descriptor.generate(testName);
+ for (String path : testDataFiles) {
+ if (new File(path).isFile()) {
+ // There is a possible case that particular test has only one test data file though the others have
+ // two (e.g. during testing caret position at virtual space).
+ toMatch--;
+ break;
+ }
+ }
+ }
+
+ return toMatch > 0;
+ }
+
+ //@NotNull
+ //private static Collection<VirtualFile> getMatchedFiles(@NotNull final Project project, @NotNull final String testName) {
+ // final List<VirtualFile> result = new ArrayList<VirtualFile>();
+ // final char c = testName.charAt(0);
+ // final String testNameWithDifferentRegister =
+ // (Character.isLowerCase(c) ? Character.toUpperCase(c) : Character.toLowerCase(c)) + testName.substring(1);
+ // final GlobalSearchScope scope = ProjectScope.getProjectScope(project);
+ // FileBasedIndex.getInstance().processAllKeys(FilenameIndex.NAME, new Processor<String>() {
+ // @Override
+ // public boolean process(String s) {
+ // if (!s.contains(testName) && !s.contains(testNameWithDifferentRegister)) {
+ // return true;
+ // }
+ //
+ // final NavigationItem[] items = FilenameIndex.getFilesByName(project, s, scope);
+ // if (items != null) {
+ // for (NavigationItem item : items) {
+ // if (item instanceof PsiFile) {
+ // result.add(((PsiFile)item).getVirtualFile());
+ // }
+ // }
+ // }
+ // return true;
+ // }
+ // }, project);
+ // return result;
+ //}
+
+ public static List<String> suggestTestDataFiles(@NotNull ProjectFileIndex fileIndex,
+ @NotNull String testName,
+ String testDataPath,
+ @NotNull PsiClass psiClass){
+ return buildDescriptor(fileIndex, Collections.singletonList(testName), psiClass).generate(testName, testDataPath);
+ }
+
+
+ @NotNull
+ private static TestDataDescriptor buildDescriptor(@NotNull ProjectFileIndex fileIndex,
+ @NotNull Collection<String> testNames,
+ @NotNull PsiClass psiClass)
+ {
+ GotoFileModel gotoModel = new GotoFileModel(psiClass.getProject());
+ List<Trinity<Matcher, String, String>> input = new ArrayList<Trinity<Matcher, String, String>>();
+ Set<String> testNamesLowerCase = new HashSet<String>();
+ for (String testName : testNames) {
+ String pattern = String.format("*%s*", testName);
+ input.add(new Trinity<Matcher, String, String>(
+ NameUtil.buildMatcher(pattern, 0, true, true, pattern.toLowerCase().equals(pattern)), testName, pattern
+ ));
+ testNamesLowerCase.add(testName.toLowerCase());
+ }
+ Set<TestLocationDescriptor> descriptors = new HashSet<TestLocationDescriptor>();
+ MultiMap<String, Trinity<Matcher, String, String>> map = getAllFileNames(input, gotoModel);
+ for (String name : map.keySet()) {
+ ProgressManager.checkCanceled();
+ boolean currentNameProcessed = false;
+ for (Trinity<Matcher, String, String> trinity : map.get(name)) {
+ final Object[] elements = gotoModel.getElementsByName(name, false, trinity.third);
+ if (elements == null) {
+ continue;
+ }
+ for (Object element : elements) {
+ if (!(element instanceof PsiFile)) {
+ continue;
+ }
+ final VirtualFile file = ((PsiFile)element).getVirtualFile();
+ if (file == null || fileIndex.isInSource(file)) {
+ continue;
+ }
+
+
+ final String filePath = PathUtil.getFileName(file.getPath()).toLowerCase();
+ int i = filePath.indexOf(trinity.second.toLowerCase());
+ // Skip files that doesn't contain target test name and files that contain digit after target test name fragment.
+ // Example: there are tests with names 'testEnter()' and 'testEnter2()' and we don't want test data file 'testEnter2'
+ // to be matched to the test 'testEnter()'.
+ if (i < 0 || (i + trinity.second.length() < filePath.length())
+ && Character.isDigit(filePath.charAt(i + trinity.second.length())))
+ {
+ continue;
+ }
+
+ TestLocationDescriptor current = new TestLocationDescriptor();
+ current.populate(trinity.second, file);
+ if (!current.isComplete()) {
+ continue;
+ }
+
+ // Handle situations like the one below:
+ // *) test class has tests with names 'testAlignedParameters' and 'testNonAlignedParameters';
+ // *) test data files with the following names present: 'AlignedParameters.java' and 'NonAlignedParameters.java';
+ // *) we're processing the following (test; test data file) pair - ('testAlignedParameters'; 'NonAlignedParameters.java');
+ // We don't want to store descriptor with file prefix 'Non' here.
+ // The same is true for suffixes, e.g. tests like 'testLeaveValidCodeBlock()' and 'testLeaveValidCodeBlockWithEmptyLineAfterIt()'
+ String prefixPattern = current.filePrefix.toLowerCase();
+ boolean checkPrefix = !StringUtil.isEmpty(prefixPattern);
+ String suffixPattern = current.fileSuffix;
+ for (TestLocationDescriptor descriptor : descriptors) {
+ if (suffixPattern.endsWith(descriptor.fileSuffix)) {
+ suffixPattern = suffixPattern.substring(0, suffixPattern.length() - descriptor.fileSuffix.length());
+ }
+ }
+ suffixPattern = suffixPattern.toLowerCase();
+ boolean checkSuffix = !StringUtil.isEmpty(suffixPattern);
+ boolean skip = false;
+ for (String testName : testNamesLowerCase) {
+ if (testName.equals(trinity.second)) {
+ continue;
+ }
+ if ((checkPrefix && testName.startsWith(prefixPattern)) || (checkSuffix && testName.endsWith(suffixPattern))) {
+ skip = true;
+ break;
+ }
+ }
+ if (skip) {
+ continue;
+ }
+
+ currentNameProcessed = true;
+ if (descriptors.isEmpty() || (descriptors.iterator().next().dir.equals(current.dir) && !descriptors.contains(current))) {
+ descriptors.add(current);
+ continue;
+ }
+ if (moreRelevantPath(current, descriptors, psiClass)) {
+ descriptors.clear();
+ descriptors.add(current);
+ }
+ }
+ if (currentNameProcessed) {
+ break;
+ }
+ }
+ }
+ return new TestDataDescriptor(descriptors);
+ }
+
+ private static synchronized MultiMap<String, Trinity<Matcher, String, String>> getAllFileNames(List<Trinity<Matcher, String, String>> input,
+ final GotoFileModel model) {
+ LinkedMultiMap<String, Trinity<Matcher, String, String>> map = new LinkedMultiMap<String, Trinity<Matcher, String, String>>();
+ for (String name : model.getNames(false)) {
+ ProgressManager.checkCanceled();
+ for (Trinity<Matcher, String, String> trinity : input) {
+ if (trinity.first.matches(name)) {
+ map.putValue(name, trinity);
+ }
+ }
+ }
+ return map;
+ }
+
+ @Nullable
+ private static String getSimpleClassName(@NotNull PsiClass psiClass) {
+ String result = psiClass.getQualifiedName();
+ if (result == null) {
+ return null;
+ }
+ if (result.endsWith("Test")) {
+ result = result.substring(0, result.length() - "Test".length());
+ }
+ int i = result.lastIndexOf('.');
+ if (i >= 0) {
+ result = result.substring(i + 1);
+ }
+ return result;
+ }
+
+ private static boolean moreRelevantPath(@NotNull TestLocationDescriptor candidate,
+ @NotNull Set<TestLocationDescriptor> currentDescriptors,
+ @NotNull PsiClass psiClass)
+ {
+ final String className = psiClass.getQualifiedName();
+ if (className == null) {
+ return false;
+ }
+
+ final TestLocationDescriptor current = currentDescriptors.iterator().next();
+ boolean candidateMatched;
+ boolean currentMatched;
+
+ // By package.
+ int i = className.lastIndexOf(".");
+ if (i >= 0) {
+ String packageAsPath = className.substring(0, i).replace('.', '/').toLowerCase();
+ candidateMatched = candidate.dir.toLowerCase().contains(packageAsPath);
+ currentMatched = current.dir.toLowerCase().contains(packageAsPath);
+ if (candidateMatched ^ currentMatched) {
+ return candidateMatched;
+ }
+ }
+
+ // By class name.
+ String simpleName = getSimpleClassName(psiClass);
+ if (simpleName != null) {
+ String pattern = simpleName.toLowerCase();
+ candidateMatched = candidate.dir.toLowerCase().contains(pattern);
+ currentMatched = current.dir.toLowerCase().contains(pattern);
+ if (candidateMatched ^ currentMatched) {
+ return candidateMatched;
+ }
+ }
+
+ return false;
+ }
+
+ private static class TestLocationDescriptor {
+
+ public String dir;
+ public String filePrefix;
+ public String fileSuffix;
+ public String ext;
+ public boolean startWithLowerCase;
+
+ public boolean isComplete() {
+ return dir != null && filePrefix != null && fileSuffix != null && ext != null;
+ }
+
+ public void populate(@NotNull String testName, @NotNull VirtualFile matched) {
+ final String withoutExtension = FileUtil.getNameWithoutExtension(testName);
+ boolean excludeExtension = !withoutExtension.equals(testName);
+ testName = withoutExtension;
+ final String fileName = matched.getNameWithoutExtension();
+ int i = fileName.indexOf(testName);
+ final char firstChar = testName.charAt(0);
+ boolean testNameStartsWithLowerCase = Character.isLowerCase(firstChar);
+ if (i < 0) {
+ i = fileName.indexOf(
+ (testNameStartsWithLowerCase ? Character.toUpperCase(firstChar) : Character.toLowerCase(firstChar)) + testName.substring(1)
+ );
+ startWithLowerCase = !testNameStartsWithLowerCase;
+ }
+ else {
+ startWithLowerCase = testNameStartsWithLowerCase;
+ }
+ if (i < 0) {
+ return;
+ }
+
+ filePrefix = fileName.substring(0, i);
+ fileSuffix = fileName.substring(i + testName.length());
+ ext = excludeExtension ? "" : matched.getExtension();
+ dir = matched.getParent().getPath();
+ }
+
+ @Override
+ public int hashCode() {
+ int result = dir != null ? dir.hashCode() : 0;
+ result = 31 * result + (filePrefix != null ? filePrefix.hashCode() : 0);
+ result = 31 * result + (fileSuffix != null ? fileSuffix.hashCode() : 0);
+ result = 31 * result + (ext != null ? ext.hashCode() : 0);
+ result = 31 * result + (startWithLowerCase ? 1 : 0);
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ TestLocationDescriptor that = (TestLocationDescriptor)o;
+ if (startWithLowerCase != that.startWithLowerCase) return false;
+ if (dir != null ? !dir.equals(that.dir) : that.dir != null) return false;
+ if (ext != null ? !ext.equals(that.ext) : that.ext != null) return false;
+ if (filePrefix != null ? !filePrefix.equals(that.filePrefix) : that.filePrefix != null) return false;
+ if (fileSuffix != null ? !fileSuffix.equals(that.fileSuffix) : that.fileSuffix != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s/%s[...]%s.%s", dir, filePrefix, fileSuffix, ext);
+ }
+ }
+
+ private static class TestDataDescriptor {
+
+ private final List<TestLocationDescriptor> myDescriptors = new ArrayList<TestLocationDescriptor>();
+
+ TestDataDescriptor(Collection<TestLocationDescriptor> descriptors) {
+ myDescriptors.addAll(descriptors);
+ }
+
+ public boolean isComplete() {
+ if (myDescriptors.isEmpty()) {
+ return false;
+ }
+
+ for (TestLocationDescriptor descriptor : myDescriptors) {
+ if (!descriptor.isComplete()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @NotNull
+ public List<String> generate(@NotNull final String testName) {
+ return generate(testName, null);
+ }
+
+ @NotNull
+ public List<String> generate(@NotNull final String testName, String root) {
+ List<String> result = new ArrayList<String>();
+ if (StringUtil.isEmpty(testName)) {
+ return result;
+ }
+ for (TestLocationDescriptor descriptor : myDescriptors) {
+ if (root != null && !root.equals(descriptor.dir)) continue;
+ result.add(String.format(
+ "%s/%s%c%s%s.%s",
+ descriptor.dir, descriptor.filePrefix,
+ descriptor.startWithLowerCase ? Character.toLowerCase(testName.charAt(0)) : Character.toUpperCase(testName.charAt(0)),
+ testName.substring(1), descriptor.fileSuffix, descriptor.ext
+ ));
+ }
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return myDescriptors.toString();
+ }
+ }
+}
diff --git a/plugins/devkit/src/testAssistant/TestDataHighlightingPass.java b/plugins/devkit/src/testAssistant/TestDataHighlightingPass.java
new file mode 100644
index 000000000000..fcdd14ebf032
--- /dev/null
+++ b/plugins/devkit/src/testAssistant/TestDataHighlightingPass.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2000-2013 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.testAssistant;
+
+import com.intellij.codeHighlighting.TextEditorHighlightingPass;
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.impl.DocumentMarkupModel;
+import com.intellij.openapi.editor.markup.*;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Key;
+import com.intellij.util.ui.PlatformColors;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+
+/**
+ * @author Konstantin Bulenkov
+ */
+public class TestDataHighlightingPass extends TextEditorHighlightingPass {
+ private static final Key<Object> KEY = Key.create("TestDataHighlighterKey");
+ private static final Object VALUE = new Object();
+
+ private static final GutterIconRenderer ICON_RENDERER = new MyGutterIconRenderer();
+
+ private static final TextAttributes CARET_ATTRIBUTES = new TextAttributes(PlatformColors.BLUE, null, null, null, Font.BOLD);
+ private static final String CARET = "<caret>";
+
+ protected TestDataHighlightingPass(@NotNull final Project project, @Nullable final Document document) {
+ super(project, document);
+ }
+
+ @Override
+ public void doCollectInformation(@NotNull ProgressIndicator progress) {
+ }
+
+ @Override
+ public void doApplyInformationToEditor() {
+ removeHighlighters();
+
+ if (myDocument == null) {
+ return;
+ }
+ final MarkupModel model = DocumentMarkupModel.forDocument(myDocument, myProject, true);
+ final String text = myDocument.getText();
+
+ if (text != null) {
+ int ind = -1;
+ while ((ind = text.indexOf(CARET, ind + 1)) >= 0) {
+ final RangeHighlighter highlighter = model.addRangeHighlighter(ind,
+ ind + CARET.length(),
+ HighlighterLayer.ADDITIONAL_SYNTAX,
+ CARET_ATTRIBUTES,
+ HighlighterTargetArea.EXACT_RANGE);
+ highlighter.setGutterIconRenderer(ICON_RENDERER);
+ highlighter.putUserData(KEY, VALUE);
+ }
+ }
+ }
+
+ private void removeHighlighters() {
+ if (myDocument == null) {
+ return;
+ }
+ final MarkupModel model = DocumentMarkupModel.forDocument(myDocument, myProject, true);
+ for (RangeHighlighter highlighter : model.getAllHighlighters()) {
+ if (highlighter.getUserData(KEY) == VALUE) {
+ highlighter.dispose();
+ }
+ }
+ }
+
+ private static class MyGutterIconRenderer extends GutterIconRenderer {
+ @NotNull
+ @Override
+ public Icon getIcon() {
+ return AllIcons.RunConfigurations.Junit;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof MyGutterIconRenderer;
+ }
+ @Override
+ public int hashCode() {
+ return getIcon().hashCode();
+ }
+ }
+}
diff --git a/plugins/devkit/src/testAssistant/TestDataHighlightingPassFactory.java b/plugins/devkit/src/testAssistant/TestDataHighlightingPassFactory.java
new file mode 100644
index 000000000000..a3284ffbdf93
--- /dev/null
+++ b/plugins/devkit/src/testAssistant/TestDataHighlightingPassFactory.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2000-2010 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.testAssistant;
+
+import com.intellij.codeHighlighting.TextEditorHighlightingPass;
+import com.intellij.codeHighlighting.TextEditorHighlightingPassFactory;
+import com.intellij.codeHighlighting.TextEditorHighlightingPassRegistrar;
+import com.intellij.openapi.components.AbstractProjectComponent;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.fileTypes.StdFileTypes;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiFile;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @author Konstantin Bulenkov
+ */
+public class TestDataHighlightingPassFactory extends AbstractProjectComponent implements TextEditorHighlightingPassFactory {
+ public static final List<String> SUPPORTED_FILE_TYPES = Arrays.asList(
+ StdFileTypes.JAVA.getDefaultExtension()
+ );
+ public static final List<String> SUPPORTED_IN_TEST_DATA_FILE_TYPES = Arrays.asList(
+ "js", "php", "css", "html", "xhtml", "jsp", "test", "py", "aj"
+ );
+ private static final int MAX_HOPES = 3;
+ private static final String TEST_DATA = "testdata";
+
+
+ public TestDataHighlightingPassFactory(Project project, TextEditorHighlightingPassRegistrar highlightingPassRegistrar) {
+ super(project);
+ highlightingPassRegistrar.registerTextEditorHighlightingPass(this, null, null, true, -1);
+ }
+
+ @NonNls
+ @NotNull
+ public String getComponentName() {
+ return getClass().getName();
+ }
+
+ @Nullable
+ public TextEditorHighlightingPass createHighlightingPass(@NotNull PsiFile file, @NotNull final Editor editor) {
+ final VirtualFile virtualFile = file.getVirtualFile();
+ if (virtualFile != null && isSupported(virtualFile)) {
+ return new TestDataHighlightingPass(myProject, PsiDocumentManager.getInstance(myProject).getDocument(file));
+ }
+ return null;
+ }
+
+ public boolean isSupported(@NotNull VirtualFile file) {
+ final String ext = file.getExtension();
+ if (SUPPORTED_FILE_TYPES.contains(ext)) {
+ return ProjectRootManager.getInstance(myProject).getFileIndex().getSourceRootForFile(file) == null;
+ }
+
+ if (SUPPORTED_IN_TEST_DATA_FILE_TYPES.contains(ext)) {
+ int i = 0;
+ VirtualFile parent = file.getParent();
+ while (parent != null && i < MAX_HOPES) {
+ if (parent.getName().toLowerCase().contains(TEST_DATA)) {
+ return true;
+ }
+ i++;
+ parent = parent.getParent();
+ }
+ }
+ return false;
+ }
+}
+
diff --git a/plugins/devkit/src/testAssistant/TestDataLineMarkerProvider.java b/plugins/devkit/src/testAssistant/TestDataLineMarkerProvider.java
new file mode 100644
index 000000000000..916e38c9f876
--- /dev/null
+++ b/plugins/devkit/src/testAssistant/TestDataLineMarkerProvider.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2000-2011 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.testAssistant;
+
+import com.intellij.codeHighlighting.Pass;
+import com.intellij.codeInsight.AnnotationUtil;
+import com.intellij.codeInsight.daemon.GutterIconNavigationHandler;
+import com.intellij.codeInsight.daemon.LineMarkerInfo;
+import com.intellij.codeInsight.daemon.LineMarkerProvider;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.editor.markup.GutterIconRenderer;
+import com.intellij.openapi.fileEditor.OpenFileDescriptor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ProjectFileIndex;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.*;
+import com.intellij.psi.util.PsiUtilCore;
+import com.intellij.util.PlatformIcons;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.awt.event.MouseEvent;
+import java.io.File;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author yole
+ */
+public class TestDataLineMarkerProvider implements LineMarkerProvider {
+ public static final String TEST_DATA_PATH_ANNOTATION_QUALIFIED_NAME = "com.intellij.testFramework.TestDataPath";
+ public static final String CONTENT_ROOT_VARIABLE = "$CONTENT_ROOT";
+ public static final String PROJECT_ROOT_VARIABLE = "$PROJECT_ROOT";
+
+ public LineMarkerInfo getLineMarkerInfo(@NotNull PsiElement element) {
+ if (ApplicationManager.getApplication().isUnitTestMode()) {
+ return null;
+ }
+ final VirtualFile file = PsiUtilCore.getVirtualFile(element);
+ if (file == null || !ProjectFileIndex.SERVICE.getInstance(element.getProject()).isInTestSourceContent(file)) {
+ return null;
+ }
+ if (element instanceof PsiMethod) {
+ final PsiMethod method = (PsiMethod)element;
+ if (isTestMethod(method)) {
+ return new LineMarkerInfo<PsiMethod>(
+ method, method.getModifierList().getTextRange(), PlatformIcons.TEST_SOURCE_FOLDER, Pass.UPDATE_ALL, null, new TestDataNavigationHandler(),
+ GutterIconRenderer.Alignment.LEFT);
+ }
+ } else if (element instanceof PsiClass) {
+ final PsiClass psiClass = (PsiClass)element;
+ final String basePath = getTestDataBasePath(psiClass);
+ if (basePath != null) {
+ return new LineMarkerInfo<PsiClass>(
+ psiClass, psiClass.getModifierList().getTextRange(), PlatformIcons.TEST_SOURCE_FOLDER, Pass.UPDATE_ALL, null, new GutterIconNavigationHandler<PsiClass>() {
+ @Override
+ public void navigate(MouseEvent e, PsiClass elt) {
+ final VirtualFile baseDir = VfsUtil.findFileByIoFile(new File(basePath), true);
+ if (baseDir != null) {
+ new OpenFileDescriptor(psiClass.getProject(), baseDir).navigate(true);
+ }
+ }
+ },
+ GutterIconRenderer.Alignment.LEFT);
+ }
+ }
+ return null;
+ }
+
+ private static boolean isTestMethod(@NotNull PsiMethod method) {
+ if (isTestMethodWithAnnotation(method)) {
+ return true;
+ }
+
+ final List<String> files = TestDataGuessByExistingFilesUtil.collectTestDataByExistingFiles(method);
+ return files != null && !files.isEmpty();
+ }
+
+ private static boolean isTestMethodWithAnnotation(@NotNull PsiMethod method) {
+ String name = method.getName();
+ if (!name.startsWith("test")) {
+ return false;
+ }
+ String testDataPath = getTestDataBasePath(method.getContainingClass());
+ if (testDataPath == null) {
+ return false;
+ }
+ List<String> fileNames = new TestDataReferenceCollector(testDataPath, name.substring(4)).collectTestDataReferences(method);
+ return fileNames != null && !fileNames.isEmpty();
+ }
+
+ public void collectSlowLineMarkers(@NotNull List<PsiElement> elements, @NotNull Collection<LineMarkerInfo> result) {
+ }
+
+ @Nullable
+ public static String getTestDataBasePath(PsiClass psiClass) {
+ final PsiAnnotation annotation = AnnotationUtil.findAnnotationInHierarchy(psiClass,
+ Collections.singleton(TEST_DATA_PATH_ANNOTATION_QUALIFIED_NAME));
+ if (annotation != null) {
+ final PsiAnnotationMemberValue value = annotation.findAttributeValue(PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME);
+ if (value instanceof PsiExpression) {
+ final Project project = value.getProject();
+ final PsiConstantEvaluationHelper evaluationHelper = JavaPsiFacade.getInstance(project).getConstantEvaluationHelper();
+ final Object constantValue = evaluationHelper.computeConstantExpression(value, false);
+ if (constantValue instanceof String) {
+ String path = (String) constantValue;
+ if (path.contains(CONTENT_ROOT_VARIABLE)) {
+ final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex();
+ final VirtualFile file = psiClass.getContainingFile().getVirtualFile();
+ if (file == null) {
+ return null;
+ }
+ final VirtualFile contentRoot = fileIndex.getContentRootForFile(file);
+ if (contentRoot == null) return null;
+ path = path.replace(CONTENT_ROOT_VARIABLE, contentRoot.getPath());
+ }
+ if (path.contains(PROJECT_ROOT_VARIABLE)) {
+ final VirtualFile baseDir = project.getBaseDir();
+ if (baseDir == null) {
+ return null;
+ }
+ path = path.replace(PROJECT_ROOT_VARIABLE, baseDir.getPath());
+ }
+ return path;
+ }
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/plugins/devkit/src/testAssistant/TestDataNavigationHandler.java b/plugins/devkit/src/testAssistant/TestDataNavigationHandler.java
new file mode 100644
index 000000000000..143ce0311d16
--- /dev/null
+++ b/plugins/devkit/src/testAssistant/TestDataNavigationHandler.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2000-2013 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.testAssistant;
+
+import com.intellij.codeInsight.daemon.GutterIconNavigationHandler;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.fileEditor.OpenFileDescriptor;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.fileTypes.FileTypeManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.ui.popup.PopupChooserBuilder;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiMethod;
+import com.intellij.ui.ColoredListCellRenderer;
+import com.intellij.ui.awt.RelativePoint;
+import com.intellij.ui.components.JBList;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.PathUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.event.MouseEvent;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+* @author yole
+*/
+public class TestDataNavigationHandler implements GutterIconNavigationHandler<PsiMethod> {
+ public void navigate(MouseEvent e, final PsiMethod elt) {
+ navigate(elt, new RelativePoint(e));
+ }
+
+ public static void navigate(PsiMethod method, final RelativePoint point) {
+ List<String> fileNames = null;
+ String testDataPath = TestDataLineMarkerProvider.getTestDataBasePath(method.getContainingClass());
+ if (testDataPath != null) {
+ fileNames = new TestDataReferenceCollector(testDataPath, method.getName().substring(4)).collectTestDataReferences(method);
+ }
+
+ if (fileNames == null || fileNames.isEmpty()) {
+ fileNames = TestDataGuessByExistingFilesUtil.collectTestDataByExistingFiles(method);
+ }
+
+ if (fileNames == null || fileNames.isEmpty()) {
+ return;
+ }
+ navigate(point, fileNames, method.getProject());
+ }
+
+ public static void navigate(@NotNull final RelativePoint point,
+ @NotNull List<String> testDataFiles,
+ final Project project) {
+ if (testDataFiles.size() == 1) {
+ openFileByIndex(project, testDataFiles, 0);
+ }
+ else if (testDataFiles.size() > 1) {
+ TestDataGroupVirtualFile groupFile = getTestDataGroup(testDataFiles);
+ if (groupFile != null) {
+ new OpenFileDescriptor(project, groupFile).navigate(true);
+ }
+ else {
+ showNavigationPopup(project, testDataFiles, point);
+ }
+ }
+ }
+
+ @Nullable
+ private static TestDataGroupVirtualFile getTestDataGroup(List<String> fileNames) {
+ if (fileNames.size() != 2) {
+ return null;
+ }
+ VirtualFile file1 = LocalFileSystem.getInstance().refreshAndFindFileByPath(fileNames.get(0));
+ VirtualFile file2 = LocalFileSystem.getInstance().refreshAndFindFileByPath(fileNames.get(1));
+ if (file1 == null || file2 == null) {
+ return null;
+ }
+ final int commonPrefixLength = StringUtil.commonPrefixLength(file1.getName(), file2.getName());
+ if (file1.getName().substring(commonPrefixLength).toLowerCase().contains("after")) {
+ return new TestDataGroupVirtualFile(file2, file1);
+ }
+ if (file2.getName().substring(commonPrefixLength).toLowerCase().contains("after")) {
+ return new TestDataGroupVirtualFile(file1, file2);
+ }
+ return null;
+ }
+
+ private static void showNavigationPopup(final Project project, final List<String> fileNames, final RelativePoint point) {
+ List<String> listPaths = new ArrayList<String>(fileNames);
+ final String CREATE_MISSING_OPTION = "Create Missing Files";
+ if (fileNames.size() == 2) {
+ VirtualFile file1 = LocalFileSystem.getInstance().refreshAndFindFileByPath(fileNames.get(0));
+ VirtualFile file2 = LocalFileSystem.getInstance().refreshAndFindFileByPath(fileNames.get(1));
+ if (file1 == null || file2 == null) {
+ listPaths.add(CREATE_MISSING_OPTION);
+ }
+ }
+ final JList list = new JBList(ArrayUtil.toStringArray(listPaths));
+ list.setCellRenderer(new ColoredListCellRenderer() {
+ @Override
+ protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) {
+ String path = (String)value;
+ String fileName = PathUtil.getFileName(path);
+ if (!fileName.equals(CREATE_MISSING_OPTION)) {
+ final FileType fileType = FileTypeManager.getInstance().getFileTypeByFileName(fileName);
+ setIcon(fileType.getIcon());
+ }
+ append(String.format("%s (%s)", fileName, PathUtil.getParentPath(path)));
+ }
+ });
+ PopupChooserBuilder builder = new PopupChooserBuilder(list);
+ builder.setItemChoosenCallback(new Runnable() {
+ public void run() {
+ final int[] indices = list.getSelectedIndices();
+ if (ArrayUtil.indexOf(indices, fileNames.size()) >= 0) {
+ createMissingFiles(project, fileNames);
+ }
+ else {
+ for (int index : indices) {
+ openFileByIndex(project, fileNames, index);
+ }
+ }
+ }
+ }).createPopup().show(point);
+ }
+
+ private static void createMissingFiles(Project project, List<String> fileNames) {
+ for (String name : fileNames) {
+ if (LocalFileSystem.getInstance().refreshAndFindFileByPath(name) == null) {
+ createFileByName(project, name);
+ }
+ }
+ final TestDataGroupVirtualFile testDataGroup = getTestDataGroup(fileNames);
+ if (testDataGroup != null) {
+ new OpenFileDescriptor(project, testDataGroup).navigate(true);
+ }
+ }
+
+ private static void openFileByIndex(final Project project, final List<String> fileNames, final int index) {
+ final String path = fileNames.get(index);
+ final VirtualFile file = LocalFileSystem.getInstance().refreshAndFindFileByPath(path);
+ if (file != null) {
+ new OpenFileDescriptor(project, file).navigate(true);
+ }
+ else {
+ int rc = Messages.showYesNoDialog(project, "The referenced testdata file " + path + " does not exist. Would you like to create it?",
+ "Create Testdata File", Messages.getQuestionIcon());
+ if (rc == Messages.YES) {
+ VirtualFile vFile = createFileByName(project, path);
+ new OpenFileDescriptor(project, vFile).navigate(true);
+ }
+ }
+ }
+
+ private static VirtualFile createFileByName(final Project project, final String path) {
+ return ApplicationManager.getApplication().runWriteAction(new Computable<VirtualFile>() {
+ public VirtualFile compute() {
+ try {
+ final File file = new File(path);
+ final VirtualFile parent = VfsUtil.createDirectories(file.getParent());
+ return parent.createChildData(this, file.getName());
+ }
+ catch (IOException e) {
+ Messages.showErrorDialog(project, e.getMessage(), "Create Testdata File");
+ return null;
+ }
+ }
+ });
+ }
+}
diff --git a/plugins/devkit/src/testAssistant/TestDataReferenceCollector.java b/plugins/devkit/src/testAssistant/TestDataReferenceCollector.java
new file mode 100644
index 000000000000..85ce75c4cfb1
--- /dev/null
+++ b/plugins/devkit/src/testAssistant/TestDataReferenceCollector.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2000-2013 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.testAssistant;
+
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.NullableComputable;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.*;
+import com.intellij.testFramework.UsefulTestCase;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.util.*;
+
+/**
+ * @author yole
+ */
+public class TestDataReferenceCollector {
+ private static final String TEST_DATA_FILE_ANNOTATION_QUALIFIED_NAME = "com.intellij.testFramework.TestDataFile";
+ private final String myTestDataPath;
+ private final String myTestName;
+ private final List<String> myLogMessages = new ArrayList<String>();
+ private PsiClass myContainingClass;
+ private boolean myFoundTestDataParameters = false;
+
+ public TestDataReferenceCollector(@Nullable String testDataPath, String testName) {
+ if (StringUtil.isNotEmpty(testDataPath) && !StringUtil.endsWithChar(testDataPath, File.separatorChar)) {
+ testDataPath += File.separatorChar;
+ }
+ myTestDataPath = testDataPath;
+ myTestName = testName;
+ }
+
+ @Nullable
+ List<String> collectTestDataReferences(@NotNull final PsiMethod method) {
+ myContainingClass = method.getContainingClass();
+ List<String> result = collectTestDataReferences(method, new HashMap<String, Computable<String>>());
+ if (!myFoundTestDataParameters) {
+ myLogMessages.add("Found no parameters annotated with @TestDataFile");
+ }
+
+ if (result == null || result.isEmpty()) {
+ result = TestDataGuessByExistingFilesUtil.collectTestDataByExistingFiles(method);
+ }
+ return result;
+ }
+
+ private List<String> collectTestDataReferences(final PsiMethod method, final Map<String, Computable<String>> argumentMap) {
+ final List<String> result = new ArrayList<String>();
+ if (myTestDataPath == null) {
+ return result;
+ }
+ method.accept(new JavaRecursiveElementVisitor() {
+ @Override
+ public void visitMethodCallExpression(PsiMethodCallExpression expression) {
+ String callText = expression.getMethodExpression().getReferenceName();
+ if (callText == null) return;
+ PsiMethod callee = expression.resolveMethod();
+ if (callee != null && callee.hasModifierProperty(PsiModifier.ABSTRACT)) {
+ final PsiClass calleeContainingClass = callee.getContainingClass();
+ if (calleeContainingClass != null && myContainingClass.isInheritor(calleeContainingClass, true)) {
+ final PsiMethod implementation = myContainingClass.findMethodBySignature(callee, true);
+ if (implementation != null) {
+ callee = implementation;
+ }
+ }
+ }
+ if (callee != null) {
+ boolean haveAnnotatedParameters = false;
+ final PsiParameter[] psiParameters = callee.getParameterList().getParameters();
+ for (int i = 0, psiParametersLength = psiParameters.length; i < psiParametersLength; i++) {
+ PsiParameter psiParameter = psiParameters[i];
+ final PsiModifierList modifierList = psiParameter.getModifierList();
+ if (modifierList != null && modifierList.findAnnotation(TEST_DATA_FILE_ANNOTATION_QUALIFIED_NAME) != null) {
+ myFoundTestDataParameters = true;
+ processCallArgument(expression, argumentMap, result, i);
+ haveAnnotatedParameters = true;
+ }
+ }
+ if (expression.getMethodExpression().getQualifierExpression() == null && !haveAnnotatedParameters) {
+ result.addAll(collectTestDataReferences(callee, buildArgumentMap(expression, callee)));
+ }
+ }
+ }
+ });
+ return result;
+ }
+
+ private void processCallArgument(PsiMethodCallExpression expression, Map<String, Computable<String>> argumentMap, List<String> result, final int index) {
+ final PsiExpression[] arguments = expression.getArgumentList().getExpressions();
+ if (arguments.length > index) {
+ String testDataFile = evaluate(arguments [index], argumentMap);
+ if (testDataFile != null) {
+ result.add(myTestDataPath + testDataFile);
+ }
+ }
+ }
+
+ private Map<String, Computable<String>> buildArgumentMap(PsiMethodCallExpression expression, PsiMethod method) {
+ Map<String, Computable<String>> result = new HashMap<String, Computable<String>>();
+ final PsiParameter[] parameters = method.getParameterList().getParameters();
+ final PsiExpression[] arguments = expression.getArgumentList().getExpressions();
+ for (int i = 0; i < arguments.length && i < parameters.length; i++) {
+ final int finalI = i;
+ result.put(parameters [i].getName(), new NullableComputable<String>() {
+ public String compute() {
+ return evaluate(arguments [finalI], Collections.<String, Computable<String>>emptyMap());
+ }
+ });
+ }
+ return result;
+ }
+
+ @Nullable
+ private String evaluate(PsiExpression expression, Map<String, Computable<String>> arguments) {
+ if (expression instanceof PsiPolyadicExpression) {
+ PsiPolyadicExpression binaryExpression = (PsiPolyadicExpression)expression;
+ if (binaryExpression.getOperationTokenType() == JavaTokenType.PLUS) {
+ String r = "";
+ for (PsiExpression op : binaryExpression.getOperands()) {
+ String lhs = evaluate(op, arguments);
+ if (lhs == null) return null;
+ r += lhs;
+ }
+ return r;
+ }
+ }
+ else if (expression instanceof PsiLiteralExpression) {
+ final Object value = ((PsiLiteralExpression)expression).getValue();
+ if (value instanceof String) {
+ return (String) value;
+ }
+ }
+ else if (expression instanceof PsiReferenceExpression) {
+ final PsiElement result = ((PsiReferenceExpression)expression).resolve();
+ if (result instanceof PsiParameter) {
+ final String name = ((PsiParameter)result).getName();
+ final Computable<String> arg = arguments.get(name);
+ return arg == null ? null : arg.compute();
+ }
+ if (result instanceof PsiVariable) {
+ final PsiExpression initializer = ((PsiVariable)result).getInitializer();
+ if (initializer != null) {
+ return evaluate(initializer, arguments);
+ }
+ }
+ }
+ else if (expression instanceof PsiMethodCallExpression) {
+ final PsiMethodCallExpression methodCall = (PsiMethodCallExpression)expression;
+ final String callText = methodCall.getMethodExpression().getText();
+ if (callText.equals("getTestName")) {
+ final PsiExpression[] psiExpressions = methodCall.getArgumentList().getExpressions();
+ if (psiExpressions.length == 1) {
+ if ("true".equals(psiExpressions[0].getText()) && !StringUtil.isEmpty(myTestName)) {
+ return UsefulTestCase.lowercaseFirstLetter(myTestName, true);
+ }
+ return myTestName;
+ }
+ }
+ }
+ if (expression != null) {
+ myLogMessages.add("Failed to evaluate " + expression.getText());
+ }
+ return null;
+ }
+
+ public String getLog() {
+ return StringUtil.join(myLogMessages, "\n");
+ }
+}
diff --git a/plugins/devkit/src/testAssistant/TestDataReferenceContributor.java b/plugins/devkit/src/testAssistant/TestDataReferenceContributor.java
new file mode 100644
index 000000000000..de3d4a446bb1
--- /dev/null
+++ b/plugins/devkit/src/testAssistant/TestDataReferenceContributor.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.testAssistant;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ProjectFileIndex;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.util.Condition;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.newvfs.ManagingFS;
+import com.intellij.psi.*;
+import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileInfoManager;
+import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReference;
+import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReferenceSet;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.ProcessingContext;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+
+import static com.intellij.patterns.PsiJavaPatterns.literalExpression;
+import static org.jetbrains.idea.devkit.testAssistant.TestDataLineMarkerProvider.*;
+
+/**
+ * @author zolotov
+ * @since 9/20/13
+ */
+public class TestDataReferenceContributor extends PsiReferenceContributor {
+ @Override
+ public void registerReferenceProviders(PsiReferenceRegistrar registrar) {
+ registrar.registerReferenceProvider(literalExpression().annotationParam(TEST_DATA_PATH_ANNOTATION_QUALIFIED_NAME),
+ new TestDataReferenceProvider());
+ }
+
+ private static class TestDataReferenceProvider extends PsiReferenceProvider {
+ @NotNull
+ @Override
+ public PsiReference[] getReferencesByElement(@NotNull final PsiElement element, @NotNull final ProcessingContext context) {
+ final TestDataReferenceSet referenceSet = new TestDataReferenceSet(element);
+ referenceSet.addCustomization(FileReferenceSet.DEFAULT_PATH_EVALUATOR_OPTION, FileReferenceSet.ABSOLUTE_TOP_LEVEL);
+ return referenceSet.getAllReferences();
+ }
+ }
+
+ private static class TestDataReferenceSet extends FileReferenceSet {
+ public TestDataReferenceSet(@NotNull PsiElement element) {
+ super(element);
+ }
+
+ @Override
+ public boolean isEmptyPathAllowed() {
+ return false;
+ }
+
+ @Override
+ public boolean isAbsolutePathReference() {
+ return super.isAbsolutePathReference() || StringUtil.startsWithChar(getPathString(), '$');
+ }
+
+ @Override
+ public FileReference createFileReference(TextRange range, int index, String text) {
+ return new TestDataReference(this, range, index, text);
+ }
+
+ @NotNull
+ @Override
+ public Collection<PsiFileSystemItem> computeDefaultContexts() {
+ return toFileSystemItems(ManagingFS.getInstance().getLocalRoots());
+ }
+
+ @Override
+ protected Condition<PsiFileSystemItem> getReferenceCompletionFilter() {
+ return DIRECTORY_FILTER;
+ }
+ }
+
+ public static class TestDataReference extends FileReference {
+ public TestDataReference(@NotNull FileReferenceSet fileReferenceSet, TextRange range, int index, String text) {
+ super(fileReferenceSet, range, index, text);
+ }
+
+ @NotNull
+ @Override
+ public Object[] getVariants() {
+ if (getIndex() == 0 && !StringUtil.startsWithChar(getFileReferenceSet().getPathString(), '/')) {
+ Collection<Object> variants = ContainerUtil.newHashSet(super.getVariants());
+ final PsiDirectory projectPsiRoot = getProjectPsiRoot();
+ if (projectPsiRoot != null) {
+ variants.add(FileInfoManager.getFileLookupItem(projectPsiRoot, PROJECT_ROOT_VARIABLE, projectPsiRoot.getIcon(0))
+ .withTypeText(projectPsiRoot.getVirtualFile().getPath(), true));
+ }
+
+ final PsiDirectory contentPsiRoot = getContentPsiRoot();
+ if (contentPsiRoot != null) {
+ variants.add(FileInfoManager.getFileLookupItem(contentPsiRoot, CONTENT_ROOT_VARIABLE, contentPsiRoot.getIcon(0))
+ .withTypeText(contentPsiRoot.getVirtualFile().getPath(), true));
+ }
+ return ArrayUtil.toObjectArray(variants);
+ }
+
+ return super.getVariants();
+ }
+
+ @NotNull
+ @Override
+ protected ResolveResult[] innerResolve(boolean caseSensitive, @NotNull PsiFile containingFile) {
+ if (getIndex() == 0 && StringUtil.startsWithChar(getText(), '$')) {
+ if (PROJECT_ROOT_VARIABLE.equals(getText())) {
+ final PsiDirectory projectPsiRoot = getProjectPsiRoot();
+ if (projectPsiRoot != null) {
+ return new ResolveResult[]{new PsiElementResolveResult(projectPsiRoot)};
+ }
+ }
+ else if (CONTENT_ROOT_VARIABLE.equals(getText())) {
+ final PsiDirectory contentPsiRoot = getContentPsiRoot();
+ if (contentPsiRoot != null) {
+ return new ResolveResult[]{new PsiElementResolveResult(contentPsiRoot)};
+ }
+ }
+ }
+ return super.innerResolve(caseSensitive, containingFile);
+ }
+
+ @Nullable
+ private PsiDirectory getProjectPsiRoot() {
+ final Project project = getElement().getProject();
+ final VirtualFile projectDir = project.getBaseDir();
+ if (projectDir != null) {
+ final PsiManager psiManager = PsiManager.getInstance(project);
+ return psiManager.findDirectory(projectDir);
+ }
+ return null;
+ }
+
+ @Nullable
+ private PsiDirectory getContentPsiRoot() {
+ final Project project = getElement().getProject();
+ final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex();
+ final VirtualFile file = getElement().getContainingFile().getOriginalFile().getVirtualFile();
+ if (file != null) {
+ final VirtualFile contentRoot = fileIndex.getContentRootForFile(file);
+ if (contentRoot != null) {
+ final PsiManager psiManager = PsiManager.getInstance(project);
+ return psiManager.findDirectory(contentRoot);
+ }
+ }
+ return null;
+ }
+ }
+}
diff --git a/plugins/devkit/src/testAssistant/TestDataRelatedItem.java b/plugins/devkit/src/testAssistant/TestDataRelatedItem.java
new file mode 100644
index 000000000000..5278c4fa6036
--- /dev/null
+++ b/plugins/devkit/src/testAssistant/TestDataRelatedItem.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.testAssistant;
+
+import com.intellij.navigation.GotoRelatedItem;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.ui.popup.JBPopupFactory;
+import com.intellij.psi.PsiMethod;
+import com.intellij.util.PathUtil;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author Denis Zhdanov
+ * @since 5/24/11 4:59 PM
+ */
+public class TestDataRelatedItem extends GotoRelatedItem{
+
+ private final List<String> myTestDataFiles = new ArrayList<String>();
+ private final Editor myEditor;
+ private final PsiMethod myMethod;
+
+ public TestDataRelatedItem(@NotNull PsiMethod method, @NotNull Editor editor, @NotNull Collection<String> testDataFiles) {
+ super(method, "Test Data");
+ myMethod = method;
+ myEditor = editor;
+ myTestDataFiles.addAll(testDataFiles);
+ }
+
+ @Override
+ public String getCustomName() {
+ if (myTestDataFiles.size() != 1) {
+ return "test data";
+ }
+ return PathUtil.getFileName(myTestDataFiles.get(0));
+ }
+
+ @Override
+ public void navigate() {
+ TestDataNavigationHandler.navigate(JBPopupFactory.getInstance().guessBestPopupLocation(myEditor), myTestDataFiles,
+ myMethod.getProject());
+ }
+}
diff --git a/plugins/devkit/src/testAssistant/TestLocationDataRule.java b/plugins/devkit/src/testAssistant/TestLocationDataRule.java
new file mode 100644
index 000000000000..6dccd0f808da
--- /dev/null
+++ b/plugins/devkit/src/testAssistant/TestLocationDataRule.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.testAssistant;
+
+import com.intellij.codeInsight.AnnotationUtil;
+import com.intellij.execution.Location;
+import com.intellij.execution.PsiLocation;
+import com.intellij.execution.junit2.PsiMemberParameterizedLocation;
+import com.intellij.execution.junit2.info.MethodLocation;
+import com.intellij.ide.impl.dataRules.GetDataRule;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.actionSystem.DataProvider;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ProjectFileIndex;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.*;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.search.GlobalSearchScopesCore;
+import com.intellij.psi.search.PsiSearchHelper;
+import com.intellij.testFramework.Parameterized;
+import com.intellij.testFramework.TestDataPath;
+import com.intellij.util.CommonProcessors;
+import com.intellij.util.containers.ContainerUtil;
+import gnu.trove.THashSet;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.*;
+
+public class TestLocationDataRule implements GetDataRule {
+ @Nullable
+ @Override
+ public Object getData(DataProvider dataProvider) {
+ final Project project = CommonDataKeys.PROJECT.getData(dataProvider);
+ final VirtualFile file = CommonDataKeys.VIRTUAL_FILE.getData(dataProvider);
+ if (project != null && file != null) {
+ final List<Location> locations = collectRelativeLocations(project, file);
+ return locations.size() == 1 ? locations.get(0) : null;
+ }
+ return null;
+ }
+
+ @NotNull
+ protected static List<Location> collectRelativeLocations(Project project, VirtualFile file) {
+ final List<Location> locations = new ArrayList<Location>();
+ final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex();
+ if (fileIndex.isInContent(file) && !fileIndex.isInSource(file) && !fileIndex.isInLibraryClasses(file)) {
+ final VirtualFile parent = file.getParent();
+ final VirtualFile contentRoot = fileIndex.getContentRootForFile(file);
+ if (contentRoot != null) {
+ final String relativePath = VfsUtilCore.getRelativePath(parent, contentRoot, '/');
+ if (relativePath != null) {
+ final PsiSearchHelper searchHelper = PsiSearchHelper.SERVICE.getInstance(project);
+ final List<String> words = StringUtil.getWordsIn(relativePath);
+ // put longer strings first
+ Collections.sort(words, new Comparator<String>() {
+ @Override
+ public int compare(final String o1, final String o2) {
+ return o2.length() - o1.length();
+ }
+ });
+
+ final GlobalSearchScope testScope = GlobalSearchScopesCore.projectTestScope(project);
+ Set<PsiFile> resultFiles = null;
+ for (String word : words) {
+ if (word.length() < 5) {
+ continue;
+ }
+ final Set<PsiFile> files = new THashSet<PsiFile>();
+ searchHelper.processAllFilesWithWordInLiterals(word, testScope, new CommonProcessors.CollectProcessor<PsiFile>(files));
+ if (resultFiles == null) {
+ resultFiles = files;
+ }
+ else {
+ resultFiles.retainAll(files);
+ }
+ if (resultFiles.isEmpty()) break;
+ }
+ if (resultFiles != null) {
+ for (Iterator<PsiFile> iterator = resultFiles.iterator(); iterator.hasNext(); ) {
+ if (!VfsUtilCore.isAncestor(contentRoot, iterator.next().getVirtualFile(), true)) {
+ iterator.remove();
+ }
+ }
+
+ final String fileName = file.getName();
+ final String nameWithoutExtension = file.getNameWithoutExtension();
+
+
+ for (PsiFile resultFile : resultFiles) {
+ if (resultFile instanceof PsiClassOwner) {
+ final PsiClass[] classes = ((PsiClassOwner)resultFile).getClasses();
+ if (classes.length > 0) {
+ ContainerUtil.addIfNotNull(locations, getLocation(project, fileName, nameWithoutExtension, classes[0]));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return locations;
+ }
+
+ @Nullable
+ private static Location getLocation(Project project,
+ String fileName,
+ String nameWithoutExtension,
+ PsiClass aClass) {
+ final PsiAnnotation annotation = AnnotationUtil.findAnnotation(aClass, TestDataPath.class.getName());
+ if (annotation != null) {
+ final Location parameterizedLocation =
+ PsiMemberParameterizedLocation.getParameterizedLocation(aClass, "[" + fileName + "]", Parameterized.class.getName());
+ if (parameterizedLocation != null) {
+ return parameterizedLocation;
+ }
+ if (StringUtil.isJavaIdentifier(nameWithoutExtension)) {
+ final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project);
+ PsiMethod method = aClass.findMethodBySignature(elementFactory.createMethod("test" + nameWithoutExtension, PsiType.VOID), true);
+ if (method != null) {
+ return MethodLocation.elementInClass(method, aClass);
+ }
+
+ method = aClass.findMethodBySignature(elementFactory.createMethod("test" + StringUtil.capitalize(nameWithoutExtension), PsiType.VOID), true);
+ if (method != null) {
+ return MethodLocation.elementInClass(method, aClass);
+ }
+ }
+ return new PsiLocation<PsiElement>(project, aClass);
+ }
+ return null;
+ }
+}
diff --git a/plugins/devkit/src/util/DescriptorUtil.java b/plugins/devkit/src/util/DescriptorUtil.java
index 3a773e9e5fa9..3cf574f9ca48 100644
--- a/plugins/devkit/src/util/DescriptorUtil.java
+++ b/plugins/devkit/src/util/DescriptorUtil.java
@@ -87,7 +87,7 @@ public class DescriptorUtil {
return getIdeaPlugin((XmlFile)file) != null;
}
- private static DomFileElement<IdeaPlugin> getIdeaPlugin(XmlFile file) {
+ public static DomFileElement<IdeaPlugin> getIdeaPlugin(XmlFile file) {
return DomManager.getDomManager(file.getProject()).getFileElement(file, IdeaPlugin.class);
}
}
diff --git a/plugins/devkit/src/inspections/quickfix/ExtensionPointCandidate.java b/plugins/devkit/src/util/ExtensionPointCandidate.java
index 5bedaf3281da..3ae694a0e7d0 100644
--- a/plugins/devkit/src/inspections/quickfix/ExtensionPointCandidate.java
+++ b/plugins/devkit/src/util/ExtensionPointCandidate.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2013 JetBrains s.r.o.
+ * Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,25 +13,35 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.jetbrains.idea.devkit.inspections.quickfix;
+package org.jetbrains.idea.devkit.util;
+
+import com.intellij.psi.SmartPsiElementPointer;
/**
-* @author yole
-*/
-class ExtensionPointCandidate {
+ * @author yole
+ */
+public class ExtensionPointCandidate {
+ public final SmartPsiElementPointer pointer;
public final String epName;
public final String attributeName;
public final String tagName;
public final String beanClassName;
- ExtensionPointCandidate(String epName, String attributeName, String tagName, String beanClassName) {
+ public ExtensionPointCandidate(SmartPsiElementPointer pointer,
+ String epName,
+ String attributeName,
+ String tagName,
+ String beanClassName) {
+ this.pointer = pointer;
this.epName = epName;
this.attributeName = attributeName;
this.tagName = tagName;
this.beanClassName = beanClassName;
}
- ExtensionPointCandidate(String epName) {
+ public ExtensionPointCandidate(SmartPsiElementPointer pointer,
+ String epName) {
+ this.pointer = pointer;
this.epName = epName;
this.attributeName = "implementation";
this.tagName = null;
diff --git a/plugins/devkit/src/util/ExtensionPointLocator.java b/plugins/devkit/src/util/ExtensionPointLocator.java
new file mode 100644
index 000000000000..c4a5b19c156a
--- /dev/null
+++ b/plugins/devkit/src/util/ExtensionPointLocator.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.util;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.*;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.search.PsiNonJavaFileReferenceProcessor;
+import com.intellij.psi.search.PsiSearchHelper;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.psi.xml.XmlTag;
+import com.intellij.util.SmartList;
+import com.intellij.util.xml.DomElement;
+import com.intellij.util.xml.DomService;
+import com.intellij.util.xml.DomUtil;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.devkit.dom.ExtensionPoint;
+import org.jetbrains.idea.devkit.dom.IdeaPlugin;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+
+public class ExtensionPointLocator {
+
+ private final PsiClass myPsiClass;
+
+ public ExtensionPointLocator(PsiClass psiClass) {
+ myPsiClass = psiClass;
+ }
+
+ public List<ExtensionPointCandidate> findDirectCandidates() {
+ final List<ExtensionPointCandidate> candidates = new SmartList<ExtensionPointCandidate>();
+ findExtensionPointCandidates(myPsiClass, candidates);
+ return candidates;
+ }
+
+ public List<ExtensionPointCandidate> findSuperCandidates() {
+ final List<ExtensionPointCandidate> candidates = new SmartList<ExtensionPointCandidate>();
+ findExtensionPointCandidatesInHierarchy(myPsiClass, candidates, new HashSet<PsiClass>());
+ return candidates;
+ }
+
+ private static void findExtensionPointCandidatesInHierarchy(PsiClass psiClass,
+ List<ExtensionPointCandidate> list,
+ HashSet<PsiClass> processed) {
+ for (PsiClass superClass : psiClass.getSupers()) {
+ if (!processed.add(superClass) ||
+ CommonClassNames.JAVA_LANG_OBJECT.equals(superClass.getQualifiedName())) {
+ continue;
+ }
+ findExtensionPointCandidates(superClass, list);
+ findExtensionPointCandidatesInHierarchy(superClass, list, processed);
+ }
+ }
+
+ private static void findExtensionPointCandidates(PsiClass psiClass, final List<ExtensionPointCandidate> list) {
+ String name = psiClass.getQualifiedName();
+ if (name == null) {
+ return;
+ }
+
+ final Project project = psiClass.getProject();
+ final Collection<VirtualFile> candidates = DomService.getInstance().getDomFileCandidates(IdeaPlugin.class, project);
+ GlobalSearchScope scope = GlobalSearchScope.filesScope(project, candidates);
+ PsiSearchHelper.SERVICE.getInstance(project).processUsagesInNonJavaFiles(name, new PsiNonJavaFileReferenceProcessor() {
+ @Override
+ public boolean process(PsiFile file, int startOffset, int endOffset) {
+ PsiElement element = file.findElementAt(startOffset);
+ processExtensionPointCandidate(element, list);
+ return true;
+ }
+ }, scope);
+ }
+
+ private static void processExtensionPointCandidate(PsiElement element, List<ExtensionPointCandidate> list) {
+ XmlTag tag = PsiTreeUtil.getParentOfType(element, XmlTag.class);
+ if (tag == null) return;
+ if ("extensionPoint".equals(tag.getName())) {
+ String epName = getEPName(tag);
+ if (epName != null) {
+ list.add(new ExtensionPointCandidate(createPointer(tag), epName));
+ }
+ }
+ else if ("with".equals(tag.getName())) {
+ XmlTag extensionPointTag = tag.getParentTag();
+ if (extensionPointTag == null) return;
+ if (!"extensionPoint".equals(extensionPointTag.getName())) return;
+ String attrName = tag.getAttributeValue("attribute");
+ String tagName = tag.getAttributeValue("tag");
+ String epName = getEPName(extensionPointTag);
+ String beanClassName = extensionPointTag.getAttributeValue("beanClass");
+ if ((attrName == null && tagName == null) || epName == null) return;
+ list.add(new ExtensionPointCandidate(createPointer(extensionPointTag), epName, attrName, tagName, beanClassName));
+ }
+ }
+
+ private static SmartPsiElementPointer createPointer(XmlTag extensionPointTag) {
+ return SmartPointerManager.getInstance(extensionPointTag.getProject()).createSmartPsiElementPointer(extensionPointTag);
+ }
+
+ @Nullable
+ private static String getEPName(XmlTag tag) {
+ final DomElement domElement = DomUtil.getDomElement(tag);
+ if (!(domElement instanceof ExtensionPoint)) return null;
+ return ((ExtensionPoint)domElement).getEffectiveQualifiedName();
+ }
+}
diff --git a/plugins/devkit/src/util/PsiUtil.java b/plugins/devkit/src/util/PsiUtil.java
index 58b6e8c065dc..8d093acb8017 100644
--- a/plugins/devkit/src/util/PsiUtil.java
+++ b/plugins/devkit/src/util/PsiUtil.java
@@ -31,9 +31,11 @@ import org.jetbrains.annotations.Nullable;
public class PsiUtil {
private static final Key<Boolean> IDEA_PROJECT = Key.create("idea.internal.inspections.enabled");
private static final String IDE_PROJECT_MARKER_CLASS = JBList.class.getName();
- public static final PsiElementVisitor EMPTY_VISITOR = new PsiElementVisitor() { };
+ public static final PsiElementVisitor EMPTY_VISITOR = new PsiElementVisitor() {
+ };
- private PsiUtil() { }
+ private PsiUtil() {
+ }
public static boolean isInstantiable(@NotNull PsiClass cls) {
final PsiModifierList modList = cls.getModifierList();
@@ -80,9 +82,9 @@ public class PsiUtil {
String text = expr.getText();
if (text == null) return false;
text = text.replaceAll(" ", "")
- .replaceAll("\n", "")
- .replaceAll("\t", "")
- .replaceAll("\r", "");
+ .replaceAll("\n", "")
+ .replaceAll("\t", "")
+ .replaceAll("\r", "");
return "getClass().getSimpleName()".equals(text) || "this.getClass().getSimpleName()".equals(text);
}
@@ -99,6 +101,18 @@ public class PsiUtil {
return null;
}
+ @Nullable
+ public static PsiMethod findNearestMethod(String name, @Nullable PsiClass cls) {
+ if (cls == null) return null;
+ for (PsiMethod method : cls.getMethods()) {
+ if (method.getParameterList().getParametersCount() == 0 && method.getName().equals(name)) {
+ return method.getModifierList().hasModifierProperty(PsiModifier.ABSTRACT) ? null : method;
+ }
+ }
+ return findNearestMethod(name, cls.getSuperClass());
+ }
+
+
public static boolean isIdeaProject(@Nullable Project project) {
if (project == null) return false;
@@ -111,9 +125,25 @@ public class PsiUtil {
return flag;
}
+ private static boolean isIntelliJBasedDir(VirtualFile baseDir) {
+ if (baseDir == null) {
+ return false;
+ }
+
+ for (VirtualFile dir : new VirtualFile[]{baseDir, baseDir.findChild("community")}) {
+ if (dir == null || !dir.isDirectory()) {
+ continue;
+ }
+ if (dir.findChild("idea.iml") != null || dir.findChild("community-main.iml") != null) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
private static boolean checkIdeaProject(@NotNull Project project) {
- VirtualFile baseDir = project.getBaseDir();
- if (baseDir == null || baseDir.findChild("idea.iml") == null && baseDir.findChild("community-main.iml") == null) {
+ if (!isIntelliJBasedDir(project.getBaseDir())) {
return false;
}