Writing A Plugin for Android Studio

Writing A Plugin for Android Studio

Do you wish you could click autocomplete an exotic language or drill down into framework String parameters? But sadly there's no IDE plugin for that, what a shame.. But do not fear, we have a solution! Let's explore how we could make our own plugin for our favourite tool. We will start with capability overview of plugin API, get into parsers and lexers, syntax highlight, adding buttons to toolbars, etc. As well as briefly cover best practices, build system configurations and deployment to get your plugin into the wilds into the plugin store!

12d6ff93ca25d366161efccadd81bbb2?s=128

Alexey Buzdin

November 04, 2017
Tweet

Transcript

  1. WRITING A PLUGIN FOR @ALEXEYBUZDIN, SEPTEMBER 2017

  2. @ALEXEYBUZDIN WRITING MOBILE AND JAVA STUFF ORGANIZING EVENTS SPEAKING AT

    CONFERENCES
  3. WRITING A PLUGIN FOR @ALEXEYBUZDIN, SEPTEMBER 2017

  4. None
  5. None
  6. None
  7. None
  8. None
  9. PLUGINS.JETBRAINS.COM

  10. > .gitignore plugin > Markdown support > CheckStyle-IDEA > Lombok

    Plugin > BashSupport > etc
  11. DEMO LET’S START WITH A PROJECT!

  12. RECAP: PLUGIN PROJECT IS A JAVA PROJECT WITH PLUGIN.XML AND

    TARGET SDK - INTELLIJ PRODUCT http://www.jetbrains.org/intellij/sdk/docs/tutorials/customlanguagesupport_tutorial.html
  13. PLUGIN.XML == ANDROIDMANIFEST.XML

  14. TIP: USE GRADLE WITH “ORG.JETBRAINS.INTELLIJ" PLUGIN HTTPS://SL.GDG.LV/SAMPLEINTELLIJPLUGIN

  15. BUILDING A PLUGIN INCREMENTALY

  16. BUILDING A PLUGIN INCREMENTALY BUT FOR WHAT?

  17. Project Goal: www.fusetools.com https://github.com/AlexeyBuzdin/Fuse.IntelliJPlugin

  18. DEMO LET’S START WITH AN ACTION!

  19. WHAT DID WE ACHIEVE? 1. We’ve written an Action 2.

    Created our own Language 3. Registered a new File Type 4. Added an Icon ☺
  20. MORE ON ACTION: GROUPING ACTIONS <actions> <group id="SimpleGroup" text="Custom Action

    Group" popup="true"> <add-to-group group-id="EditorPopupMenu" anchor="first"/> <action class="org.jetbrains.tutorials.actions.GroupedAction" id="org.jetbrains.tutorials.actions.GroupedAction" text="Grouped Action" description="Grouped Action Demo"> </action> </group> </actions>
  21. MORE ON ACTION: DEFAULTACTIONGROUP public class CustomDefaultActionGroup extends DefaultActionGroup {

    @Override public void update(AnActionEvent event) { Editor editor = event.getData(CommonDataKeys.EDITOR); event.getPresentation().setVisible(true); event.getPresentation().setEnabled(editor != null); event.getPresentation().setIcon(AllIcons.General.Error); } }
  22. LET’S FIX THIS! <hikr.Page ux:Class="EditHikePage"> <Router ux:Dependency="router" /> <JavaScript File="EditHikePage.js"

    /> <DockPanel> <Grid ColumnCount="2" Dock="Bottom"> <hikr.Button Text="Cancel" Clicked="{cancel}" /> <hikr.Button Text="Save" Clicked="{save}" /> </Grid> <ScrollView> <StackPanel ItemSpacing="10" Padding="10"> <hikr.Text ux:Class="TitleText" Opacity=".6" Margin="0,0,0,5" /> <StackPanel> <TitleText>Name:</TitleText> <hikr.TextBox Value="{name}" /> </StackPanel> <StackPanel> <TitleText>Comments:</TitleText> <hikr.TextView Value="{comments}" TextWrapping="Wrap" /> </StackPanel> </StackPanel> </ScrollView> </DockPanel> </hikr.Page>
  23. LET’S FIX THIS! <hikr.Page ux:Class="EditHikePage"> <Router ux:Dependency="router" /> <JavaScript File="EditHikePage.js"

    /> <DockPanel> <Grid ColumnCount="2" Dock="Bottom"> <hikr.Button Text="Cancel" Clicked="{cancel}" /> <hikr.Button Text="Save" Clicked="{save}" /> </Grid> <ScrollView> <StackPanel ItemSpacing="10" Padding="10"> <hikr.Text ux:Class="TitleText" Opacity=".6" Margin="0,0,0,5" /> <StackPanel> <TitleText>Name:</TitleText> <hikr.TextBox Value="{name}" /> </StackPanel> <StackPanel> <TitleText>Comments:</TitleText> <hikr.TextView Value="{comments}" TextWrapping="Wrap" /> </StackPanel> </StackPanel> </ScrollView> </DockPanel> </hikr.Page>
  24. None
  25. PARSER AND LEXER LEXER - PARSES CODE INTO TOKENS PARSER

    - VALIDATES CODE STRUCTURE
  26. BACKUS–NAUR FORM > <symbol> ::= __expression__ > <integer> ::= <digit>|<integer><digit>

    > <name> ::= <first_name><last_name>
  27. BACKUS–NAUR FORM FOR CSV <file> ::= <line>*

  28. BACKUS–NAUR FORM FOR CSV <file> ::= <line>* <line> ::= <value>?

    | <value>(“,”<value>)+ “,”?
  29. BACKUS–NAUR FORM FOR CSV <file> ::= <line>* <line> ::= <value>?

    | <value>(“,”<value>)+ “,”? <value> ::= [^,]+
  30. Grammar-Kit root_rule ::= rule_A rule_B rule_C rule_D // a sequence

    rule_A ::= token | 'or_text' | "another_one" // a choice rule_B ::= [ optional_token ] and_another_one? // optional parts rule_C ::= &required !forbidden // predicates rule_D ::= { can_use_braces + (and_parens) * } // grouping and repetition // Grammar-Kit extensions: private left rule_with_modifier ::= '+' // rule modifiers left rule_with_attributes ::= '?' {elementType=rule_D} // left rule and attributes private meta list_macro ::= <<p>> (',' <<p>>) * // meta rule private list_usage ::= <<list_macro rule_D>> // external expression https://github.com/JetBrains/Grammar-Kit
  31. DEMO LET’S DESCRIBE A LANGUAGE!

  32. JFlex - Lexical Analyzer Generator http://jflex.de/manual.html

  33. package com.simpleplugin; import com.intellij.lexer.FlexLexer; import com.intellij.psi.tree.IElementType; import com.simpleplugin.psi.SimpleTypes; import com.intellij.psi.TokenType;

    %% %class SimpleLexer %implements FlexLexer %unicode %function advance %type IElementType %eof{ return; %eof} CRLF=\R WHITE_SPACE=[\ \n\t\f] FIRST_VALUE_CHARACTER=[^ \n\f\\] | "\\"{CRLF} | "\\". VALUE_CHARACTER=[^\n\f\\] | "\\"{CRLF} | "\\". END_OF_LINE_COMMENT=("#"|"!")[^\r\n]* SEPARATOR=[:=] KEY_CHARACTER=[^:=\ \n\t\f\\] | "\\ "
  34. PROPERTY: KEY = VALUE %state WAITING_VALUE %% <YYINITIAL> {END_OF_LINE_COMMENT} {

    yybegin(YYINITIAL); return SimpleTypes.COMMENT; } <YYINITIAL> {KEY_CHARACTER}+ { yybegin(YYINITIAL); return SimpleTypes.KEY; } <YYINITIAL> {SEPARATOR} { yybegin(WAITING_VALUE); return SimpleTypes.SEPARATOR; } <WAITING_VALUE> {CRLF}({CRLF}|{WHITE_SPACE})+ { yybegin(YYINITIAL); return TokenType.WHITE_SPACE; } <WAITING_VALUE> {WHITE_SPACE}+ { yybegin(WAITING_VALUE); return TokenType.WHITE_SPACE; } <WAITING_VALUE> {FIRST_VALUE_CHARACTER}{VALUE_CHARACTER}* { yybegin(YYINITIAL); return SimpleTypes.VALUE; } ({CRLF}|{WHITE_SPACE})+ { yybegin(YYINITIAL); return TokenType.WHITE_SPACE; } . { return TokenType.BAD_CHARACTER; }
  35. DEMO NEXT STEP IS LEXER!

  36. WHAT DID WE ACHIEVE? 1. We’ve described a Parser 2.

    Implemented a Lexer 3. Added some ! NEXT STEPS?
  37. COLORSETTINGSPAGE

  38. PROJECT WIZARD Full javax.swing.* support

  39. RUN CONFIGURATIONS Gather state from JPanel and triggers on startProcess()

  40. WHAT CAN YOU DO WITH PSIELEMENT 1. References and GoTo

    2. Find Usage 3. Code Completion 4. Annotators 5. Line Marker 6. More
  41. TIPS PREVENTING UI FREEZES ALWAYS CHECK FOR NEGATIVE CASE IMPROVE

    THE BNF
  42. GREAT EXAMPLES > jetbrains-plugin-graph-database > intellij-erlang > intellij-rust

  43. THANK YOU! Q&A FOLLOW ME ON TWITTER @ALEXEYBUZDIN