Slide 1

Slide 1 text

Intellij IDEA Plugins Crash Course Yuriy Artamonov

Slide 2

Slide 2 text

Plan 1. IntelliJ IDEA Plugin System 2. Java Frameworks from IDE point of view 3. Let’s build a plugin 4. When not to build plugins at all? 5. What’s next? 2

Slide 3

Slide 3 text

Motivation ■ Everyone has: in-house frameworks, libraries, configs, processes, etc ■ We cannot completely get rid of routine work related to source code, testing, deployment and maintenance We must fight! 3

Slide 4

Slide 4 text

How to Fight Routine Work Options: ■ Build frameworks! ■ Generate code! ■ Improve your tools - IDE 4

Slide 5

Slide 5 text

Let’s share IntelliJ IDEA Knowledge 5

Slide 6

Slide 6 text

Plugins Can Provide Tons of Features ★ Languages and file formats ★ Static code analysis ★ Code navigation ★ Editor improvements ★ Refactoring and code generation ★ Communication with thirdparty tools and services ★ Code migration from old APIs ... 6

Slide 7

Slide 7 text

Key Mechanisms of IntelliJ IDEA ■ Components and services ■ Virtual File System (VFS) ■ Languages parsing and representation (PSI) ■ Code editor ■ Inspections ■ File indexes ■ Background processes ■ UI library ■ Extension points IntelliJ Platform IDEA CE (Java, Groovy, Kotlin) PHP Support Ruby Support IDEA Ultimate PHP Storm RubyMine 7

Slide 8

Slide 8 text

Plugin System All JetBrains IDEs consist of plugins: 1. Plugin classes loaded with a separate class loader 2. Each plugin implements extension points of IDE and may provide its own 3. Plugins may depend on IDE modules and other plugins 8 Groovy Plugin Java Plugin Properties Plugin Ultimate Modules optional

Slide 9

Slide 9 text

How to start 9 1. Check/Enable Plugin DevKit 2. Create Gradle project with IntelliJ Platform Plugin option 3. Declare and implement extension points We strongly recommend using Kotlin!

Slide 10

Slide 10 text

Plugin Structure .../resources/META-INF/plugin.xml micronaut-intellij-plugin Micronaut Framework Support com.intellij.modules.java

Slide 11

Slide 11 text

Extension Points ■ Thousands of them: action, inspection, intention action, reference contributor, ui settings group, language parser, language formatter и т.д. ■ Registered in XML-file – plugin.xml manifest ■ You can find them: LangExtensionPoints.xml, PlatformExtensionPoints.xml, VcsExtensionPoints.xml, etc. Example: Groovy – plugin ( plugin.xml > 1300 LOC ) github.com/JetBrains/intellij-community 11

Slide 12

Slide 12 text

Micronaut Modern JVM Framework for lightweight modular applications (Java / Kotlin / Groovy) Features: ■ Dependency Injection ■ HTTP Web Services (Sync/Async) ■ HTTP Client ■ Data Repositories ■ Compile-time everywhere ■ AOT Compatible 12

Slide 13

Slide 13 text

References 1. Graeme Rocher - Introduction to Micronaut 2. Kirill Tolkachev, Maxim Gorelikov (RU) - Micronaut vs Spring Boot, или Кто тут самый маленький? 13

Slide 14

Slide 14 text

What do we need to support in the plugin? 14

Slide 15

Slide 15 text

First Try Problem: 1. Create a project 2. Run 3. Profit Nothing happens! Documentation: If you are using Java or Kotlin and IntelliJ IDEA make sure you have enabled annotation processing. 15 Let’s check that Annotations Processors are enabled in Micronaut projects!

Slide 16

Slide 16 text

Find Proper Extenstion Point 1. Register class of com.intellij.openapi.startup.StartupActivity: 2. Use UI Inspector to find the IDE mechanisms (Ctrl+Alt+Click): 16

Slide 17

Slide 17 text

Implicit Usages Problem: frameworks are smart and love implicit logic. @Controller class WelcomeController { @View("welcome") @Get("/") public HttpResponse welcome() { return HttpResponse.ok(); } } 💡 Class ‘WelcomeController’ is never used. 17 Let’s teach IDE how to understand implicits!

Slide 18

Slide 18 text

Implicit Usage Provider New extension point: class MicronautImplicitUsageProvider : ImplicitUsageProvider { override fun isImplicitWrite(element: PsiElement?): Boolean { TODO() } override fun isImplicitRead(element: PsiElement?): Boolean { TODO() } override fun isImplicitUsage(element: PsiElement?): Boolean { TODO() } } 18

Slide 19

Slide 19 text

Languages Syntax Basics PSI - Program Structure Interface, a special representation: ■ Structure of the project and code ■ Includes semantic data ■ Everything is PsiElement (almost) ■ Every language provides its own PSI elements 19 PsiFile PsiElement PsiElement PsiElement PsiElement PSI Tree

Slide 20

Slide 20 text

Java PSI model Key elements: ■ PsiJavaFile ■ PsiImportList ■ PsiClass ■ PsiAnnotation ■ PsiField ■ PsiMethod ■ ... See: Tools - View PSI Structure of Current File 20

Slide 21

Slide 21 text

How to find a problem in cron expressions Example: @Singleton public class VisitProcessor { @Scheduled(cron = "0 55 11 * * * .") public void run() { // do work } } What’s wrong here? 21

Slide 22

Slide 22 text

Create Inspections Inspection engine: ■ Static analysis on the fly ■ Traverses PSI tree ■ Can provide quick fixes See: LocalInspectionTool LocalQuickFix 22

Slide 23

Slide 23 text

Check @Scheduled cron expression New inspection in plugin.xml: Inspection class: class MicronautScheduledInspection : AbstractBaseJavaLocalInspectionTool() { override fun checkMethod(method: PsiMethod, manager: InspectionManager, isOnTheFly: Boolean) : Array? { // check here return ProblemDescriptor.EMPTY_ARRAY } } 23

Slide 24

Slide 24 text

JVM Languages Zoo Unified Abstract Syntax Tree (UAST) ■ Describes JVM languages superset: Java, Kotlin и Groovy ■ Elements ○ UElement, UFile, UClass, UMember, UField, UMethod, ... ■ Blocks ○ UComment, UDeclaration, UExpression, UBlockExpression, UCallExpression, USwitchExpression, … ■ Use PSI if needed (resolve) ■ Code generation is limited (use PSI) See: org.jetbrains.uast 24 ПРАВИЛЬНЫЙ ЗООПАРК

Slide 25

Slide 25 text

UAST inspections 1. Change language to UAST in plugin.xml 2. Obtain UElement 3. Find a problem in UAST tree (or in synthetic PSI) 4. Go back to sourcePsi 5. Register problem for sourcePsi See: Tools - Internal Actions - Dump UAST Tree Examples: intellij-community/plugins/devkit/ и Android Studio 25

Slide 26

Slide 26 text

Code navigation Publish/Subcribe with @EventListener: ■ Events are hard to understand ■ Developers hate them ■ IDE does not help Idea: Icons on gutter with navigation to publishers / subsribers! See: LineMarkerProvider RelatedItemLineMarkerProvider 26

Slide 27

Slide 27 text

Base Indexes ■ Word Index ○ Key – word hashcode ○ Value – bitmask of source (in code, in comment, in string literal) ○ Used in Find in Path, Find Usages ■ File names and file types indexes ■ Java Class Names index See: PsiSearchHelper 27

Slide 28

Slide 28 text

How to find something useful Module java-indexing-api provides search methods for standard indexes: ■ ReferencesSearch ■ MethodReferenceSearch ■ AnnotatedElementsSearch ■ … Query MethodReferencesSearch.search(psiMethod, scope, strict) Query AnnotatedElementsSearch.searchPsiMethods(annotationClass, scope) 28

Slide 29

Slide 29 text

Cache it! Search is slow, cache it if possible 29 val cacheManager = CachedValuesManager.getManager(module.project) return cacheManager.getCachedValue(module) { val result = heavyFunction(module) CachedValueProvider.Result.create( result , PsiModificationTracker.MODIFICATION_COUNT, // << track code changes everywhere ProjectRootManager.getInstance(module.project) // and in project structure ) } See: CachedValuesManager

Slide 30

Slide 30 text

References in PSI tree ■ References sets relation between: usage ➜ declaration ■ May provide auto completion ■ We can add references without changing the underlying language See: PsiReferenceContributor PsiLanguageInjectionHost 30

Slide 31

Slide 31 text

Provide References In Micronaut any annotation may contain references to application.properties keys in a form of ${some.property-name}: @Value("${datasources.default.url}") private String datasourceUrl; Idea: let’s connect those strings and config keys with PSI reference 31

Slide 32

Slide 32 text

What’s next? 32

Slide 33

Slide 33 text

What do We Usually Support in Framework Plugins Main extension points: ■ Implicit Usages ■ References ■ Inspections and Quick Fixes ■ Intention Actions ■ Line Markers ■ Language Injection ■ New Project Wizard ■ File Templates 33

Slide 34

Slide 34 text

Sometimes it Requires Tons of Code Spring Framework Support in numbers: ■ 260k LOC Java ■ 20k LOC Kotlin ■ 350k LOC Total ■ 3400 Tests 34

Slide 35

Slide 35 text

Deploy Plugin for Your Company Requirements: ■ HTTP server (e.g. Nginx) ■ Plugins registry file: updatePlugins.xml

Slide 36

Slide 36 text

How to Live without Plugins If there is a chance to not write a plugin - do not create a plugin! Instead: ■ Suppress unused for class annotated with … ■ Setup required plugins and inspections ■ Commit 📁 .idea directory to VCS ■ Setup Language Injection ■ Use JetBrains annotations (nullity, contract, pure, language) dependencies { compileOnly 'org.jetbrains:annotations:17.0.0' } 36

Slide 37

Slide 37 text

Structural Search for Inspections Check code without plugin: ■ Enable Inspections - Structured Search Inspection ■ Setup search templates Example: @Inject $Field$ 37

Slide 38

Slide 38 text

Links ■ IntelliJ IDEA CE Sources: github.com/JetBrains/intellij-community ■ Plugin DevKit Docs: jetbrains.org/intellij/sdk/docs/basics.html ■ Forum: IntelliJ IDEA Open API and Plugin Development 38

Slide 39

Slide 39 text

Questions ? Source code: github.com/jreznot/micronaut-intellij-plugin Twitter: @Yuriy_Artamonov