Code Samples READMEs

This commit is contained in:
Jakub Chrzanowski 2020-08-12 11:25:19 +02:00 committed by GitHub
parent 5bc3d29733
commit 857d945494
131 changed files with 1705 additions and 616 deletions

View File

@ -0,0 +1,63 @@
# IntelliJ Platform SDK Code Samples
[![official JetBrains project](https://jb.gg/badges/official.svg)][jb:confluence-on-gh]
[![JetBrains IntelliJ Platform SDK Docs](https://jb.gg/badges/docs.svg)][jb:docs]
[![Twitter Follow](https://img.shields.io/twitter/follow/JBPlatform?style=flat)][jb:twitter]
[![Build](https://github.com/JetBrains/intellij-sdk-docs/workflows/Build/badge.svg)][gh:build]
[![Slack](https://img.shields.io/badge/Slack-%23intellij--platform-blue)][jb:slack]
Learn how to build plugins using IntelliJ Platform SDK for the [JetBrains products][jb:products] by experimenting with
our code samples. These samples show you how features work and help you jumpstart your plugins.
There is also [IntelliJ Platform Plugin Template][gh:template] project available.
## Structure
Code Samples depend on the [IntelliJ Platform SDK][docs] and [Gradle][docs:gradle] as a build system.
The main plugin definition file is stored in the `plugin.xml` file, which is created according
to the [Plugin Configuration File documentation][docs:plugin.xml]. It describes definitions of the actions, extensions,
or listeners provided by the plugin.
## Code Samples
In the following table, you may find all available samples provided in the separated directories as stand-alone
projects available for running with the Gradle `:runIde` task.
| Code Sample | Description |
| ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| [Action Basics](./action_basics) | Action and Action Group patterns implementation, adds entries to the Tools menu. |
| [Comparing References Inspection](./comparing_references_inspection) | Local Inspection Tool, adds entries to `Preferences \| Editor \| Inspections \| Java \| Probable Bugs`. |
| [Conditional Operator Intention](./conditional_operator_intention) | Intention action, suggests converting a ternary operator into an *if* block and adds entry to `Preferences \| Editor \| Intentions \| SDK Intentions`. |
| [Editor Basics](./editor_basics) | Basic Editor APIs example with editor popup menu with extra actions. |
| [Facet Basics](./facet_basics) | Custom Facet pattern, adds *SDK Facet* to the `Project Structure \| Project Settings \| Facets menu`. |
| [Framework Basics](./framework_basics) | Basic *SDK Demo Framework* support added to the `File \| New \| Project \| IntelliJ Platform Plugin` |
| [Inspection Basics](./inspection_basics) | Code Inspection entry added to the `Preferences \| Editor \| Inspections \| SDK \| Example Tools`. |
| [Kotlin Demo](./kotlin_demo) | Kotlin example extending the *Main Menu* with a `Greeting` menu group. |
| [Live Templates](./live_templates) | Live templates for Markdown language, adds an entry to the `Preferences \| Editor \| Live Templates` dialog. |
| [Max Opened Projects](./max_opened_projects) | Application services and listeners, shows warning dialog when more than 3 open projects are opened. |
| [Module](./module) | *SDK Demo Module* module type added to the `File \| New \| Project...`. |
| [Product Specific - PyCharm Sample](./product_specific/pycharm_basics) | Plugin project configuration for the PyCharm IDE. |
| [Project Model](./project_model) | Interacts with the project model, adds menu items to `Tools` and `Editor Context` menus. |
| [Project View Pane](./project_view_pane) | Project View Pane listing only image files. |
| [Project Wizard](./project_wizard) | Project Wizard example with demo steps. |
| [PSI Demo](./psi_demo) | PSI Navigation features presentation. |
| [Run Configuration](./run_configuration) | Run configuration implementation with factory, options and UI. |
| [Settings](./settings) | Custom settings panel, adds a settings panel to the `Settings \| Preferences` panel under `Tools`. |
| [Simple Language Plugin](./simple_language_plugin) | Custom language support, defines a new *Simple language* with syntax highlighting, annotations, code completion, and other features. |
| [Theme Basics](./theme_basics) | Sample *UI Theme* plugin with basic interface modifications. |
| [Tool Window](./tool_window) | Custom Tool Window example plugin. |
| [Tree Structure Provider](./tree_structure_provider) | Tree Structure Provider showing only plain text files. |
[gh:build]: https://github.com/JetBrains/intellij-sdk-docs/actions?query=workflow%3ABuild
[gh:template]: https://github.com/JetBrains/intellij-platform-plugin-template
[jb:confluence-on-gh]: https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub
[jb:docs]: https://www.jetbrains.org/intellij/sdk/docs
[jb:products]: https://www.jetbrains.com/products.html
[jb:slack]: https://plugins.jetbrains.com/slack
[jb:twitter]: https://twitter.com/JBPlatform
[docs]: https://www.jetbrains.org/intellij/sdk/docs
[docs:gradle]: https://www.jetbrains.org/intellij/sdk/docs/tutorials/build_system.html
[docs:plugin.xml]: https://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_configuration_file.html

36
action_basics/README.md Normal file
View File

@ -0,0 +1,36 @@
# Action Sample Project [![JetBrains IntelliJ Platform SDK Docs](https://jb.gg/badges/docs.svg)][docs]
*Reference: [Action System in IntelliJ SDK Docs][docs:actions]*
## Quickstart
Action Sample Project demonstrates registering actions process in various configurations.
Each action is an extension of the [`AnAction`][sdk:AnAction] abstract class and brings the possibility of extending IDE with an event performed with the user interaction - i.e., clicking the button, using the keyboard or mouse shortcuts.
Plugin registers the [`PopupDialogAction`][file:PopupDialogAction] action, which provides a popup dialog as a feedback, in three different ways:
- by assigning the keyboard (<kbd>Ctrl/Cmd</kbd>+<kbd>Alt</kbd>+<kbd>A</kbd>, <kbd>C</kbd>) and mouse shortcuts (<kbd>Ctrl/Cmd</kbd> + <kbd>Mouse Button 3</kbd> + <kbd>Double Click</kbd>),
- by adding action item to the `ToolsMenu` group, available in Tools menu,
- by adding action item to the `EditorPopupMenu` group, available in Editor's context menu.
### Actions
| ID | Implementation | Extension Point Class |
| -------------------------------------------------- | --------------------------------------------------------- | ------------------------------ |
| `org.intellij.sdk.action.PopupDialogAction` | [PopupDialogAction][file:PopupDialogAction] | [AnAction][sdk:AnAction] |
| `org.intellij.sdk.action.GroupPopDialogAction` | [PopupDialogAction][file:PopupDialogAction] | [AnAction][sdk:AnAction] |
| `org.intellij.sdk.action.CustomGroupedAction` | [PopupDialogAction][file:PopupDialogAction] | [AnAction][sdk:AnAction] |
| `org.intellij.sdk.action.CustomDefaultActionGroup` | [CustomDefaultActionGroup][file:CustomDefaultActionGroup] | [ActionGroup][sdk:ActionGroup] |
| `org.intellij.sdk.action.DynamicActionGroup` | [DynamicActionGroup][file:DynamicActionGroup] | [ActionGroup][sdk:ActionGroup] |
*Reference: [Action System in IntelliJ SDK Docs][docs:actions]*
[docs]: https://www.jetbrains.org/intellij/sdk/docs
[docs:actions]: https://www.jetbrains.org/intellij/sdk/docs/basics/action_system.html
[file:PopupDialogAction]: ./src/main/java/org/intellij/sdk/action/PopupDialogAction.java
[file:CustomDefaultActionGroup]: ./src/main/java/org/intellij/sdk/action/CustomDefaultActionGroup.java
[file:DynamicActionGroup]: ./src/main/java/org/intellij/sdk/action/DynamicActionGroup.java
[sdk:AnAction]: upsource:///platform/editor-ui-api/src/com/intellij/openapi/actionSystem/AnAction.java
[sdk:ActionGroup]: upsource:///platform/platform-api/src/com/intellij/openapi/actionSystem/ActionInGroup.java

View File

@ -3,8 +3,11 @@
package icons; package icons;
import com.intellij.openapi.util.IconLoader; import com.intellij.openapi.util.IconLoader;
import javax.swing.*; import javax.swing.*;
public class SdkIcons { public class SdkIcons {
public static final Icon Sdk_default_icon = IconLoader.getIcon("/icons/sdk_16.svg"); public static final Icon Sdk_default_icon = IconLoader.getIcon("/icons/sdk_16.svg");
} }

View File

@ -12,13 +12,14 @@ import icons.SdkIcons;
* Creates an action group to contain menu actions. See plugin.xml declarations. * Creates an action group to contain menu actions. See plugin.xml declarations.
*/ */
public class CustomDefaultActionGroup extends DefaultActionGroup { public class CustomDefaultActionGroup extends DefaultActionGroup {
/** /**
* Given CustomDefaultActionGroup is derived from ActionGroup, in this context * Given CustomDefaultActionGroup is derived from ActionGroup, in this context
* update() determines whether the action group itself should be enabled or disabled. * update() determines whether the action group itself should be enabled or disabled.
* Requires an editor to be active in order to enable the group functionality. * Requires an editor to be active in order to enable the group functionality.
*
* @param event Event received when the associated group-id menu is chosen.
* @see com.intellij.openapi.actionSystem.AnAction#update(AnActionEvent) * @see com.intellij.openapi.actionSystem.AnAction#update(AnActionEvent)
* @param event Event received when the associated group-id menu is chosen.
*/ */
@Override @Override
public void update(AnActionEvent event) { public void update(AnActionEvent event) {
@ -28,4 +29,5 @@ public class CustomDefaultActionGroup extends DefaultActionGroup {
// Take this opportunity to set an icon for the menu entry. // Take this opportunity to set an icon for the menu entry.
event.getPresentation().setIcon(SdkIcons.Sdk_default_icon); event.getPresentation().setIcon(SdkIcons.Sdk_default_icon);
} }
} }

View File

@ -30,9 +30,7 @@ public class DynamicActionGroup extends ActionGroup {
@Override @Override
public AnAction[] getChildren(AnActionEvent e) { public AnAction[] getChildren(AnActionEvent e) {
return new AnAction[]{ return new AnAction[]{
new PopupDialogAction("Action Added at Runtime", new PopupDialogAction("Action Added at Runtime", "Dynamic Action Demo", SdkIcons.Sdk_default_icon)
"Dynamic Action Demo",
SdkIcons.Sdk_default_icon)
}; };
} }

View File

@ -20,40 +20,43 @@ import javax.swing.*;
* in the plugin.xml file. But when added at runtime this class is instantiated by an action group. * in the plugin.xml file. But when added at runtime this class is instantiated by an action group.
*/ */
public class PopupDialogAction extends AnAction { public class PopupDialogAction extends AnAction {
/** /**
* This default constructor is used by the IntelliJ Platform framework to * This default constructor is used by the IntelliJ Platform framework to
* instantiate this class based on plugin.xml declarations. Only needed in PopupDialogAction * instantiate this class based on plugin.xml declarations. Only needed in PopupDialogAction
* class because a second constructor is overridden. * class because a second constructor is overridden.
*
* @see AnAction#AnAction() * @see AnAction#AnAction()
*/ */
public PopupDialogAction() { public PopupDialogAction() {
super(); super();
} }
/** /**
* This constructor is used to support dynamically added menu actions. * This constructor is used to support dynamically added menu actions.
* It sets the text, description to be displayed for the menu item. * It sets the text, description to be displayed for the menu item.
* Otherwise, the default AnAction constructor is used by the IntelliJ Platform. * Otherwise, the default AnAction constructor is used by the IntelliJ Platform.
* @param text The text to be displayed as a menu item. *
* @param description The description of the menu item. * @param text The text to be displayed as a menu item.
* @param icon The icon to be used with the menu item. * @param description The description of the menu item.
* @param icon The icon to be used with the menu item.
*/ */
public PopupDialogAction(@Nullable String text, @Nullable String description, @Nullable Icon icon) { public PopupDialogAction(@Nullable String text, @Nullable String description, @Nullable Icon icon) {
super(text, description, icon); super(text, description, icon);
} }
/** /**
* Gives the user feedback when the dynamic action menu is chosen. * Gives the user feedback when the dynamic action menu is chosen.
* Pops a simple message dialog. See the psi_demo plugin for an * Pops a simple message dialog. See the psi_demo plugin for an
* example of how to use AnActionEvent to access data. * example of how to use AnActionEvent to access data.
*
* @param event Event received when the associated menu item is chosen. * @param event Event received when the associated menu item is chosen.
*/ */
@Override @Override
public void actionPerformed(@NotNull AnActionEvent event) { public void actionPerformed(@NotNull AnActionEvent event) {
// Using the event, create and show a dialog // Using the event, create and show a dialog
Project currentProject = event.getProject(); Project currentProject = event.getProject();
StringBuffer dlgMsg = new StringBuffer(event.getPresentation().getText() + " Selected!"); StringBuilder dlgMsg = new StringBuilder(event.getPresentation().getText() + " Selected!");
String dlgTitle = event.getPresentation().getDescription(); String dlgTitle = event.getPresentation().getDescription();
// If an element is selected in the editor, add info about it. // If an element is selected in the editor, add info about it.
Navigatable nav = event.getData(CommonDataKeys.NAVIGATABLE); Navigatable nav = event.getData(CommonDataKeys.NAVIGATABLE);
@ -62,10 +65,11 @@ public class PopupDialogAction extends AnAction {
} }
Messages.showMessageDialog(currentProject, dlgMsg.toString(), dlgTitle, Messages.getInformationIcon()); Messages.showMessageDialog(currentProject, dlgMsg.toString(), dlgTitle, Messages.getInformationIcon());
} }
/** /**
* Determines whether this menu item is available for the current context. * Determines whether this menu item is available for the current context.
* Requires a project to be open. * Requires a project to be open.
*
* @param e Event received when the associated group-id menu is chosen. * @param e Event received when the associated group-id menu is chosen.
*/ */
@Override @Override
@ -74,5 +78,5 @@ public class PopupDialogAction extends AnAction {
Project project = e.getProject(); Project project = e.getProject();
e.getPresentation().setEnabledAndVisible(project != null); e.getPresentation().setEnabledAndVisible(project != null);
} }
} }

View File

@ -2,16 +2,16 @@
<idea-plugin> <idea-plugin>
<!-- Unique id for this plugin. Must stay constant for the life of the plugin. --> <!-- Unique id for this plugin. Must stay constant for the life of the plugin. -->
<id>org.intellij.sdk.action</id> <id>org.intellij.sdk.action</id>
<!-- Text to display as name on Preferences/Settings | Plugin page --> <!-- Text to display as name on Preferences/Settings | Plugin page -->
<name>SDK: Action Sample Project</name> <name>SDK: Action Sample</name>
<!-- Indicate this plugin can be loaded in all IntelliJ Platform-based products. --> <!-- Indicate this plugin can be loaded in all IntelliJ Platform-based products. -->
<depends>com.intellij.modules.platform</depends> <depends>com.intellij.modules.platform</depends>
<!-- Text to display as description on Preferences/Settings | Plugin page --> <!-- Text to display as description on Preferences/Settings | Plugin page -->
<description> <description>
<![CDATA[ <![CDATA[
Demonstrates implementing Action and Action Group patterns.<br> Adds entries to the Tools menu. Demonstrates implementing Action and Action Group patterns.<br> Adds entries to the Tools menu.
@ -31,40 +31,53 @@
<vendor url="https://plugins.jetbrains.com">IntelliJ Platform SDK</vendor> <vendor url="https://plugins.jetbrains.com">IntelliJ Platform SDK</vendor>
<actions> <actions>
<!-- See https://www.jetbrains.org/intellij/sdk/docs/basics/action_system.html#registering-actions-in-pluginxml <!--
for information about the elements and attributes used for actions and groups. --> See https://www.jetbrains.org/intellij/sdk/docs/basics/action_system.html#registering-actions-in-pluginxml
<!-- The <action> element adds a static menu item in first position of the Tools menu that shows PopupDialogAction. --> for information about the elements and attributes used for actions and groups.
The <action> element adds a static menu item in first position of the Tools menu that shows PopupDialogAction.
-->
<action id="org.intellij.sdk.action.PopupDialogAction" class="org.intellij.sdk.action.PopupDialogAction" <action id="org.intellij.sdk.action.PopupDialogAction" class="org.intellij.sdk.action.PopupDialogAction"
text="Action Basics Plugin: Pop Dialog Action" description="SDK action example" icon="SdkIcons.Sdk_default_icon"> text="Action Basics Plugin: Pop Dialog Action" description="SDK action example"
icon="SdkIcons.Sdk_default_icon">
<override-text place="MainMenu" text="Pop Dialog Action"/> <override-text place="MainMenu" text="Pop Dialog Action"/>
<keyboard-shortcut first-keystroke="control alt A" second-keystroke="C" keymap="$default"/> <keyboard-shortcut first-keystroke="control alt A" second-keystroke="C" keymap="$default"/>
<mouse-shortcut keystroke="control button3 doubleClick" keymap="$default"/> <mouse-shortcut keystroke="control button3 doubleClick" keymap="$default"/>
<add-to-group group-id="ToolsMenu" anchor="first"/> <add-to-group group-id="ToolsMenu" anchor="first"/>
</action> </action>
<!-- All of the following menu groups add the action PopupDialogAction to menus in different ways. <!--
Note that even though these groups reuse the same action class, in each use the action ids are unique. --> All of the following menu groups add the action PopupDialogAction to menus in different ways.
<!-- GroupedActions demonstrates declaring an action group using the default ActionGroup implementation provided by the Note that even though these groups reuse the same action class, in each use the action ids are unique.
IntelliJ Platform framework. (Note the lack of a group "class" attribute.) GroupedActions gets inserted after PopupDialogAction GroupedActions demonstrates declaring an action group using the default ActionGroup implementation provided by the
in the Tools menu. Because the group's implementation is default, it cannot impose enable/disable conditions. Instead it IntelliJ Platform framework. (Note the lack of a group "class" attribute.) GroupedActions gets inserted after
must rely on the conditions imposed by the parent menu where it is inserted. It declares one action in the group. --> PopupDialogAction in the Tools menu. Because the group's implementation is default, it cannot impose
<group id="org.intellij.sdk.action.GroupedActions" text="Static Grouped Actions" popup="true" icon="SdkIcons.Sdk_default_icon"> enable/disable conditions. Instead it must rely on the conditions imposed by the parent menu where it is inserted.
It declares one action in the group.
-->
<group id="org.intellij.sdk.action.GroupedActions" text="Static Grouped Actions" popup="true"
icon="SdkIcons.Sdk_default_icon">
<add-to-group group-id="ToolsMenu" anchor="after" relative-to-action="org.intellij.sdk.action.PopupDialogAction"/> <add-to-group group-id="ToolsMenu" anchor="after" relative-to-action="org.intellij.sdk.action.PopupDialogAction"/>
<action class="org.intellij.sdk.action.PopupDialogAction" id="org.intellij.sdk.action.GroupPopDialogAction" <action class="org.intellij.sdk.action.PopupDialogAction" id="org.intellij.sdk.action.GroupPopDialogAction"
text="A Group Action" description="SDK static grouped action example" icon="SdkIcons.Sdk_default_icon"> text="A Group Action" description="SDK static grouped action example" icon="SdkIcons.Sdk_default_icon">
</action> </action>
</group> </group>
<!-- CustomDefaultActionGroup demonstrates declaring an action group based on a ActionGroup class supplied by this plugin. <!--
This group is to be inserted atop the Editor Popup Menu. It declares one action in the group. --> CustomDefaultActionGroup demonstrates declaring an action group based on a ActionGroup class supplied by this
<group id="org.intellij.sdk.action.CustomDefaultActionGroup" class="org.intellij.sdk.action.CustomDefaultActionGroup" popup="true" plugin. This group is to be inserted atop the Editor Popup Menu. It declares one action in the group.
text="Popup Grouped Actions" description="Custom defaultActionGroup demo" icon="SdkIcons.Sdk_default_icon"> -->
<group id="org.intellij.sdk.action.CustomDefaultActionGroup"
class="org.intellij.sdk.action.CustomDefaultActionGroup" popup="true"
text="Popup Grouped Actions" description="Custom defaultActionGroup demo" icon="SdkIcons.Sdk_default_icon">
<add-to-group group-id="EditorPopupMenu" anchor="first"/> <add-to-group group-id="EditorPopupMenu" anchor="first"/>
<action class="org.intellij.sdk.action.PopupDialogAction" id="org.intellij.sdk.action.CustomGroupedAction" <action class="org.intellij.sdk.action.PopupDialogAction" id="org.intellij.sdk.action.CustomGroupedAction"
text="A Popup Action" description="SDK popup grouped action example" icon="SdkIcons.Sdk_default_icon"/> text="A Popup Action" description="SDK popup grouped action example" icon="SdkIcons.Sdk_default_icon"/>
</group> </group>
<!-- DynamicActionGroup demonstrates declaring an action group without a static action declaration. <!--
An action is added to the group programmatically in the DynamicActionGroup implementation. --> DynamicActionGroup demonstrates declaring an action group without a static action declaration.
<group id="org.intellij.sdk.action.DynamicActionGroup" class="org.intellij.sdk.action.DynamicActionGroup" popup="true" An action is added to the group programmatically in the DynamicActionGroup implementation.
text="Dynamically Grouped Actions" description="SDK dynamically grouped action example" icon="SdkIcons.Sdk_default_icon"> -->
<group id="org.intellij.sdk.action.DynamicActionGroup" class="org.intellij.sdk.action.DynamicActionGroup"
popup="true" text="Dynamically Grouped Actions" description="SDK dynamically grouped action example"
icon="SdkIcons.Sdk_default_icon">
<add-to-group group-id="ToolsMenu" anchor="after" relative-to-action="org.intellij.sdk.action.GroupedActions"/> <add-to-group group-id="ToolsMenu" anchor="after" relative-to-action="org.intellij.sdk.action.GroupedActions"/>
</group> </group>
</actions> </actions>

View File

@ -0,0 +1,26 @@
# Comparing References Inspection Sample [![JetBrains IntelliJ Platform SDK Docs](https://jb.gg/badges/docs.svg)][docs]
*Reference: [Code Inspections in IntelliJ SDK Docs][docs:code_inspections]*
## Quickstart
Comparing References Inspection Sample demonstrates the implementation of the [Code Inspections][docs:code_inspections] feature for Java classes.
The plugin inspects your Java code and highlights any fragments containing the comparison of two `String` or `Date` variables.
If such a check finds a comparison using the `==` or !`=` operators instead of the `.equals()` method, the plugin proposes a *quick-fix* action.
### Extension Points
| Name | Implementation | Extension Point Class |
| ------------------------------ | ------------------------------------------------------------------- | -------------------------------------------------------- |
| `com.intellij.localInspection` | [ComparingReferencesInspection][file:ComparingReferencesInspection] | [AbstractBaseJavaLocalInspectionTool][sdk:AbstractBJLIT] |
*Reference: [Plugin Extension Points in IntelliJ SDK Docs][docs:ep]*
[docs]: https://www.jetbrains.org/intellij/sdk/docs
[docs:code_inspections]: https://www.jetbrains.org/intellij/sdk/docs/tutorials/code_inspections.html
[docs:ep]: https://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_extensions.html
[file:ComparingReferencesInspection]: ./src/main/java/org/intellij/sdk/codeInspection/ComparingReferencesInspection.java
[sdk:AbstractBJLIT]: upsource:///java/java-analysis-api/src/com/intellij/codeInspection/AbstractBaseJavaLocalInspectionTool.java

View File

@ -24,17 +24,17 @@ import static com.siyeh.ig.psiutils.ExpressionUtils.isNullLiteral;
* The quick fix converts these comparisons to 'a.equals(b) or '!a.equals(b)' respectively. * The quick fix converts these comparisons to 'a.equals(b) or '!a.equals(b)' respectively.
*/ */
public class ComparingReferencesInspection extends AbstractBaseJavaLocalInspectionTool { public class ComparingReferencesInspection extends AbstractBaseJavaLocalInspectionTool {
// Defines the text of the quick fix intention
public static final String QUICK_FIX_NAME = "SDK: " +
InspectionsBundle.message("inspection.comparing.references.use.quickfix");
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.ComparingReferencesInspection"); private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.ComparingReferencesInspection");
private final CriQuickFix myQuickFix = new CriQuickFix(); private final CriQuickFix myQuickFix = new CriQuickFix();
// Defines the text of the quick fix intention
public static final String QUICK_FIX_NAME = "SDK: " + InspectionsBundle.message("inspection.comparing.references.use.quickfix");
// This string holds a list of classes relevant to this inspection. // This string holds a list of classes relevant to this inspection.
@SuppressWarnings({"WeakerAccess"}) @SuppressWarnings({"WeakerAccess"})
@NonNls @NonNls
public String CHECKED_CLASSES = "java.lang.String;java.util.Date"; public String CHECKED_CLASSES = "java.lang.String;java.util.Date";
/** /**
* This method is called to get the panel describing the inspection. * This method is called to get the panel describing the inspection.
* It is called every time the user selects the inspection in preferences. * It is called every time the user selects the inspection in preferences.
@ -55,7 +55,7 @@ public class ComparingReferencesInspection extends AbstractBaseJavaLocalInspecti
panel.add(checkedClasses); panel.add(checkedClasses);
return panel; return panel;
} }
/** /**
* This method is overridden to provide a custom visitor * This method is overridden to provide a custom visitor
* that inspects expressions with relational operators '==' and '!=' * that inspects expressions with relational operators '==' and '!='
@ -70,14 +70,15 @@ public class ComparingReferencesInspection extends AbstractBaseJavaLocalInspecti
@Override @Override
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) { public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new JavaElementVisitor() { return new JavaElementVisitor() {
/** /**
* This string defines the short message shown to a user signaling the inspection * This string defines the short message shown to a user signaling the inspection
* found a problem. It reuses a string from the inspections bundle. * found a problem. It reuses a string from the inspections bundle.
*/ */
@NonNls @NonNls
private final String DESCRIPTION_TEMPLATE = "SDK " + InspectionsBundle.message("inspection.comparing.references.problem.descriptor"); private final String DESCRIPTION_TEMPLATE = "SDK " +
InspectionsBundle.message("inspection.comparing.references.problem.descriptor");
/** /**
* Avoid defining visitors for both Reference and Binary expressions. * Avoid defining visitors for both Reference and Binary expressions.
* *
@ -86,7 +87,7 @@ public class ComparingReferencesInspection extends AbstractBaseJavaLocalInspecti
@Override @Override
public void visitReferenceExpression(PsiReferenceExpression psiReferenceExpression) { public void visitReferenceExpression(PsiReferenceExpression psiReferenceExpression) {
} }
/** /**
* Evaluate binary psi expressions to see if they contain * Evaluate binary psi expressions to see if they contain
* relational operators '==' and '!=', AND they contain * relational operators '==' and '!=', AND they contain
@ -105,19 +106,20 @@ public class ComparingReferencesInspection extends AbstractBaseJavaLocalInspecti
// The binary expression is the correct type for this inspection // The binary expression is the correct type for this inspection
PsiExpression lOperand = expression.getLOperand(); PsiExpression lOperand = expression.getLOperand();
PsiExpression rOperand = expression.getROperand(); PsiExpression rOperand = expression.getROperand();
if (rOperand == null || isNullLiteral(lOperand) || isNullLiteral(rOperand)) if (rOperand == null || isNullLiteral(lOperand) || isNullLiteral(rOperand)) {
return; return;
}
// Nothing is compared to null, now check the types being compared // Nothing is compared to null, now check the types being compared
PsiType lType = lOperand.getType(); PsiType lType = lOperand.getType();
PsiType rType = rOperand.getType(); PsiType rType = rOperand.getType();
if (isCheckedType(lType) || isCheckedType(rType)) { if (isCheckedType(lType) || isCheckedType(rType)) {
// Identified an expression with potential problems, add to list with fix object. // Identified an expression with potential problems, add to list with fix object.
holder.registerProblem(expression, holder.registerProblem(expression,
DESCRIPTION_TEMPLATE, myQuickFix); DESCRIPTION_TEMPLATE, myQuickFix);
} }
} }
} }
/** /**
* Verifies the input is the correct {@code PsiType} for this inspection. * Verifies the input is the correct {@code PsiType} for this inspection.
* *
@ -126,26 +128,28 @@ public class ComparingReferencesInspection extends AbstractBaseJavaLocalInspecti
* one of the classes in the CHECKED_CLASSES list. * one of the classes in the CHECKED_CLASSES list.
*/ */
private boolean isCheckedType(PsiType type) { private boolean isCheckedType(PsiType type) {
if (!(type instanceof PsiClassType)) if (!(type instanceof PsiClassType)) {
return false; return false;
}
StringTokenizer tokenizer = new StringTokenizer(CHECKED_CLASSES, ";"); StringTokenizer tokenizer = new StringTokenizer(CHECKED_CLASSES, ";");
while (tokenizer.hasMoreTokens()) { while (tokenizer.hasMoreTokens()) {
String className = tokenizer.nextToken(); String className = tokenizer.nextToken();
if (type.equalsToText(className)) if (type.equalsToText(className)) {
return true; return true;
}
} }
return false; return false;
} }
}; };
} }
/** /**
* This class provides a solution to inspection problem expressions by manipulating * This class provides a solution to inspection problem expressions by manipulating
* the PSI tree to use a.equals(b) instead of '==' or '!=' * the PSI tree to use a.equals(b) instead of '==' or '!='
*/ */
private static class CriQuickFix implements LocalQuickFix { private static class CriQuickFix implements LocalQuickFix {
/** /**
* Returns a partially localized string for the quick fix intention. * Returns a partially localized string for the quick fix intention.
* Used by the test code for this plugin. * Used by the test code for this plugin.
@ -157,7 +161,7 @@ public class ComparingReferencesInspection extends AbstractBaseJavaLocalInspecti
public String getName() { public String getName() {
return QUICK_FIX_NAME; return QUICK_FIX_NAME;
} }
/** /**
* This method manipulates the PSI tree to replace 'a==b' with 'a.equals(b) * This method manipulates the PSI tree to replace 'a==b' with 'a.equals(b)
* or 'a!=b' with '!a.equals(b)' * or 'a!=b' with '!a.equals(b)'
@ -171,18 +175,19 @@ public class ComparingReferencesInspection extends AbstractBaseJavaLocalInspecti
IElementType opSign = binaryExpression.getOperationTokenType(); IElementType opSign = binaryExpression.getOperationTokenType();
PsiExpression lExpr = binaryExpression.getLOperand(); PsiExpression lExpr = binaryExpression.getLOperand();
PsiExpression rExpr = binaryExpression.getROperand(); PsiExpression rExpr = binaryExpression.getROperand();
if (rExpr == null) if (rExpr == null) {
return; return;
}
PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory(); PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
PsiMethodCallExpression equalsCall = PsiMethodCallExpression equalsCall =
(PsiMethodCallExpression) factory.createExpressionFromText("a.equals(b)", null); (PsiMethodCallExpression) factory.createExpressionFromText("a.equals(b)", null);
equalsCall.getMethodExpression().getQualifierExpression().replace(lExpr); equalsCall.getMethodExpression().getQualifierExpression().replace(lExpr);
equalsCall.getArgumentList().getExpressions()[0].replace(rExpr); equalsCall.getArgumentList().getExpressions()[0].replace(rExpr);
PsiExpression result = (PsiExpression) binaryExpression.replace(equalsCall); PsiExpression result = (PsiExpression) binaryExpression.replace(equalsCall);
if (opSign == JavaTokenType.NE) { if (opSign == JavaTokenType.NE) {
PsiPrefixExpression negation = (PsiPrefixExpression) factory.createExpressionFromText("!a", null); PsiPrefixExpression negation = (PsiPrefixExpression) factory.createExpressionFromText("!a", null);
negation.getOperand().replace(result); negation.getOperand().replace(result);
@ -192,11 +197,12 @@ public class ComparingReferencesInspection extends AbstractBaseJavaLocalInspecti
LOG.error(e); LOG.error(e);
} }
} }
@NotNull @NotNull
public String getFamilyName() { public String getFamilyName() {
return getName(); return getName();
} }
} }
} }

View File

@ -2,8 +2,8 @@
<idea-plugin> <idea-plugin>
<!-- Unique id for this plugin. Must stay constant for the life of the plugin. --> <!-- Unique id for this plugin. Must stay constant for the life of the plugin. -->
<id>org.intelliJ.sdk.codeInspection</id> <id>org.intellij.sdk.codeInspection</id>
<!-- Text to display as name on Preferences/Settings | Plugin page --> <!-- Text to display as name on Preferences/Settings | Plugin page -->
<name>SDK: Comparing References Inspection Sample</name> <name>SDK: Comparing References Inspection Sample</name>
@ -11,10 +11,11 @@
<!-- Evaluates java PSI --> <!-- Evaluates java PSI -->
<depends>com.intellij.modules.java</depends> <depends>com.intellij.modules.java</depends>
<!-- Text to display as description on Preferences/Settings | Plugin page --> <!-- Text to display as description on Preferences/Settings | Plugin page -->
<description> <description>
<![CDATA[ <![CDATA[
Demonstrates implementing a Local Inspection Tool.<br> Adds entries to <b>Preferences | Editor | Inspections | Java | Probable Bugs</b>. Demonstrates implementing a Local Inspection Tool.<br> Adds entries to
<b>Preferences | Editor | Inspections | Java | Probable Bugs</b>.
]]> ]]>
</description> </description>
<change-notes> <change-notes>
@ -31,39 +32,38 @@
<vendor url="https://plugins.jetbrains.com">IntelliJ Platform SDK</vendor> <vendor url="https://plugins.jetbrains.com">IntelliJ Platform SDK</vendor>
<extensions defaultExtensionNs="com.intellij"> <extensions defaultExtensionNs="com.intellij">
<!--
Extend the IntelliJ Platform local inspection type, and connect it to the implementation class in this plugin.
<localInspection> type element is applied within the scope of a file under edit.
It is preferred over <inspectionToolProvider>
@see intellij.platform.resources.LangExtensionPoints
@see com.intellij.codeInspection.InspectionProfileEntry
<!-- Extend the IntelliJ Platform local inspection type, and connect it to the implementation class Attributes:
in this plugin. language= Language ID
<localInspection> type element is applied within the scope of a file under edit. shortName= Not specified, will be computed by the underlying implementation classes.
It is preferred over <inspectionToolProvider> displayName= The string to be shown in the Preferences | Editor | Inspections panel
@see intellij.platform.resources.LangExtensionPoints The displayName gets registered to identify this inspection.
@see com.intellij.codeInspection.InspectionProfileEntry Can be localized using key= and bundle= attributes instead.
Attributes: groupPath= Defines the outermost grouping for this inspection in
language= Language ID the Preferences | Editor | Inspections panel. Not localized.
shortName= Not specified, will be computed by the underlying implementation classes. groupBundle= Name of *.bundle file to translate groupKey.
displayName= The string to be shown in the Preferences | Editor | Inspections panel In this case reuse an IntelliJ Platform bundle file from intellij.platform.resources.en
The displayName gets registered to identify this inspection. groupKey= Key to use for translation subgroup name using groupBundle file.
Can be localized using key= and bundle= attributes instead. In this case reuse the IntelliJ Platform subcategory "Probable bugs"
groupPath= Defines the outermost grouping for this inspection in enabledByDefault= Inspection state when Inspections panel is created.
the Preferences | Editor | Inspections panel. Not localized. level= The default level of error found by this inspection, e.g. INFO, ERROR, etc.
groupBundle= Name of *.bundle file to translate groupKey. @see com.intellij.codeHighlighting.HighlightDisplayLevel
In this case reuse an IntelliJ Platform bundle file from intellij.platform.resources.en implementationClass= FQN of inspection implementation
groupKey= Key to use for translation subgroup name using groupBundle file. -->
In this case reuse the IntelliJ Platform subcategory "Probable bugs"
enabledByDefault= Inspection state when Inspections panel is created.
level= The default level of error found by this inspection, e.g. INFO, ERROR, etc.
@see com.intellij.codeHighlighting.HighlightDisplayLevel
implementationClass= FQN of inspection implementation
-->
<localInspection language="JAVA" <localInspection language="JAVA"
displayName="SDK: '==' or '!=' used instead of 'equals()'" displayName="SDK: '==' or '!=' used instead of 'equals()'"
groupPath="Java" groupPath="Java"
groupBundle="messages.InspectionsBundle" groupBundle="messages.InspectionsBundle"
groupKey="group.names.probable.bugs" groupKey="group.names.probable.bugs"
enabledByDefault="true" enabledByDefault="true"
level="WARNING" level="WARNING"
implementationClass="org.intellij.sdk.codeInspection.ComparingReferencesInspection"/> implementationClass="org.intellij.sdk.codeInspection.ComparingReferencesInspection"/>
</extensions> </extensions>
</idea-plugin> </idea-plugin>

View File

@ -52,14 +52,14 @@ public class ComparingReferencesInspectionTest extends LightJavaCodeInsightFixtu
/** /**
* Test the "==" case * Test the "==" case
*/ */
public void testRelationalEq() throws Throwable { public void testRelationalEq() {
doTest("Eq"); doTest("Eq");
} }
/** /**
* Test the "!=" case * Test the "!=" case
*/ */
public void testRelationalNeq() throws Throwable { public void testRelationalNeq() {
doTest("Neq"); doTest("Neq");
} }

View File

@ -0,0 +1,51 @@
# Conditional Operator Converter [![JetBrains IntelliJ Platform SDK Docs](https://jb.gg/badges/docs.svg)][docs]
*Reference: [Code Intentions in IntelliJ SDK Docs][docs:conditional_operator_intention]*
## Quickstart
Conditional Operator Converter provides an intention for converting the *ternary operator* into the *if* statement, i.e.:
```java
public class X {
void f(boolean isMale) {
String title = isMale ? "Mr." : "Ms.";
System.out.println("title = " + title);
}
}
```
will become:
```java
public class X {
void f(boolean isMale) {
String title;
if (isMale) {
title = "Mr.";
} else {
title = "Ms.";
}
System.out.println("title = " + title);
}
}
```
To invoke the intention action, it is necessary to place the caret on the `?` character of the ternary operator.
The converter in the `isAvailable` method, has defined the token check to match `JavaTokenType.QUEST`, which is `?` character.
### Extension Points
| Name | Implementation | Extension Point Class |
| ------------------------------ | ----------------------------------------------------------------- | ------------------------------------------------------------------ |
| `com.intellij.intentionAction` | [ConditionalOperatorConverter][file:ConditionalOperatorConverter] | [PsiElementBaseIntentionAction][sdk:PsiElementBaseIntentionAction] |
*Reference: [Plugin Extension Points in IntelliJ SDK Docs][docs:ep]*
[docs]: https://www.jetbrains.org/intellij/sdk/docs
[docs:conditional_operator_intention]: https://www.jetbrains.org/intellij/sdk/docs/tutorials/code_intentions.html
[docs:ep]: https://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_extensions.html
[file:ConditionalOperatorConverter]: ./src/main/java/org/intellij/sdk/intention/ConditionalOperatorConverter.java
[sdk:PsiElementBaseIntentionAction]: upsource:///platform/lang-api/src/com/intellij/codeInsight/intention/PsiElementBaseIntentionAction.java

View File

@ -10,7 +10,9 @@ import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException; import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.*; import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/** /**
* Implements an intention action to replace a ternary statement with if-then-else * Implements an intention action to replace a ternary statement with if-then-else
@ -27,51 +29,51 @@ public class ConditionalOperatorConverter extends PsiElementBaseIntentionAction
return "SDK Convert ternary operator to if statement"; return "SDK Convert ternary operator to if statement";
} }
/** /**
* Returns text for name of this family of intentions. It is used to externalize * Returns text for name of this family of intentions. It is used to externalize
* "auto-show" state of intentions. * "auto-show" state of intentions.
* It is also the directory name for the descriptions. * It is also the directory name for the descriptions.
* *
* @return the intention family name. * @return the intention family name.
*/ */
@NotNull @NotNull
public String getFamilyName() { public String getFamilyName() {
return "ConditionalOperatorIntention"; return "ConditionalOperatorIntention";
} }
/** /**
* Checks whether this intention is available at the caret offset in file - the caret * Checks whether this intention is available at the caret offset in file - the caret
* must sit just before a "?" character in a ternary statement. If this condition is met, * must sit just before a "?" character in a ternary statement. If this condition is met,
* this intention's entry is shown in the available intentions list. * this intention's entry is shown in the available intentions list.
* * <p>
* Note: this method must do its checks quickly and return. * Note: this method must do its checks quickly and return.
* *
* @param project a reference to the Project object being edited. * @param project a reference to the Project object being edited.
* @param editor a reference to the object editing the project source * @param editor a reference to the object editing the project source
* @param element a reference to the PSI element currently under the caret * @param element a reference to the PSI element currently under the caret
* @return * @return <ul>
* <ul>
* <li> true if the caret is in a literal string element, so this functionality * <li> true if the caret is in a literal string element, so this functionality
* should be added to the intention menu.</li> * should be added to the intention menu.</li>
* <li> false for all other types of caret positions</li> * <li> false for all other types of caret positions</li>
* </ul> * </ul>
*/ */
public boolean isAvailable(@NotNull Project project, Editor editor, @Nullable PsiElement element) { public boolean isAvailable(@NotNull Project project, Editor editor, @Nullable PsiElement element) {
// Quick sanity check // Quick sanity check
if (element == null) return false; if (element == null) {
return false;
}
// Is this a token of type representing a "?" character? // Is this a token of type representing a "?" character?
if (element instanceof PsiJavaToken) { if (element instanceof PsiJavaToken) {
final PsiJavaToken token = (PsiJavaToken) element; final PsiJavaToken token = (PsiJavaToken) element;
if (token.getTokenType() != JavaTokenType.QUEST) return false; if (token.getTokenType() != JavaTokenType.QUEST) {
return false;
}
// Is this token part of a fully formed conditional, i.e. a ternary? // Is this token part of a fully formed conditional, i.e. a ternary?
if (token.getParent() instanceof PsiConditionalExpression) { if (token.getParent() instanceof PsiConditionalExpression) {
final PsiConditionalExpression conditionalExpression = (PsiConditionalExpression) token.getParent(); final PsiConditionalExpression conditionalExpression = (PsiConditionalExpression) token.getParent();
return conditionalExpression.getThenExpression() != null // Satisfies all criteria; call back invoke method
&& conditionalExpression.getElseExpression() != null;// Satisfies all criteria; call back invoke method return conditionalExpression.getThenExpression() != null && conditionalExpression.getElseExpression() != null;
} }
return false; return false;
} }
@ -84,31 +86,38 @@ public class ConditionalOperatorConverter extends PsiElementBaseIntentionAction
* moved above the if-then-else statement. Called when user selects this intention action * moved above the if-then-else statement. Called when user selects this intention action
* from the available intentions list. * from the available intentions list.
* *
* @param project a reference to the Project object being edited. * @param project a reference to the Project object being edited.
* @param editor a reference to the object editing the project source * @param editor a reference to the object editing the project source
* @param element a reference to the PSI element currently under the caret * @param element a reference to the PSI element currently under the caret
* @throws IncorrectOperationException Thrown by underlying (Psi model) write action context * @throws IncorrectOperationException Thrown by underlying (Psi model) write action context
* when manipulation of the psi tree fails. * when manipulation of the psi tree fails.
* @see ConditionalOperatorConverter#startInWriteAction() * @see ConditionalOperatorConverter#startInWriteAction()
*/ */
public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException { public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element)
throws IncorrectOperationException {
// Get the factory for making new PsiElements, and the code style manager to format new statements // Get the factory for making new PsiElements, and the code style manager to format new statements
final PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory(); final PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
final CodeStyleManager codeStylist = CodeStyleManager.getInstance(project); final CodeStyleManager codeStylist = CodeStyleManager.getInstance(project);
// Get the parent of the "?" element in the ternary statement to find the conditional expression that contains it // Get the parent of the "?" element in the ternary statement to find the conditional expression that contains it
PsiConditionalExpression conditionalExpression = PsiTreeUtil.getParentOfType(element, PsiConditionalExpression.class, false); PsiConditionalExpression conditionalExpression =
PsiTreeUtil.getParentOfType(element, PsiConditionalExpression.class, false);
// Verify the conditional expression exists and has two outcomes in the ternary statement. // Verify the conditional expression exists and has two outcomes in the ternary statement.
if (conditionalExpression == null) return; if (conditionalExpression == null) {
if (conditionalExpression.getThenExpression() == null || conditionalExpression.getElseExpression() == null) return; return;
}
if (conditionalExpression.getThenExpression() == null || conditionalExpression.getElseExpression() == null) {
return;
}
// Keep searching up the Psi Tree in case the ternary is part of a FOR statement. // Keep searching up the Psi Tree in case the ternary is part of a FOR statement.
PsiElement originalStatement = PsiTreeUtil.getParentOfType(conditionalExpression, PsiStatement.class, false); PsiElement originalStatement = PsiTreeUtil.getParentOfType(conditionalExpression, PsiStatement.class, false);
while (originalStatement instanceof PsiForStatement) { while (originalStatement instanceof PsiForStatement) {
originalStatement = PsiTreeUtil.getParentOfType(originalStatement, PsiStatement.class, true); originalStatement = PsiTreeUtil.getParentOfType(originalStatement, PsiStatement.class, true);
} }
if (originalStatement == null) return; if (originalStatement == null) {
return;
}
// If the original statement is a declaration based on a ternary operator, // If the original statement is a declaration based on a ternary operator,
// split the declaration and assignment // split the declaration and assignment
@ -120,12 +129,14 @@ public class ConditionalOperatorConverter extends PsiElementBaseIntentionAction
PsiLocalVariable variable = null; PsiLocalVariable variable = null;
for (PsiElement declaredElement : declaredElements) { for (PsiElement declaredElement : declaredElements) {
if (declaredElement instanceof PsiLocalVariable && if (declaredElement instanceof PsiLocalVariable &&
PsiTreeUtil.isAncestor(declaredElement, conditionalExpression, true)) { PsiTreeUtil.isAncestor(declaredElement, conditionalExpression, true)) {
variable = (PsiLocalVariable) declaredElement; variable = (PsiLocalVariable) declaredElement;
break; break;
} }
} }
if (variable == null) return; if (variable == null) {
return;
}
// Ensure that the variable declaration is not combined with other declarations, and add a mark // Ensure that the variable declaration is not combined with other declarations, and add a mark
variable.normalizeDeclaration(); variable.normalizeDeclaration();
@ -134,7 +145,7 @@ public class ConditionalOperatorConverter extends PsiElementBaseIntentionAction
// Create a new expression to declare the local variable // Create a new expression to declare the local variable
PsiExpressionStatement statement = PsiExpressionStatement statement =
(PsiExpressionStatement) factory.createStatementFromText(variable.getName() + " = 0;", null); (PsiExpressionStatement) factory.createStatementFromText(variable.getName() + " = 0;", null);
statement = (PsiExpressionStatement) codeStylist.reformat(statement); statement = (PsiExpressionStatement) codeStylist.reformat(statement);
// Replace initializer with the ternary expression, making an assignment statement using the ternary // Replace initializer with the ternary expression, making an assignment statement using the ternary
@ -152,7 +163,7 @@ public class ConditionalOperatorConverter extends PsiElementBaseIntentionAction
// Create an IF statement from a string with placeholder elements. // Create an IF statement from a string with placeholder elements.
// This will replace the ternary statement // This will replace the ternary statement
PsiIfStatement newIfStmt = (PsiIfStatement) factory.createStatementFromText("if (true) {a=b;} else {c=d;}",null); PsiIfStatement newIfStmt = (PsiIfStatement) factory.createStatementFromText("if (true) {a=b;} else {c=d;}", null);
newIfStmt = (PsiIfStatement) codeStylist.reformat(newIfStmt); newIfStmt = (PsiIfStatement) codeStylist.reformat(newIfStmt);
// Replace the conditional expression with the one from the original ternary expression // Replace the conditional expression with the one from the original ternary expression
@ -161,25 +172,26 @@ public class ConditionalOperatorConverter extends PsiElementBaseIntentionAction
// Begin building the assignment string for the THEN and ELSE clauses using the // Begin building the assignment string for the THEN and ELSE clauses using the
// parent of the ternary conditional expression // parent of the ternary conditional expression
PsiAssignmentExpression assignmentExpression = PsiTreeUtil.getParentOfType(conditionalExpression, PsiAssignmentExpression.class, false); PsiAssignmentExpression assignmentExpression =
PsiTreeUtil.getParentOfType(conditionalExpression, PsiAssignmentExpression.class, false);
// Get the contents of the assignment expression up to the start of the ternary expression // Get the contents of the assignment expression up to the start of the ternary expression
String exprFrag = assignmentExpression.getLExpression().getText() + assignmentExpression.getOperationSign().getText() ; String exprFrag = assignmentExpression.getLExpression().getText()
+ assignmentExpression.getOperationSign().getText();
// Build the THEN statement string for the new IF statement, // Build the THEN statement string for the new IF statement,
// make a PsiExpressionStatement from the string, and switch the placeholder // make a PsiExpressionStatement from the string, and switch the placeholder
String thenStr = exprFrag + conditionalExpression.getThenExpression().getText() + ";" ; String thenStr = exprFrag + conditionalExpression.getThenExpression().getText() + ";";
PsiExpressionStatement thenStmt = (PsiExpressionStatement) factory.createStatementFromText(thenStr, null); PsiExpressionStatement thenStmt = (PsiExpressionStatement) factory.createStatementFromText(thenStr, null);
( (PsiBlockStatement) newIfStmt.getThenBranch() ).getCodeBlock().getStatements()[0].replace(thenStmt); ((PsiBlockStatement) newIfStmt.getThenBranch()).getCodeBlock().getStatements()[0].replace(thenStmt);
// Build the ELSE statement string for the new IF statement, // Build the ELSE statement string for the new IF statement,
// make a PsiExpressionStatement from the string, and switch the placeholder // make a PsiExpressionStatement from the string, and switch the placeholder
String elseStr = exprFrag + conditionalExpression.getElseExpression().getText() + ";" ; String elseStr = exprFrag + conditionalExpression.getElseExpression().getText() + ";";
PsiExpressionStatement elseStmt = (PsiExpressionStatement) factory.createStatementFromText(elseStr, null); PsiExpressionStatement elseStmt = (PsiExpressionStatement) factory.createStatementFromText(elseStr, null);
( (PsiBlockStatement) newIfStmt.getElseBranch() ).getCodeBlock().getStatements()[0].replace(elseStmt); ((PsiBlockStatement) newIfStmt.getElseBranch()).getCodeBlock().getStatements()[0].replace(elseStmt);
// Replace the entire original statement with the new IF // Replace the entire original statement with the new IF
newIfStmt = (PsiIfStatement) originalStatement.replace(newIfStmt); newIfStmt = (PsiIfStatement) originalStatement.replace(newIfStmt);
} }
/** /**
@ -191,8 +203,8 @@ public class ConditionalOperatorConverter extends PsiElementBaseIntentionAction
* <li> false if this intention action will start a write action</li> * <li> false if this intention action will start a write action</li>
* </ul> * </ul>
*/ */
public boolean startInWriteAction() {return true;} public boolean startInWriteAction() {
return true;
}
} }

View File

@ -2,7 +2,7 @@
<idea-plugin> <idea-plugin>
<!-- Unique id for this plugin. Must stay constant for the life of the plugin. --> <!-- Unique id for this plugin. Must stay constant for the life of the plugin. -->
<id>org.intellij.sdk.intention</id> <id>org.intellij.sdk.intention</id>
<!-- Text to display as name on Preferences/Settings | Plugin page --> <!-- Text to display as name on Preferences/Settings | Plugin page -->
@ -12,10 +12,11 @@
<depends>com.intellij.modules.java</depends> <depends>com.intellij.modules.java</depends>
<depends>com.intellij.modules.platform</depends> <depends>com.intellij.modules.platform</depends>
<!-- Text to display as description on Preferences/Settings | Plugin page --> <!-- Text to display as description on Preferences/Settings | Plugin page -->
<description> <description>
<![CDATA[ <![CDATA[
Intention action that suggests converting a ternary operator into an 'if' block.<br>Adds entry to <b>Preferences | Editor | Intentions | SDK Intentions<b>. Intention action that suggests converting a ternary operator into an 'if' block.<br>
Adds entry to <b>Preferences | Editor | Intentions | SDK Intentions<b>.
]]> ]]>
</description> </description>
<change-notes> <change-notes>

43
editor_basics/README.md Normal file
View File

@ -0,0 +1,43 @@
# Editor Sample Project [![JetBrains IntelliJ Platform SDK Docs](https://jb.gg/badges/docs.svg)][docs]
*Reference: [Basics of Working with the Editor in IntelliJ SDK Docs][docs:editor_basics]*
## Quickstart
Editor Sample Project provides a [TypedHandlerDelegate][sdk:TypedHandlerDelegate] implementation, which inserts `editor_basics` on the top of the edited document any time user types a character.
In addition, three actions are available in the Editor context menu:
- Editor Replace Text - replaces the selected text with `editor_basics`,
- Editor Add Caret - adds extra caret below the current one,
- Caret Position - shows message dialog with information about the caret position.
### Extension Points
| Name | Implementation | Extension Point Class |
| --------------------------- | ------------------------------------- | ------------------------------------------------ |
| `com.intellij.typedHandler` | [MyTypedHandler][file:MyTypedHandler] | [TypedHandlerDelegate][sdk:TypedHandlerDelegate] |
*Reference: [Plugin Extension Points in IntelliJ SDK Docs][docs:ep]*
### Actions
| ID | Implementation | Extension Point Class |
| ------------------------------------------ | ----------------------------------------------------------- | ------------------------ |
| `EditorBasics.EditorIllustrationAction` | [EditorIllustrationAction][file:EditorIllustrationAction] | [AnAction][sdk:AnAction] |
| `EditorBasics.EditorHandlerIllustration` | [EditorHandlerIllustration][file:EditorHandlerIllustration] | [AnAction][sdk:AnAction] |
| `EditorBasics.LogicalPositionIllustration` | [EditorAreaIllustration][file:EditorAreaIllustration] | [AnAction][sdk:AnAction] |
*Reference: [Action System in IntelliJ SDK Docs][docs:actions]*
[docs]: https://www.jetbrains.org/intellij/sdk/docs
[docs:actions]: https://www.jetbrains.org/intellij/sdk/docs/basics/action_system.html
[docs:editor_basics]: https://www.jetbrains.org/intellij/sdk/docs/tutorials/editor_basics.html
[docs:ep]: https://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_extensions.html
[file:MyTypedHandler]: ./src/main/java/org/intellij/sdk/editor/MyTypedHandler.java
[file:EditorIllustrationAction]: ./src/main/java/org/intellij/sdk/editor/EditorIllustrationAction.java
[file:EditorHandlerIllustration]: ./src/main/java/org/intellij/sdk/editor/EditorHandlerIllustration.java
[file:EditorAreaIllustration]: ./src/main/java/org/intellij/sdk/editor/EditorAreaIllustration.java
[sdk:TypedHandlerDelegate]: upsource:///platform/lang-api/src/com/intellij/codeInsight/editorActions/TypedHandlerDelegate.java
[sdk:AnAction]: upsource:///platform/editor-ui-api/src/com/intellij/openapi/actionSystem/AnAction.java

View File

@ -7,5 +7,7 @@ import com.intellij.openapi.util.IconLoader;
import javax.swing.*; import javax.swing.*;
public class SdkIcons { public class SdkIcons {
public static final Icon Sdk_default_icon = IconLoader.getIcon("/icons/sdk_16.svg"); public static final Icon Sdk_default_icon = IconLoader.getIcon("/icons/sdk_16.svg");
} }

View File

@ -17,10 +17,11 @@ import org.jetbrains.annotations.NotNull;
* @see com.intellij.openapi.actionSystem.AnAction * @see com.intellij.openapi.actionSystem.AnAction
*/ */
public class EditorAreaIllustration extends AnAction { public class EditorAreaIllustration extends AnAction {
/** /**
* Displays a message with information about the current caret. * Displays a message with information about the current caret.
* @param e Event related to this action *
* @param e Event related to this action
*/ */
@Override @Override
public void actionPerformed(@NotNull final AnActionEvent e) { public void actionPerformed(@NotNull final AnActionEvent e) {
@ -38,12 +39,13 @@ public class EditorAreaIllustration extends AnAction {
"Offset: " + caretOffset; "Offset: " + caretOffset;
Messages.showInfoMessage(report, "Caret Parameters Inside The Editor"); Messages.showInfoMessage(report, "Caret Parameters Inside The Editor");
} }
/** /**
* Sets visibility and enables this action menu item if: * Sets visibility and enables this action menu item if:
* A project is open, * A project is open,
* An editor is active, * An editor is active,
* @param e Event related to this action *
* @param e Event related to this action
*/ */
@Override @Override
public void update(@NotNull final AnActionEvent e) { public void update(@NotNull final AnActionEvent e) {
@ -53,4 +55,5 @@ public class EditorAreaIllustration extends AnAction {
//Set visibility only in case of existing project and editor //Set visibility only in case of existing project and editor
e.getPresentation().setEnabledAndVisible(project != null && editor != null); e.getPresentation().setEnabledAndVisible(project != null && editor != null);
} }
} }

View File

@ -2,9 +2,13 @@
package org.intellij.sdk.editor; package org.intellij.sdk.editor;
import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.actionSystem.*; import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
import com.intellij.openapi.editor.actionSystem.EditorActionManager;
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -14,9 +18,11 @@ import org.jetbrains.annotations.NotNull;
* @see com.intellij.openapi.actionSystem.AnAction * @see com.intellij.openapi.actionSystem.AnAction
*/ */
public class EditorHandlerIllustration extends AnAction { public class EditorHandlerIllustration extends AnAction {
/** /**
* Clones a new caret at a higher Logical Position line number. * Clones a new caret at a higher Logical Position line number.
* @param e Event related to this action *
* @param e Event related to this action
*/ */
@Override @Override
public void actionPerformed(@NotNull final AnActionEvent e) { public void actionPerformed(@NotNull final AnActionEvent e) {
@ -25,17 +31,19 @@ public class EditorHandlerIllustration extends AnAction {
// Get the action manager in order to get the necessary action handler... // Get the action manager in order to get the necessary action handler...
final EditorActionManager actionManager = EditorActionManager.getInstance(); final EditorActionManager actionManager = EditorActionManager.getInstance();
// Get the action handler registered to clone carets // Get the action handler registered to clone carets
final EditorActionHandler actionHandler = actionManager.getActionHandler(IdeActions.ACTION_EDITOR_CLONE_CARET_BELOW); final EditorActionHandler actionHandler =
actionManager.getActionHandler(IdeActions.ACTION_EDITOR_CLONE_CARET_BELOW);
// Clone one caret below the active caret // Clone one caret below the active caret
actionHandler.execute(editor, editor.getCaretModel().getPrimaryCaret(), e.getDataContext()); actionHandler.execute(editor, editor.getCaretModel().getPrimaryCaret(), e.getDataContext());
} }
/** /**
* Enables and sets visibility of this action menu item if: * Enables and sets visibility of this action menu item if:
* A project is open, * A project is open,
* An editor is active, * An editor is active,
* At least one caret exists * At least one caret exists
* @param e Event related to this action *
* @param e Event related to this action
*/ */
@Override @Override
public void update(@NotNull final AnActionEvent e) { public void update(@NotNull final AnActionEvent e) {
@ -49,5 +57,5 @@ public class EditorHandlerIllustration extends AnAction {
} }
e.getPresentation().setEnabledAndVisible(menuAllowed); e.getPresentation().setEnabledAndVisible(menuAllowed);
} }
} }

View File

@ -18,10 +18,11 @@ import org.jetbrains.annotations.NotNull;
* @see com.intellij.openapi.actionSystem.AnAction * @see com.intellij.openapi.actionSystem.AnAction
*/ */
public class EditorIllustrationAction extends AnAction { public class EditorIllustrationAction extends AnAction {
/** /**
* Replaces the run of text selected by the primary caret with a fixed string. * Replaces the run of text selected by the primary caret with a fixed string.
* @param e Event related to this action *
* @param e Event related to this action
*/ */
@Override @Override
public void actionPerformed(@NotNull final AnActionEvent e) { public void actionPerformed(@NotNull final AnActionEvent e) {
@ -37,18 +38,19 @@ public class EditorIllustrationAction extends AnAction {
// Replace the selection with a fixed string. // Replace the selection with a fixed string.
// Must do this document change in a write action context. // Must do this document change in a write action context.
WriteCommandAction.runWriteCommandAction(project, () -> WriteCommandAction.runWriteCommandAction(project, () ->
document.replaceString(start, end, "editor_basics") document.replaceString(start, end, "editor_basics")
); );
// De-select the text range that was just replaced // De-select the text range that was just replaced
primaryCaret.removeSelection(); primaryCaret.removeSelection();
} }
/** /**
* Sets visibility and enables this action menu item if: * Sets visibility and enables this action menu item if:
* A project is open, * A project is open,
* An editor is active, * An editor is active,
* Some characters are selected * Some characters are selected
* @param e Event related to this action *
* @param e Event related to this action
*/ */
@Override @Override
public void update(@NotNull final AnActionEvent e) { public void update(@NotNull final AnActionEvent e) {
@ -56,6 +58,9 @@ public class EditorIllustrationAction extends AnAction {
final Project project = e.getProject(); final Project project = e.getProject();
final Editor editor = e.getData(CommonDataKeys.EDITOR); final Editor editor = e.getData(CommonDataKeys.EDITOR);
// Set visibility and enable only in case of existing project and editor and if a selection exists // Set visibility and enable only in case of existing project and editor and if a selection exists
e.getPresentation().setEnabledAndVisible( project != null && editor != null && editor.getSelectionModel().hasSelection() ); e.getPresentation().setEnabledAndVisible(
project != null && editor != null && editor.getSelectionModel().hasSelection()
);
} }
} }

View File

@ -17,6 +17,7 @@ import org.jetbrains.annotations.NotNull;
* Document changes are made in the context of a write action. * Document changes are made in the context of a write action.
*/ */
class MyTypedHandler extends TypedHandlerDelegate { class MyTypedHandler extends TypedHandlerDelegate {
@NotNull @NotNull
@Override @Override
public Result charTyped(char c, @NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) { public Result charTyped(char c, @NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
@ -28,4 +29,5 @@ class MyTypedHandler extends TypedHandlerDelegate {
WriteCommandAction.runWriteCommandAction(project, runnable); WriteCommandAction.runWriteCommandAction(project, runnable);
return Result.STOP; return Result.STOP;
} }
} }

View File

@ -2,20 +2,22 @@
<idea-plugin> <idea-plugin>
<!-- Unique id for this plugin. Must stay constant for the life of the plugin. --> <!-- Unique id for this plugin. Must stay constant for the life of the plugin. -->
<id>org.intellij.sdk.editor</id> <id>org.intellij.sdk.editor</id>
<!-- Text to display as name on Preferences/Settings | Plugin page --> <!-- Text to display as name on Preferences/Settings | Plugin page -->
<name>SDK: Editor Sample Project</name> <name>SDK: Editor Sample</name>
<!-- Product and plugin compatibility requirements --> <!-- Product and plugin compatibility requirements -->
<depends>com.intellij.modules.platform</depends> <depends>com.intellij.modules.platform</depends>
<!-- Text to display as description on Preferences/Settings | Plugin page --> <!-- Text to display as description on Preferences/Settings | Plugin page -->
<description> <description>
<![CDATA[ <![CDATA[
Illustrates various basic Editor APIs. Requires at least project to be open, and a file open in the editor to see the menu items this plugin adds to the editor popup menu.<br>Mouse over each of this plugin's menu items to see hints in the lower left corner of the IDE. Illustrates various basic Editor APIs. Requires at least project to be open, and a file open in the editor
]]> to see the menu items this plugin adds to the editor popup menu.<br>Mouse over each of this plugin's menu items
to see hints in the lower left corner of the IDE.
]]>
</description> </description>
<change-notes> <change-notes>
<![CDATA[ <![CDATA[

27
facet_basics/README.md Normal file
View File

@ -0,0 +1,27 @@
# Facet Basics [![JetBrains IntelliJ Platform SDK Docs](https://jb.gg/badges/docs.svg)][docs]
*Reference: [Facet in IntelliJ SDK Docs][docs:facet_basics]*
## Quickstart
Facets extend base IDE features with additional frameworks support by providing additional libraries, dependencies, technologies, and UI elements for configuring framework-specific settings.
Facet Basics represents configuration specific for a particular framework or technology, associated with a module.
SDK Facet is available to use in the `Project Settings > Facets` section.
It allows us to specify any configuration specified by the `FacetConfiguration` implementation - path to the SDK in this case.
### Extension Points
| Name | Implementation | Extension Point Class |
| ------------------------ | ----------------------------------- | -------------------------- |
| `com.intellij.facetType` | [DemoFacetType][file:DemoFacetType] | [FacetType][sdk:FacetType] |
*Reference: [Plugin Extension Points in IntelliJ SDK Docs][docs:ep]*
[docs]: https://www.jetbrains.org/intellij/sdk/docs
[docs:facet_basics]: https://www.jetbrains.org/intellij/sdk/docs/reference_guide/project_model/facet.html
[docs:ep]: https://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_extensions.html
[file:DemoFacetType]: ./src/main/java/org/intellij/sdk/facet/DemoFacetType.java
[sdk:FacetType]: upsource:///platform/lang-api/src/com/intellij/facet/FacetType.java

View File

@ -7,5 +7,7 @@ import com.intellij.openapi.util.IconLoader;
import javax.swing.*; import javax.swing.*;
public class SdkIcons { public class SdkIcons {
public static final Icon Sdk_default_icon = IconLoader.getIcon("/icons/sdk_16.svg"); public static final Icon Sdk_default_icon = IconLoader.getIcon("/icons/sdk_16.svg");
} }

View File

@ -20,6 +20,7 @@ public class DemoFacetConfiguration implements FacetConfiguration, PersistentSta
/** /**
* Called by the IntelliJ Platform when saving this facet's state persistently. * Called by the IntelliJ Platform when saving this facet's state persistently.
*
* @return a component state. All properties, public and annotated fields are serialized. * @return a component state. All properties, public and annotated fields are serialized.
* Only values which differ from default (i.e. the value of newly instantiated class) are serialized. * Only values which differ from default (i.e. the value of newly instantiated class) are serialized.
* {@code null} value indicates that the returned state won't be stored, and * {@code null} value indicates that the returned state won't be stored, and
@ -30,7 +31,7 @@ public class DemoFacetConfiguration implements FacetConfiguration, PersistentSta
public DemoFacetState getState() { public DemoFacetState getState() {
return myFacetState; return myFacetState;
} }
/** /**
* Called by the IntelliJ Platform when this facet's state is loaded. * Called by the IntelliJ Platform when this facet's state is loaded.
* The method can and will be called several times, if * The method can and will be called several times, if
@ -40,7 +41,7 @@ public class DemoFacetConfiguration implements FacetConfiguration, PersistentSta
public void loadState(@NotNull DemoFacetState state) { public void loadState(@NotNull DemoFacetState state) {
myFacetState = state; myFacetState = state;
} }
/** /**
* Creates a set of editor tabs for this facet, potentially one per context. * Creates a set of editor tabs for this facet, potentially one per context.
* *
@ -51,7 +52,7 @@ public class DemoFacetConfiguration implements FacetConfiguration, PersistentSta
@Override @Override
public FacetEditorTab[] createEditorTabs(FacetEditorContext context, FacetValidatorsManager manager) { public FacetEditorTab[] createEditorTabs(FacetEditorContext context, FacetValidatorsManager manager) {
return new FacetEditorTab[]{ return new FacetEditorTab[]{
new DemoFacetEditorTab(myFacetState, context, manager) new DemoFacetEditorTab(myFacetState, context, manager)
}; };
} }

View File

@ -6,7 +6,6 @@ import com.intellij.facet.ui.FacetEditorContext;
import com.intellij.facet.ui.FacetEditorTab; import com.intellij.facet.ui.FacetEditorTab;
import com.intellij.facet.ui.FacetValidatorsManager; import com.intellij.facet.ui.FacetValidatorsManager;
import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -20,10 +19,10 @@ import java.awt.*;
*/ */
public class DemoFacetEditorTab extends FacetEditorTab { public class DemoFacetEditorTab extends FacetEditorTab {
private static final String FACET_PANEL_PROMPT = "Path To SDK: "; private static final String FACET_PANEL_PROMPT = "Path To SDK: ";
private final DemoFacetState mySettings; private final DemoFacetState mySettings;
private final JTextField myPath; private final JTextField myPath;
/** /**
* Only org.intellij.sdk.facet.DemoFacetState is captured so it can be updated per user changes * Only org.intellij.sdk.facet.DemoFacetState is captured so it can be updated per user changes
* in the EditorTab. * in the EditorTab.
@ -33,11 +32,12 @@ public class DemoFacetEditorTab extends FacetEditorTab {
* @param validator Facet validator manager, can be used to get and apply a custom validator for * @param validator Facet validator manager, can be used to get and apply a custom validator for
* this facet. * this facet.
*/ */
public DemoFacetEditorTab(@NotNull DemoFacetState state, @NotNull FacetEditorContext context, @NotNull FacetValidatorsManager validator) { public DemoFacetEditorTab(@NotNull DemoFacetState state, @NotNull FacetEditorContext context,
@NotNull FacetValidatorsManager validator) {
mySettings = state; mySettings = state;
myPath = new JTextField(state.getDemoFacetState()); myPath = new JTextField(state.getDemoFacetState());
} }
/** /**
* Provides the JPanel displayed in the Preferences | Facet UI * Provides the JPanel displayed in the Preferences | Facet UI
* *
@ -53,8 +53,8 @@ public class DemoFacetEditorTab extends FacetEditorTab {
facetPanel.add(top, BorderLayout.NORTH); facetPanel.add(top, BorderLayout.NORTH);
return facetPanel; return facetPanel;
} }
/** /**
* @return the name of this facet for display in this editor tab. * @return the name of this facet for display in this editor tab.
*/ */
@ -63,7 +63,7 @@ public class DemoFacetEditorTab extends FacetEditorTab {
public String getDisplayName() { public String getDisplayName() {
return DemoFacetType.FACET_NAME; return DemoFacetType.FACET_NAME;
} }
/** /**
* Determines if the facet state entered in the UI differs * Determines if the facet state entered in the UI differs
* from the currently stored state. * from the currently stored state.
@ -76,7 +76,7 @@ public class DemoFacetEditorTab extends FacetEditorTab {
public boolean isModified() { public boolean isModified() {
return !StringUtil.equals(mySettings.getDemoFacetState(), myPath.getText().trim()); return !StringUtil.equals(mySettings.getDemoFacetState(), myPath.getText().trim());
} }
/** /**
* Stores new facet state (text) entered by the user. * Stores new facet state (text) entered by the user.
* Called when {@link #isModified()} returns true. * Called when {@link #isModified()} returns true.
@ -92,7 +92,7 @@ public class DemoFacetEditorTab extends FacetEditorTab {
throw new ConfigurationException(e.toString()); throw new ConfigurationException(e.toString());
} }
} }
/** /**
* Copies current org.intellij.sdk.facet.DemoFacetState into the myPath UI element. * Copies current org.intellij.sdk.facet.DemoFacetState into the myPath UI element.
* This method is called each time this editor tab is needed for display. * This method is called each time this editor tab is needed for display.
@ -101,5 +101,5 @@ public class DemoFacetEditorTab extends FacetEditorTab {
public void reset() { public void reset() {
myPath.setText(mySettings.getDemoFacetState()); myPath.setText(mySettings.getDemoFacetState());
} }
} }

View File

@ -9,6 +9,7 @@ import org.jetbrains.annotations.NotNull;
* In this case it is just a string containing a path to an SDK. * In this case it is just a string containing a path to an SDK.
*/ */
public class DemoFacetState { public class DemoFacetState {
static final String DEMO_FACET_INIT_PATH = ""; static final String DEMO_FACET_INIT_PATH = "";
public String myPathToSdk; public String myPathToSdk;

View File

@ -2,11 +2,14 @@
package org.intellij.sdk.facet; package org.intellij.sdk.facet;
import com.intellij.facet.*; import com.intellij.facet.Facet;
import com.intellij.facet.FacetType;
import com.intellij.facet.FacetTypeId;
import com.intellij.openapi.module.Module; import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleType; import com.intellij.openapi.module.ModuleType;
import icons.SdkIcons; import icons.SdkIcons;
import org.jetbrains.annotations.*; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*; import javax.swing.*;
@ -16,6 +19,7 @@ import javax.swing.*;
* Allows application of this facet to all ModuleTypes. * Allows application of this facet to all ModuleTypes.
*/ */
public class DemoFacetType extends FacetType<DemoFacet, DemoFacetConfiguration> { public class DemoFacetType extends FacetType<DemoFacet, DemoFacetConfiguration> {
public static final String FACET_ID = "DEMO_FACET_ID"; public static final String FACET_ID = "DEMO_FACET_ID";
public static final String FACET_NAME = "SDK Facet"; public static final String FACET_NAME = "SDK Facet";
public static final FacetTypeId<DemoFacet> DEMO_FACET_TYPE_ID = new FacetTypeId<>(FACET_ID); public static final FacetTypeId<DemoFacet> DEMO_FACET_TYPE_ID = new FacetTypeId<>(FACET_ID);
@ -47,4 +51,5 @@ public class DemoFacetType extends FacetType<DemoFacet, DemoFacetConfiguration>
public Icon getIcon() { public Icon getIcon() {
return SdkIcons.Sdk_default_icon; return SdkIcons.Sdk_default_icon;
} }
} }

View File

@ -2,7 +2,7 @@
<idea-plugin> <idea-plugin>
<!-- Unique id for this plugin. Must stay constant for the life of the plugin. --> <!-- Unique id for this plugin. Must stay constant for the life of the plugin. -->
<id>org.intellij.sdk.facet</id> <id>org.intellij.sdk.facet</id>
<!-- Text to display as name on Preferences/Settings | Plugin page --> <!-- Text to display as name on Preferences/Settings | Plugin page -->
@ -11,10 +11,11 @@
<!-- Product and plugin compatibility requirements --> <!-- Product and plugin compatibility requirements -->
<depends>com.intellij.modules.lang</depends> <depends>com.intellij.modules.lang</depends>
<!-- Text to display as description on Preferences/Settings | Plugin page --> <!-- Text to display as description on Preferences/Settings | Plugin page -->
<description> <description>
<![CDATA[ <![CDATA[
Demonstrates implementing the custom Facet pattern.<br>Adds <em>SDK Facet</em> to the Project Structure | Project Settings | Facets menu. Demonstrates implementing the custom Facet pattern.<br>Adds <em>SDK Facet</em>
to the Project Structure | Project Settings | Facets menu.
]]> ]]>
</description> </description>
<change-notes> <change-notes>

View File

@ -0,0 +1,24 @@
# Framework Sample Project [![JetBrains IntelliJ Platform SDK Docs](https://jb.gg/badges/docs.svg)][docs]
*Reference: [Supporting Frameworks in IntelliJ SDK Docs][docs:supporting_frameworks]*
## Quickstart
Framework Sample Project provides a [DemoFramework][file:DemoFramework], which allows embedding framework support within the Project Wizard.
This sample implementation adds a new *SDK Demo Framework* support in the Java type project.
### Extension Points
| Name | Implementation | Extension Point Class |
| ----------------------------- | ----------------------------------- | -------------------------------------- |
| `com.intellij.framework.type` | [DemoFramework][file:DemoFramework] | [FrameworkTypeEx][sdk:FrameworkTypeEx] |
*Reference: [Plugin Extension Points in IntelliJ SDK Docs][docs:ep]*
[docs]: https://www.jetbrains.org/intellij/sdk/docs
[docs:supporting_frameworks]: https://jetbrains.org/intellij/sdk/docs/tutorials/framework.html
[docs:ep]: https://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_extensions.html
[file:DemoFramework]: ./src/main/java/org/intellij/sdk/framework/DemoFramework.java
[sdk:FrameworkTypeEx]: upsource:///java/idea-ui/src/com/intellij/framework/FrameworkTypeEx.java

View File

@ -7,5 +7,7 @@ import com.intellij.openapi.util.IconLoader;
import javax.swing.*; import javax.swing.*;
public class SdkIcons { public class SdkIcons {
public static final Icon Sdk_default_icon = IconLoader.getIcon("/icons/sdk_16.svg"); public static final Icon Sdk_default_icon = IconLoader.getIcon("/icons/sdk_16.svg");
} }

View File

@ -71,4 +71,5 @@ public class DemoFramework extends FrameworkTypeEx {
public Icon getIcon() { public Icon getIcon() {
return SdkIcons.Sdk_default_icon; return SdkIcons.Sdk_default_icon;
} }
} }

View File

@ -2,19 +2,20 @@
<idea-plugin> <idea-plugin>
<!-- Unique id for this plugin. Must stay constant for the life of the plugin. --> <!-- Unique id for this plugin. Must stay constant for the life of the plugin. -->
<id>org.intellij.sdk.framework</id> <id>org.intellij.sdk.framework</id>
<!-- Text to display as name on Preferences/Settings | Plugin page --> <!-- Text to display as name on Preferences/Settings | Plugin page -->
<name>SDK: Framework Sample Project</name> <name>SDK: Framework Sample</name>
<!-- Product and plugin compatibility requirements --> <!-- Product and plugin compatibility requirements -->
<depends>com.intellij.modules.java</depends> <depends>com.intellij.modules.java</depends>
<!-- Text to display as description on Preferences/Settings | Plugin page --> <!-- Text to display as description on Preferences/Settings | Plugin page -->
<description> <description>
<![CDATA[ <![CDATA[
Demonstrates basic Framework support. <br>Adds <i>SDK Demo Framework</i> to <b>File | New | Project | IntelliJ Platform Plugin</b> Demonstrates basic Framework support. <br>Adds <i>SDK Demo Framework</i> to
<b>File | New | Project | IntelliJ Platform Plugin</b>
]]> ]]>
</description> </description>
<change-notes> <change-notes>

View File

@ -0,0 +1,25 @@
# Inspection Sample Project [![JetBrains IntelliJ Platform SDK Docs](https://jb.gg/badges/docs.svg)][docs]
*Reference: [Code Inspections in IntelliJ SDK Docs][docs:code_inspections]*
## Quickstart
Inspection Sample Project implements a simple local inspection producing warnings for the regular plain text files.
Inspection, enabled by default, uses a visitor passing all PSI elements with no error reporting.
### Extension Points
| Name | Implementation | Extension Point Class |
| ------------------------------ | --------------------------------------------- | ---------------------------------------------- |
| `com.intellij.localInspection` | [DemoCodeInspection][file:DemoCodeInspection] | [LocalInspectionTool][sdk:LocalInspectionTool] |
*Reference: [Plugin Extension Points in IntelliJ SDK Docs][docs:ep]*
[docs]: https://www.jetbrains.org/intellij/sdk/docs
[docs:code_inspections]: https://jetbrains.org/intellij/sdk/docs/tutorials/code_inspections.html
[docs:ep]: https://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_extensions.html
[file:DemoCodeInspection]: ./src/main/java/org/intellij/sdk/inspection/DemoCodeInspection.java
[sdk:LocalInspectionTool]: upsource:///platform/analysis-api/src/com/intellij/codeInspection/LocalInspectionTool.java

View File

@ -7,7 +7,7 @@ import com.intellij.codeInspection.ProblemsHolder;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class DemoCodeInspection extends LocalInspectionTool { public class DemoCodeInspection extends LocalInspectionTool {
/** /**
* This method is overridden to provide a custom visitor * This method is overridden to provide a custom visitor
* The visitor must not be recursive and must be thread-safe. * The visitor must not be recursive and must be thread-safe.
@ -21,4 +21,5 @@ public class DemoCodeInspection extends LocalInspectionTool {
public DemoInspectionVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) { public DemoInspectionVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
return new DemoInspectionVisitor(); return new DemoInspectionVisitor();
} }
} }

View File

@ -8,6 +8,7 @@ import com.intellij.psi.PsiPlainTextFile;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class DemoInspectionVisitor extends PsiElementVisitor { public class DemoInspectionVisitor extends PsiElementVisitor {
@Override @Override
public void visitElement(@NotNull PsiElement element) { public void visitElement(@NotNull PsiElement element) {
super.visitElement(element); super.visitElement(element);
@ -17,4 +18,5 @@ public class DemoInspectionVisitor extends PsiElementVisitor {
public void visitPlainTextFile(@NotNull PsiPlainTextFile file) { public void visitPlainTextFile(@NotNull PsiPlainTextFile file) {
super.visitPlainTextFile(file); super.visitPlainTextFile(file);
} }
} }

View File

@ -2,19 +2,20 @@
<idea-plugin> <idea-plugin>
<!-- Unique id for this plugin. Must stay constant for the life of the plugin. --> <!-- Unique id for this plugin. Must stay constant for the life of the plugin. -->
<id>org.intellij.sdk.inspection</id> <id>org.intellij.sdk.inspection</id>
<!-- Text to display as name on Preferences/Settings | Plugin page --> <!-- Text to display as name on Preferences/Settings | Plugin page -->
<name>SDK: Inspection Sample Project</name> <name>SDK: Inspection Sample</name>
<!-- Product and plugin compatibility requirements - this inspects text, which is part of the lang module --> <!-- Product and plugin compatibility requirements - this inspects text, which is part of the lang module -->
<depends>com.intellij.modules.lang</depends> <depends>com.intellij.modules.lang</depends>
<!-- Text to display as description on Preferences/Settings | Plugin page --> <!-- Text to display as description on Preferences/Settings | Plugin page -->
<description> <description>
<![CDATA[ <![CDATA[
Basic example of working with code inspections. Adds entry to <b>Preferences | Editor | Inspections | SDK | Example Tools</b>. Basic example of working with code inspections. Adds entry to
<b>Preferences | Editor | Inspections | SDK | Example Tools</b>.
]]> ]]>
</description> </description>
<change-notes> <change-notes>
@ -31,27 +32,28 @@
<vendor url="https://plugins.jetbrains.com">IntelliJ Platform SDK</vendor> <vendor url="https://plugins.jetbrains.com">IntelliJ Platform SDK</vendor>
<extensions defaultExtensionNs="com.intellij"> <extensions defaultExtensionNs="com.intellij">
<!--
Extend the IntelliJ Platform local inspection type, and connect it to the implementation class in this plugin.
<!-- Extend the IntelliJ Platform local inspection type, and connect it to the implementation class <localInspection> type element is applied within the scope of a file under edit.
in this plugin. It is preferred over <inspectionToolProvider>
<localInspection> type element is applied within the scope of a file under edit. @see intellij.platform.resources.LangExtensionPoints
It is preferred over <inspectionToolProvider> @see com.intellij.codeInspection.InspectionProfileEntry
@see intellij.platform.resources.LangExtensionPoints
@see com.intellij.codeInspection.InspectionProfileEntry Attributes:
Attributes: language= Language ID
language= Language ID shortName= Not specified, will be computed by the underlying implementation classes.
shortName= Not specified, will be computed by the underlying implementation classes. displayName= The string to be shown in the Preferences | Editor | Inspections panel
displayName= The string to be shown in the Preferences | Editor | Inspections panel The displayName gets registered to identify this inspection.
The displayName gets registered to identify this inspection. Can be localized using key= and bundle= attributes instead.
Can be localized using key= and bundle= attributes instead. groupPath= Defines the outermost grouping for this inspection in
groupPath= Defines the outermost grouping for this inspection in the Preferences | Editor | Inspections panel. Not localized.
the Preferences | Editor | Inspections panel. Not localized. groupName= The subgroup containing this inspection. Not localized.
groupName= The subgroup containing this inspection. Not localized. enabledByDefault= Inspection state when Inspections panel is created.
enabledByDefault= Inspection state when Inspections panel is created. level= The default level of error found by this inspection, e.g. INFO, ERROR, etc.
level= The default level of error found by this inspection, e.g. INFO, ERROR, etc. @see com.intellij.codeHighlighting.HighlightDisplayLevel
@see com.intellij.codeHighlighting.HighlightDisplayLevel implementationClass= FQN of inspection implementation
implementationClass= FQN of inspection implementation -->
-->
<localInspection language="TEXT" <localInspection language="TEXT"
displayName="Inspection basics" displayName="Inspection basics"
groupName="Example inspections" groupName="Example inspections"
@ -60,4 +62,5 @@
level="WARNING" level="WARNING"
implementationClass="org.intellij.sdk.inspection.DemoCodeInspection"/> implementationClass="org.intellij.sdk.inspection.DemoCodeInspection"/>
</extensions> </extensions>
</idea-plugin> </idea-plugin>

25
kotlin_demo/README.md Normal file
View File

@ -0,0 +1,25 @@
# Kotlin Demo [![JetBrains IntelliJ Platform SDK Docs](https://jb.gg/badges/docs.svg)][docs]
*Reference: [Kotlin for Plugin Developers in IntelliJ SDK Docs][docs:kotlin]*
## Quickstart
Simple Kotlin Demo project is an example of the Kotlin-based plugin that provides the most straightforward action implemented by the [HelloAction.kt][file:HelloAction] Kotlin class.
Action, added to the Main Menu, shows a message dialog when invoked.
### Actions
| ID | Implementation | Extension Point Class |
| ---------------- | ------------------------------- | ------------------------ |
| `MyPlugin.Hello` | [HelloAction][file:HelloAction] | [AnAction][sdk:AnAction] |
*Reference: [Action System in IntelliJ SDK Docs][docs:actions]*
[docs]: https://www.jetbrains.org/intellij/sdk/docs
[docs:actions]: https://www.jetbrains.org/intellij/sdk/docs/basics/action_system.html
[docs:kotlin]: https://jetbrains.org/intellij/sdk/docs/tutorials/kotlin.html
[file:HelloAction]: ./src/main/kotlin/org/intellij/sdk/kotlin/HelloAction.kt
[sdk:AnAction]: upsource:///platform/editor-ui-api/src/com/intellij/openapi/actionSystem/AnAction.java

View File

@ -13,4 +13,5 @@ class HelloAction : DumbAwareAction() {
val project = event.getData(PlatformDataKeys.PROJECT) val project = event.getData(PlatformDataKeys.PROJECT)
Messages.showMessageDialog(project, "Hello from Kotlin!", "Greeting", Messages.getInformationIcon()) Messages.showMessageDialog(project, "Hello from Kotlin!", "Greeting", Messages.getInformationIcon())
} }
} }

View File

@ -2,7 +2,7 @@
<idea-plugin> <idea-plugin>
<!-- Unique id for this plugin. Must stay constant for the life of the plugin. --> <!-- Unique id for this plugin. Must stay constant for the life of the plugin. -->
<id>org.intellij.sdk.kotlin</id> <id>org.intellij.sdk.kotlin</id>
<!-- Text to display as name on Preferences/Settings | Plugin page --> <!-- Text to display as name on Preferences/Settings | Plugin page -->
@ -11,10 +11,10 @@
<!-- Indicate this plugin can be loaded in all IntelliJ Platform-based products. --> <!-- Indicate this plugin can be loaded in all IntelliJ Platform-based products. -->
<depends>com.intellij.modules.platform</depends> <depends>com.intellij.modules.platform</depends>
<!-- Text to display as description on Preferences/Settings | Plugin page --> <!-- Text to display as description on Preferences/Settings | Plugin page -->
<description> <description>
<![CDATA[ <![CDATA[
Adds a <i>Greeting</i> menu group anchored last in the <b>Main Menu</b> Adds a <i>Greeting</i> menu group anchored last in the <b>Main Menu</b>
]]> ]]>
</description> </description>
<change-notes> <change-notes>

31
live_templates/README.md Normal file
View File

@ -0,0 +1,31 @@
# Live Templates Sample [![JetBrains IntelliJ Platform SDK Docs](https://jb.gg/badges/docs.svg)][docs]
*Reference: [Live Templates in IntelliJ SDK Docs][docs:live_templates]*
## Quickstart
Live Templates Sample Project implements two example live templates for the Markdown language:
- New link reference - by typing the `{<TAB>`, the following template will be inserted: `[$TEXT$]($LINK$)$END$`
- Convert to title case - retrieves the text from the macro or selection, if available.
### Extension Points
| Name | Implementation | Extension Point Class |
| ----------------------------------- | --------------------------------------- | ---------------------------------------------- |
| `com.intellij.defaultLiveTemplates` | [Markdown][file:Markdown] | |
| `com.intellij.liveTemplateContext` | [MarkdownContext][file:MarkdownContext] | [TemplateContextType][sdk:TemplateContextType] |
| `com.intellij.liveTemplateMacro` | [TitleCaseMacro][file:TitleCaseMacro] | [MacroBase][sdk:MacroBase] |
*Reference: [Plugin Extension Points in IntelliJ SDK Docs][docs:ep]*
[docs]: https://www.jetbrains.org/intellij/sdk/docs
[docs:live_templates]: https://jetbrains.org/intellij/sdk/docs/tutorials/live_templates.html
[docs:ep]: https://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_extensions.html
[file:Markdown]: ./src/main/resources/liveTemplates/Markdown.xml
[file:MarkdownContext]: ./src/main/java/org/intellij/sdk/liveTemplates/MarkdownContext.java
[file:TitleCaseMacro]: ./src/main/java/org/intellij/sdk/liveTemplates/TitleCaseMacro.java
[sdk:TemplateContextType]: upsource:///platform/analysis-api/src/com/intellij/codeInsight/template/TemplateContextType.java
[sdk:MacroBase]: upsource:///platform/lang-impl/src/com/intellij/codeInsight/template/macro/MacroBase.java

View File

@ -4,10 +4,10 @@ package org.intellij.sdk.liveTemplates;
import com.intellij.codeInsight.template.TemplateActionContext; import com.intellij.codeInsight.template.TemplateActionContext;
import com.intellij.codeInsight.template.TemplateContextType; import com.intellij.codeInsight.template.TemplateContextType;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class MarkdownContext extends TemplateContextType { public class MarkdownContext extends TemplateContextType {
protected MarkdownContext() { protected MarkdownContext() {
super("MARKDOWN", "Markdown"); super("MARKDOWN", "Markdown");
} }

View File

@ -8,6 +8,7 @@ import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class TitleCaseMacro extends MacroBase { public class TitleCaseMacro extends MacroBase {
public TitleCaseMacro() { public TitleCaseMacro() {
super("titleCase", "titleCase(String)"); super("titleCase", "titleCase(String)");
} }
@ -23,14 +24,14 @@ public class TitleCaseMacro extends MacroBase {
protected Result calculateResult(@NotNull Expression[] params, ExpressionContext context, boolean quick) { protected Result calculateResult(@NotNull Expression[] params, ExpressionContext context, boolean quick) {
// Retrieve the text from the macro or selection, if any is available. // Retrieve the text from the macro or selection, if any is available.
String text = getTextResult(params, context, true); String text = getTextResult(params, context, true);
if (text != null) { if (text == null) {
if (text.length() > 0) { return null;
// Capitalize the start of every word
text = StringUtil.toTitleCase(text);
}
return new TextResult(text);
} }
return null; if (text.length() > 0) {
// Capitalize the start of every word
text = StringUtil.toTitleCase(text);
}
return new TextResult(text);
} }
@Override @Override

View File

@ -2,19 +2,20 @@
<idea-plugin> <idea-plugin>
<!-- Unique id for this plugin. Must stay constant for the life of the plugin. --> <!-- Unique id for this plugin. Must stay constant for the life of the plugin. -->
<id>org.intellij.sdk.liveTemplates</id> <id>org.intellij.sdk.liveTemplates</id>
<!-- Text to display as name on Preferences/Settings | Plugin page --> <!-- Text to display as name on Preferences/Settings | Plugin page -->
<name>SDK: Live Templates Sample Project</name> <name>SDK: Live Templates Sample</name>
<!-- Product and plugin compatibility requirements --> <!-- Product and plugin compatibility requirements -->
<depends>com.intellij.modules.lang</depends> <depends>com.intellij.modules.lang</depends>
<!-- Text to display as description on Preferences/Settings | Plugin page --> <!-- Text to display as description on Preferences/Settings | Plugin page -->
<description> <description>
<![CDATA[ <![CDATA[
Demonstrates implementing live templates for Markdown language.<br> Adds an entry to the <b>Preferences | Editor | Live Templates</b> dialog. Demonstrates implementing live templates for Markdown language.<br> Adds an entry to the
<b>Preferences | Editor | Live Templates</b> dialog.
]]> ]]>
</description> </description>
<change-notes> <change-notes>

View File

@ -0,0 +1,35 @@
# Maximum Open Projects Sample [![JetBrains IntelliJ Platform SDK Docs](https://jb.gg/badges/docs.svg)][docs]
*Reference: [Plugin Services in IntelliJ SDK Docs][docs:plugin_services]*
## Quickstart
Maximum Open Projects Sample implements a `ProjectManagerListener` with two methods applied to check if the current projects have been opened or closed.
Each method refers to the `ProjectCountingService` service registered as an `applicationService` extension point.
It provides methods to increase and decrease the global counter of the currently opened projects in the IDE.
After opening each one, a message dialog is presented to the user with the current number.
### Extension Points
| Name | Implementation | Extension Point Class |
| --------------------------------- | ----------------------------------------------------- | --------------------- |
| `com.intellij.applicationService` | [ProjectCountingService][file:ProjectCountingService] | |
*Reference: [Plugin Extension Points in IntelliJ SDK Docs][docs:ep]*
### Application Listeners
| Name | Implementation | Extension Point Class |
| -------- | --------------------------------------------------------- | ---------------------------------------------------- |
| listener | [ProjectOpenCloseListener][file:ProjectOpenCloseListener] | [ProjectManagerListener][sdk:ProjectManagerListener] |
*Reference: [Plugin Listeners in IntelliJ SDK Docs][docs:listeners]*
[docs]: https://www.jetbrains.org/intellij/sdk/docs
[docs:plugin_services]: https://jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_services.html
[docs:ep]: https://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_extensions.html
[docs:listeners]: https://jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_listeners.html
[file:ProjectCountingService]: ./src/main/java/org/intellij/sdk/maxOpenProjects/ProjectCountingService.java
[file:ProjectOpenCloseListener]: ./src/main/java/org/intellij/sdk/maxOpenProjects/ProjectOpenCloseListener.java
[sdk:ProjectManagerListener]: upsource:///platform/projectModel-api/src/com/intellij/openapi/project/ProjectManagerListener.java

View File

@ -7,6 +7,7 @@ package org.intellij.sdk.maxOpenProjects;
* how many projects are open at a given time. * how many projects are open at a given time.
*/ */
public class ProjectCountingService { public class ProjectCountingService {
// Sets the maximum allowed number of opened projects. // Sets the maximum allowed number of opened projects.
private final static int MAX_OPEN_PRJ_LIMIT = 3; private final static int MAX_OPEN_PRJ_LIMIT = 3;
// The count of open projects must always be >= 0 // The count of open projects must always be >= 0

View File

@ -23,7 +23,9 @@ public class ProjectOpenCloseListener implements ProjectManagerListener {
@Override @Override
public void projectOpened(@NotNull Project project) { public void projectOpened(@NotNull Project project) {
// Ensure this isn't part of testing // Ensure this isn't part of testing
if (ApplicationManager.getApplication().isUnitTestMode()) return; if (ApplicationManager.getApplication().isUnitTestMode()) {
return;
}
// Get the counting service // Get the counting service
ProjectCountingService projectCountingService = ServiceManager.getService(ProjectCountingService.class); ProjectCountingService projectCountingService = ServiceManager.getService(ProjectCountingService.class);
// Increment the project count // Increment the project count
@ -46,7 +48,9 @@ public class ProjectOpenCloseListener implements ProjectManagerListener {
@Override @Override
public void projectClosed(@NotNull Project project) { public void projectClosed(@NotNull Project project) {
// Ensure this isn't part of testing // Ensure this isn't part of testing
if (ApplicationManager.getApplication().isUnitTestMode()) return; if (ApplicationManager.getApplication().isUnitTestMode()) {
return;
}
// Get the counting service // Get the counting service
ProjectCountingService projectCountingService = ServiceManager.getService(ProjectCountingService.class); ProjectCountingService projectCountingService = ServiceManager.getService(ProjectCountingService.class);
// Decrement the count because a project just closed // Decrement the count because a project just closed

View File

@ -2,25 +2,29 @@
<idea-plugin> <idea-plugin>
<!-- Unique id for this plugin. Must stay constant for the life of the plugin. --> <!-- Unique id for this plugin. Must stay constant for the life of the plugin. -->
<id>org.intellij.sdk.maxOpenProjects</id> <id>org.intellij.sdk.maxOpenProjects</id>
<!-- Text to display as name on Preferences/Settings | Plugin page --> <!-- Text to display as name on Preferences/Settings | Plugin page -->
<name>SDK: Maximum Open Projects Sample</name> <name>SDK: Maximum Open Projects Sample</name>
<!-- Product and plugin compatibility requirements --> <!-- Product and plugin compatibility requirements -->
<depends>com.intellij.modules.platform</depends> <depends>com.intellij.modules.platform</depends>
<!-- Text to display as description on Preferences/Settings | Plugin page --> <!-- Text to display as description on Preferences/Settings | Plugin page -->
<description> <description>
<![CDATA[ <![CDATA[
Demonstrates adding application services and listeners. Shows warning dialog when more than 3 open projects is exceeded. Demonstrates adding application services and listeners. Shows warning dialog when more than 3 open projects
is exceeded.
]]> ]]>
</description> </description>
<change-notes> <change-notes>
<![CDATA[ <![CDATA[
<ul> <ul>
<li><b>2.1.0</b> Remove project component and use listener instead. No longer denies opening additional projects. Just notifies the user.</li> <li>
<b>2.1.0</b> Remove project component and use listener instead. No longer denies opening additional
projects. Just notifies the user.
</li>
<li><b>2.0.0</b> Convert to Gradle-based plugin.</li> <li><b>2.0.0</b> Convert to Gradle-based plugin.</li>
<li><b>1.0.0</b> Release 2018.3 and earlier.</li> <li><b>1.0.0</b> Release 2018.3 and earlier.</li>
</ul> </ul>
@ -31,10 +35,12 @@
<vendor url="https://plugins.jetbrains.com">IntelliJ Platform SDK</vendor> <vendor url="https://plugins.jetbrains.com">IntelliJ Platform SDK</vendor>
<applicationListeners> <applicationListeners>
<listener class="org.intellij.sdk.maxOpenProjects.ProjectOpenCloseListener" topic="com.intellij.openapi.project.ProjectManagerListener"/> <listener class="org.intellij.sdk.maxOpenProjects.ProjectOpenCloseListener"
topic="com.intellij.openapi.project.ProjectManagerListener"/>
</applicationListeners> </applicationListeners>
<extensions defaultExtensionNs="com.intellij"> <extensions defaultExtensionNs="com.intellij">
<applicationService serviceImplementation="org.intellij.sdk.maxOpenProjects.ProjectCountingService"/> <applicationService serviceImplementation="org.intellij.sdk.maxOpenProjects.ProjectCountingService"/>
</extensions> </extensions>
</idea-plugin> </idea-plugin>

24
module/README.md Normal file
View File

@ -0,0 +1,24 @@
# Module Type Sample [![JetBrains IntelliJ Platform SDK Docs](https://jb.gg/badges/docs.svg)][docs]
*Reference: [Plugin Services in IntelliJ SDK Docs][docs:plugin_services]*
## Quickstart
The sample project that presents an implementation of the `moduleType` extension point, which adds a new module type to the *New Module* Project Wizard.
Module with a custom name, description, and icon set provides a `ModuleBuilder` with extra steps present for additional module configuration.
### Extension Points
| Name | Implementation | Extension Point Class |
| ------------------------- | ------------------------------------- | ---------------------------- |
| `com.intellij.moduleType` | [DemoModuleType][file:DemoModuleType] | [ModuleType][sdk:ModuleType] |
*Reference: [Plugin Extension Points in IntelliJ SDK Docs][docs:ep]*
[docs]: https://www.jetbrains.org/intellij/sdk/docs
[docs:plugin_services]: https://jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_services.html
[docs:ep]: https://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_extensions.html
[file:DemoModuleType]: ./src/main/java/org/intellij/sdk/module/DemoModuleType.java
[sdk:ModuleType]: upsource:///platform/lang-api/src/com/intellij/openapi/module/ModuleType.java

View File

@ -7,5 +7,7 @@ import com.intellij.openapi.util.IconLoader;
import javax.swing.*; import javax.swing.*;
public class SdkIcons { public class SdkIcons {
public static final Icon Sdk_default_icon = IconLoader.getIcon("/icons/sdk_16.svg"); public static final Icon Sdk_default_icon = IconLoader.getIcon("/icons/sdk_16.svg");
} }

View File

@ -6,19 +6,18 @@ import com.intellij.ide.util.projectWizard.ModuleBuilder;
import com.intellij.ide.util.projectWizard.ModuleWizardStep; import com.intellij.ide.util.projectWizard.ModuleWizardStep;
import com.intellij.ide.util.projectWizard.WizardContext; import com.intellij.ide.util.projectWizard.WizardContext;
import com.intellij.openapi.Disposable; import com.intellij.openapi.Disposable;
import com.intellij.openapi.module.ModuleType;
import com.intellij.openapi.roots.ModifiableRootModel; import com.intellij.openapi.roots.ModifiableRootModel;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
public class DemoModuleBuilder extends ModuleBuilder { public class DemoModuleBuilder extends ModuleBuilder {
@Override @Override
public void setupRootModel(@NotNull ModifiableRootModel model) { public void setupRootModel(@NotNull ModifiableRootModel model) {
} }
@Override @Override
public ModuleType getModuleType() { public DemoModuleType getModuleType() {
return DemoModuleType.getInstance(); return DemoModuleType.getInstance();
} }
@ -27,4 +26,5 @@ public class DemoModuleBuilder extends ModuleBuilder {
public ModuleWizardStep getCustomOptionsStep(WizardContext context, Disposable parentDisposable) { public ModuleWizardStep getCustomOptionsStep(WizardContext context, Disposable parentDisposable) {
return new DemoModuleWizardStep(); return new DemoModuleWizardStep();
} }
} }

View File

@ -13,6 +13,7 @@ import org.jetbrains.annotations.NotNull;
import javax.swing.*; import javax.swing.*;
public class DemoModuleType extends ModuleType<DemoModuleBuilder> { public class DemoModuleType extends ModuleType<DemoModuleBuilder> {
private static final String ID = "DEMO_MODULE_TYPE"; private static final String ID = "DEMO_MODULE_TYPE";
public DemoModuleType() { public DemoModuleType() {
@ -54,4 +55,5 @@ public class DemoModuleType extends ModuleType<DemoModuleBuilder> {
@NotNull ModulesProvider modulesProvider) { @NotNull ModulesProvider modulesProvider) {
return super.createWizardSteps(wizardContext, moduleBuilder, modulesProvider); return super.createWizardSteps(wizardContext, moduleBuilder, modulesProvider);
} }
} }

View File

@ -7,6 +7,7 @@ import com.intellij.ide.util.projectWizard.ModuleWizardStep;
import javax.swing.*; import javax.swing.*;
public class DemoModuleWizardStep extends ModuleWizardStep { public class DemoModuleWizardStep extends ModuleWizardStep {
@Override @Override
public JComponent getComponent() { public JComponent getComponent() {
return new JLabel("Provide some setting here"); return new JLabel("Provide some setting here");
@ -16,4 +17,5 @@ public class DemoModuleWizardStep extends ModuleWizardStep {
public void updateDataModel() { public void updateDataModel() {
//todo update model according to UI //todo update model according to UI
} }
} }

View File

@ -2,16 +2,16 @@
<idea-plugin> <idea-plugin>
<!-- Unique id for this plugin. Must stay constant for the life of the plugin. --> <!-- Unique id for this plugin. Must stay constant for the life of the plugin. -->
<id>org.intellij.sdk.module</id> <id>org.intellij.sdk.module</id>
<!-- Text to display as name on Preferences/Settings | Plugin page --> <!-- Text to display as name on Preferences/Settings | Plugin page -->
<name>SDK: Module Type Sample Project</name> <name>SDK: Module Type Sample</name>
<!-- Product and plugin compatibility requirements --> <!-- Product and plugin compatibility requirements -->
<depends>com.intellij.modules.platform</depends> <depends>com.intellij.modules.platform</depends>
<!-- Text to display as description on Preferences/Settings | Plugin page --> <!-- Text to display as description on Preferences/Settings | Plugin page -->
<description> <description>
<![CDATA[ <![CDATA[
Demonstrates example of working with module types<br>Adds <i>SDK Demo Module</i> to <b>File | New | Project...</b> Demonstrates example of working with module types<br>Adds <i>SDK Demo Module</i> to <b>File | New | Project...</b>

View File

@ -0,0 +1,23 @@
# PyCharm Sample [![JetBrains IntelliJ Platform SDK Docs](https://jb.gg/badges/docs.svg)][docs]
*Reference: [PyCharm Plugin Development in IntelliJ SDK Docs][docs:pycharm]*
## Quickstart
PyCharm Sample is a plugin that depends on the PyCharm IDE having the proper dependencies specified in the Gradle configuration file.
The implementation utilizes a simple action added to the *MainMenu* group displaying a message dialog after invoking.
### Actions
| ID | Implementation | Extension Point Class |
| -------------------------------------------- | ------------------------------------------- | ------------------------ |
| `org.intellij.sdk.pycharm.PopupDialogAction` | [PopupDialogAction][file:PopupDialogAction] | [AnAction][sdk:AnAction] |
*Reference: [Action System in IntelliJ SDK Docs][docs:actions]*
[docs]: https://www.jetbrains.org/intellij/sdk/docs
[docs:actions]: https://www.jetbrains.org/intellij/sdk/docs/basics/action_system.html
[docs:pycharm]: https://jetbrains.org/intellij/sdk/docs/products/pycharm.html
[file:PopupDialogAction]: ./src/main/java/org/intellij/sdk/pycharm/PopupDialogAction.java
[sdk:AnAction]: upsource:///platform/editor-ui-api/src/com/intellij/openapi/actionSystem/AnAction.java

View File

@ -7,5 +7,7 @@ import com.intellij.openapi.util.IconLoader;
import javax.swing.*; import javax.swing.*;
public class SdkIcons { public class SdkIcons {
public static final Icon Sdk_default_icon = IconLoader.getIcon("/icons/sdk_16.svg"); public static final Icon Sdk_default_icon = IconLoader.getIcon("/icons/sdk_16.svg");
} }

View File

@ -15,25 +15,27 @@ import org.jetbrains.annotations.NotNull;
* in the plugin.xml file. But when added at runtime this class is instantiated by an action group. * in the plugin.xml file. But when added at runtime this class is instantiated by an action group.
*/ */
public class PopupDialogAction extends AnAction { public class PopupDialogAction extends AnAction {
/** /**
* Gives the user feedback when the dynamic action menu is chosen. * Gives the user feedback when the dynamic action menu is chosen.
* Pops a simple message dialog. See the psi_demo plugin for an * Pops a simple message dialog. See the psi_demo plugin for an
* example of how to use AnActionEvent to access data. * example of how to use AnActionEvent to access data.
*
* @param event Event received when the associated menu item is chosen. * @param event Event received when the associated menu item is chosen.
*/ */
@Override @Override
public void actionPerformed(@NotNull AnActionEvent event) { public void actionPerformed(@NotNull AnActionEvent event) {
Project project = event.getProject(); Project project = event.getProject();
Messages.showMessageDialog(project, Messages.showMessageDialog(project,
"Popup dialog action", "Popup dialog action",
"Greetings from PyCharm Basics Plugin", "Greetings from PyCharm Basics Plugin",
Messages.getInformationIcon()); Messages.getInformationIcon());
} }
/** /**
* Determines whether this menu item is available for the current context. * Determines whether this menu item is available for the current context.
* Requires a project to be open. * Requires a project to be open.
*
* @param e Event received when the associated group-id menu is chosen. * @param e Event received when the associated group-id menu is chosen.
*/ */
@Override @Override
@ -42,5 +44,5 @@ public class PopupDialogAction extends AnAction {
Project project = e.getProject(); Project project = e.getProject();
e.getPresentation().setEnabledAndVisible(project != null); e.getPresentation().setEnabledAndVisible(project != null);
} }
} }

View File

@ -2,19 +2,19 @@
<idea-plugin> <idea-plugin>
<!-- Unique id for this plugin. Must stay constant for the life of the plugin. --> <!-- Unique id for this plugin. Must stay constant for the life of the plugin. -->
<id>org.intellij.sdk.pycharm</id> <id>org.intellij.sdk.pycharm</id>
<!-- Text to display as name on Preferences/Settings | Plugin page --> <!-- Text to display as name on Preferences/Settings | Plugin page -->
<name>SDK: PyCharm Sample Project</name> <name>SDK: PyCharm Sample</name>
<!-- Requires the python plugin to run --> <!-- Requires the python plugin to run -->
<depends>com.intellij.modules.python</depends> <depends>com.intellij.modules.python</depends>
<!-- Text to display as description on Preferences/Settings | Plugin page --> <!-- Text to display as description on Preferences/Settings | Plugin page -->
<description> <description>
<![CDATA[ <![CDATA[
Demonstrates how to configure a plugin project for a PyCharm plugin. Demonstrates how to configure a plugin project for a PyCharm plugin.
]]> ]]>
</description> </description>
<change-notes> <change-notes>
@ -27,10 +27,10 @@
<vendor url="https://plugins.jetbrains.com">IntelliJ Platform SDK</vendor> <vendor url="https://plugins.jetbrains.com">IntelliJ Platform SDK</vendor>
<actions> <actions>
<!-- Define a new menu group as a last entry in the main menu --> <!-- Define a new menu group as a last entry in the main menu -->
<group id="org.intellij.sdk.pycharm.NewGroupedActions" text="SDK: Plugin" popup="true"> <group id="org.intellij.sdk.pycharm.NewGroupedActions" text="SDK: Plugin" popup="true">
<add-to-group group-id="MainMenu" anchor="last"/> <add-to-group group-id="MainMenu" anchor="last"/>
<!-- Add a single action to the new group --> <!-- Add a single action to the new group -->
<action id="org.intellij.sdk.pycharm.PopupDialogAction" class="org.intellij.sdk.pycharm.PopupDialogAction" <action id="org.intellij.sdk.pycharm.PopupDialogAction" class="org.intellij.sdk.pycharm.PopupDialogAction"
text="Pop a Dialog" description="SDK PyCharm basics example" icon="SdkIcons.Sdk_default_icon"> text="Pop a Dialog" description="SDK PyCharm basics example" icon="SdkIcons.Sdk_default_icon">
</action> </action>

36
project_model/README.md Normal file
View File

@ -0,0 +1,36 @@
# Project Model Sample [![JetBrains IntelliJ Platform SDK Docs](https://jb.gg/badges/docs.svg)][docs]
*Reference: [PyCharm Plugin Development in IntelliJ SDK Docs][docs:pycharm]*
## Quickstart
Project Model Sample project provides five actions that present data extracted using `ProjectRootManager` instance in the message dialogs. Within the implemented actions, you will be able to:
- fetch libraries used in the project,
- retrieve the information about the module details,
- rename the used SDK,
- get the content source roots,
- or extend the project dependencies with an additional library.
### Actions
| ID | Implementation | Extension Point Class |
| --------------------------------- | ----------------------------------------------------------------- | ------------------------ |
| `ProjectModel.SourceRoots` | [ShowSourceRootsActions][file:ShowSourceRootsActions] | [AnAction][sdk:AnAction] |
| `ProjectModel.ProjectSdk` | [ProjectSdkAction][file:ProjectSdkAction] | [AnAction][sdk:AnAction] |
| `ProjectModel.ProjectFileIndex` | [ProjectFileIndexSampleAction][file:ProjectFileIndexSampleAction] | [AnAction][sdk:AnAction] |
| `ProjectModel.ModificationAction` | [ModificationAction][file:ModificationAction] | [AnAction][sdk:AnAction] |
| `ProjectModel.LibrariesAction` | [LibrariesAction][file:LibrariesAction] | [AnAction][sdk:AnAction] |
*Reference: [Action System in IntelliJ SDK Docs][docs:actions]*
[docs]: https://www.jetbrains.org/intellij/sdk/docs
[docs:actions]: https://www.jetbrains.org/intellij/sdk/docs/basics/action_system.html
[docs:pycharm]: https://jetbrains.org/intellij/sdk/docs/products/pycharm.html
[file:ShowSourceRootsActions]: ./src/main/java/org/intellij/sdk/project/model/ShowSourceRootsActions.java
[file:ProjectSdkAction]: ./src/main/java/org/intellij/sdk/project/model/ProjectSdkAction.java
[file:ProjectFileIndexSampleAction]: ./src/main/java/org/intellij/sdk/project/model/ProjectFileIndexSampleAction.java
[file:ModificationAction]: ./src/main/java/org/intellij/sdk/project/model/ModificationAction.java
[file:LibrariesAction]: ./src/main/java/org/intellij/sdk/project/model/LibrariesAction.java
[sdk:AnAction]: upsource:///platform/editor-ui-api/src/com/intellij/openapi/actionSystem/AnAction.java

View File

@ -7,5 +7,7 @@ import com.intellij.openapi.util.IconLoader;
import javax.swing.*; import javax.swing.*;
public class SdkIcons { public class SdkIcons {
public static final Icon Sdk_default_icon = IconLoader.getIcon("/icons/sdk_16.svg"); public static final Icon Sdk_default_icon = IconLoader.getIcon("/icons/sdk_16.svg");
} }

View File

@ -16,16 +16,23 @@ import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class LibrariesAction extends AnAction { public class LibrariesAction extends AnAction {
@Override @Override
public void update(@NotNull final AnActionEvent event) { public void update(@NotNull final AnActionEvent event) {
Project project = event.getProject(); Project project = event.getProject();
if (project == null) return; if (project == null) {
return;
}
Navigatable element = event.getData(CommonDataKeys.NAVIGATABLE); Navigatable element = event.getData(CommonDataKeys.NAVIGATABLE);
if (element instanceof PsiClass) { if (element instanceof PsiClass) {
PsiFile psiFile = ((PsiClass) element).getContainingFile(); PsiFile psiFile = ((PsiClass) element).getContainingFile();
if (psiFile == null) return; if (psiFile == null) {
return;
}
VirtualFile virtualFile = psiFile.getVirtualFile(); VirtualFile virtualFile = psiFile.getVirtualFile();
if (virtualFile == null) return; if (virtualFile == null) {
return;
}
event.getPresentation().setEnabledAndVisible(true); event.getPresentation().setEnabledAndVisible(true);
} }
} }
@ -33,22 +40,32 @@ public class LibrariesAction extends AnAction {
@Override @Override
public void actionPerformed(@NotNull AnActionEvent event) { public void actionPerformed(@NotNull AnActionEvent event) {
Project project = event.getProject(); Project project = event.getProject();
if (project == null) return; if (project == null) {
return;
}
Navigatable element = event.getData(CommonDataKeys.NAVIGATABLE); Navigatable element = event.getData(CommonDataKeys.NAVIGATABLE);
if (element instanceof PsiClass) { if (element instanceof PsiClass) {
PsiFile psiFile = ((PsiClass) element).getContainingFile(); PsiFile psiFile = ((PsiClass) element).getContainingFile();
if (psiFile == null) return; if (psiFile == null) {
return;
}
VirtualFile virtualFile = psiFile.getVirtualFile(); VirtualFile virtualFile = psiFile.getVirtualFile();
if (virtualFile == null) return; if (virtualFile == null) {
return;
}
final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex(); final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex();
StringBuilder jars = new StringBuilder(); StringBuilder jars = new StringBuilder();
for (OrderEntry orderEntry : fileIndex.getOrderEntriesForFile(virtualFile)) { for (OrderEntry orderEntry : fileIndex.getOrderEntriesForFile(virtualFile)) {
if (orderEntry instanceof LibraryOrderEntry) { if (orderEntry instanceof LibraryOrderEntry) {
final LibraryOrderEntry libraryEntry = (LibraryOrderEntry) orderEntry; final LibraryOrderEntry libraryEntry = (LibraryOrderEntry) orderEntry;
final Library library = libraryEntry.getLibrary(); final Library library = libraryEntry.getLibrary();
if (library == null) continue; if (library == null) {
continue;
}
VirtualFile[] files = library.getFiles(OrderRootType.CLASSES); VirtualFile[] files = library.getFiles(OrderRootType.CLASSES);
if (files.length == 0) continue; if (files.length == 0) {
continue;
}
for (VirtualFile jar : files) { for (VirtualFile jar : files) {
jars.append(jar.getName()).append(", "); jars.append(jar.getName()).append(", ");
} }
@ -64,4 +81,5 @@ public class LibrariesAction extends AnAction {
"Libraries Info"); "Libraries Info");
} }
} }
} }

View File

@ -18,19 +18,28 @@ import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class ModificationAction extends AnAction { public class ModificationAction extends AnAction {
@Override @Override
public void actionPerformed(@NotNull final AnActionEvent event) { public void actionPerformed(@NotNull final AnActionEvent event) {
Project project = event.getProject(); Project project = event.getProject();
if (project == null) return; if (project == null) {
return;
}
Navigatable element = event.getData(CommonDataKeys.NAVIGATABLE); Navigatable element = event.getData(CommonDataKeys.NAVIGATABLE);
if (element instanceof PsiClass) { if (element instanceof PsiClass) {
PsiFile file = ((PsiClass) element).getContainingFile(); PsiFile file = ((PsiClass) element).getContainingFile();
if (file == null) return; if (file == null) {
return;
}
final VirtualFile virtualFile = file.getVirtualFile(); final VirtualFile virtualFile = file.getVirtualFile();
if (virtualFile == null) return; if (virtualFile == null) {
return;
}
final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex(); final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex();
final Module module = fileIndex.getModuleForFile(virtualFile); final Module module = fileIndex.getModuleForFile(virtualFile);
if (module == null) return; if (module == null) {
return;
}
if (!ModuleRootManager.getInstance(module).getFileIndex().isInContent(virtualFile)) { if (!ModuleRootManager.getInstance(module).getFileIndex().isInContent(virtualFile)) {
ModuleRootModificationUtil.addModuleLibrary(module, virtualFile.getUrl()); ModuleRootModificationUtil.addModuleLibrary(module, virtualFile.getUrl());
} }
@ -44,4 +53,5 @@ public class ModificationAction extends AnAction {
Navigatable element = event.getData(CommonDataKeys.NAVIGATABLE); Navigatable element = event.getData(CommonDataKeys.NAVIGATABLE);
event.getPresentation().setEnabledAndVisible(project != null && element != null); event.getPresentation().setEnabledAndVisible(project != null && element != null);
} }
} }

View File

@ -17,6 +17,7 @@ import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class ProjectFileIndexSampleAction extends AnAction { public class ProjectFileIndexSampleAction extends AnAction {
@Override @Override
public void update(@NotNull final AnActionEvent event) { public void update(@NotNull final AnActionEvent event) {
Project project = event.getProject(); Project project = event.getProject();
@ -29,7 +30,9 @@ public class ProjectFileIndexSampleAction extends AnAction {
public void actionPerformed(@NotNull final AnActionEvent event) { public void actionPerformed(@NotNull final AnActionEvent event) {
Project project = event.getProject(); Project project = event.getProject();
final Editor editor = event.getData(CommonDataKeys.EDITOR); final Editor editor = event.getData(CommonDataKeys.EDITOR);
if (project == null || editor == null) return; if (project == null || editor == null) {
return;
}
Document document = editor.getDocument(); Document document = editor.getDocument();
FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance(); FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance();
VirtualFile virtualFile = fileDocumentManager.getFile(document); VirtualFile virtualFile = fileDocumentManager.getFile(document);
@ -44,11 +47,12 @@ public class ProjectFileIndexSampleAction extends AnAction {
boolean isInLibraryClasses = projectFileIndex.isInLibraryClasses(virtualFile); boolean isInLibraryClasses = projectFileIndex.isInLibraryClasses(virtualFile);
boolean isInLibrarySource = projectFileIndex.isInLibrarySource(virtualFile); boolean isInLibrarySource = projectFileIndex.isInLibrarySource(virtualFile);
Messages.showInfoMessage("Module: " + moduleName + "\n" + Messages.showInfoMessage("Module: " + moduleName + "\n" +
"Module content root: " + moduleContentRoot + "\n" + "Module content root: " + moduleContentRoot + "\n" +
"Is library file: " + isLibraryFile + "\n" + "Is library file: " + isLibraryFile + "\n" +
"Is in library classes: " + isInLibraryClasses + "Is in library classes: " + isInLibraryClasses +
", Is in library source: " + isInLibrarySource, ", Is in library source: " + isInLibrarySource,
"Main File Info for" + virtualFile.getName()); "Main File Info for" + virtualFile.getName());
} }
} }
} }

View File

@ -11,6 +11,7 @@ import com.intellij.openapi.ui.Messages;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class ProjectSdkAction extends AnAction { public class ProjectSdkAction extends AnAction {
@Override @Override
public void actionPerformed(@NotNull final AnActionEvent event) { public void actionPerformed(@NotNull final AnActionEvent event) {
Project project = event.getProject(); Project project = event.getProject();
@ -33,4 +34,5 @@ public class ProjectSdkAction extends AnAction {
event.getPresentation().setEnabledAndVisible(sdk != null); event.getPresentation().setEnabledAndVisible(sdk != null);
} }
} }
} }

View File

@ -11,18 +11,23 @@ import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class ShowSourceRootsActions extends AnAction { public class ShowSourceRootsActions extends AnAction {
@Override @Override
public void actionPerformed(@NotNull final AnActionEvent event) { public void actionPerformed(@NotNull final AnActionEvent event) {
Project project = event.getProject(); Project project = event.getProject();
if (project == null) return; if (project == null) {
return;
}
String projectName = project.getName(); String projectName = project.getName();
StringBuilder sourceRootsList = new StringBuilder(); StringBuilder sourceRootsList = new StringBuilder();
VirtualFile[] vFiles = ProjectRootManager.getInstance(project).getContentSourceRoots(); VirtualFile[] vFiles = ProjectRootManager.getInstance(project).getContentSourceRoots();
for (VirtualFile file : vFiles) { for (VirtualFile file : vFiles) {
sourceRootsList.append(file.getUrl()).append("\n"); sourceRootsList.append(file.getUrl()).append("\n");
} }
Messages.showInfoMessage("Source roots for the " + projectName + " plugin:\n" + sourceRootsList.toString(), Messages.showInfoMessage(
"Project Properties"); "Source roots for the " + projectName + " plugin:\n" + sourceRootsList.toString(),
"Project Properties"
);
} }
@Override @Override
@ -31,4 +36,5 @@ public class ShowSourceRootsActions extends AnAction {
event.getPresentation().setEnabled(visibility); event.getPresentation().setEnabled(visibility);
event.getPresentation().setVisible(visibility); event.getPresentation().setVisible(visibility);
} }
} }

View File

@ -2,19 +2,20 @@
<idea-plugin> <idea-plugin>
<!-- Unique id for this plugin. Must stay constant for the life of the plugin. --> <!-- Unique id for this plugin. Must stay constant for the life of the plugin. -->
<id>org.intellij.sdk.project.model</id> <id>org.intellij.sdk.project.model</id>
<!-- Text to display as name on Preferences/Settings | Plugin page --> <!-- Text to display as name on Preferences/Settings | Plugin page -->
<name>SDK: Project Model Sample Project</name> <name>SDK: Project Model Sample</name>
<!-- Product and plugin compatibility requirements --> <!-- Product and plugin compatibility requirements -->
<depends>com.intellij.modules.java</depends> <depends>com.intellij.modules.java</depends>
<!-- Text to display as description on Preferences/Settings | Plugin page --> <!-- Text to display as description on Preferences/Settings | Plugin page -->
<description> <description>
<![CDATA[ <![CDATA[
Demonstrates various aspects of interacting with project model.<br>Adds menu items to <b>Tools</b> and <b>Editor Context</b> menus. Demonstrates various aspects of interacting with project model.<br>Adds menu items to
<b>Tools</b> and <b>Editor Context</b> menus.
]]> ]]>
</description> </description>
<change-notes> <change-notes>

View File

@ -0,0 +1,24 @@
# Project View Pane Demo [![JetBrains IntelliJ Platform SDK Docs](https://jb.gg/badges/docs.svg)][docs]
*Reference: [Plugin Services in IntelliJ SDK Docs][docs:project_view]*
## Quickstart
The current demo describes an implementation of the `projectViewPane` extension point, which allows creating an additional presentation type for the Project view pane.
`ImagesProjectViewPane` limits the project tree to the images only.
### Extension Points
| Name | Implementation | Extension Point Class |
| ------------------------------ | --------------------------------------------------- | ------------------------------------------------------------ |
| `com.intellij.projectViewPane` | [ImagesProjectViewPane][file:ImagesProjectViewPane] | [AbstractProjectViewPSIPane][sdk:AbstractProjectViewPSIPane] |
*Reference: [Plugin Extension Points in IntelliJ SDK Docs][docs:ep]*
[docs]: https://www.jetbrains.org/intellij/sdk/docs
[docs:project_view]: https://jetbrains.org/intellij/sdk/docs/basics/project_view.html
[docs:ep]: https://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_extensions.html
[file:ImagesProjectViewPane]: ./src/main/java/org/intellij/sdk/view/pane/ImagesProjectViewPane.java
[sdk:AbstractProjectViewPSIPane]: upsource:///platform/lang-impl/src/com/intellij/ide/projectView/impl/AbstractProjectViewPSIPane.java

View File

@ -25,6 +25,7 @@ import javax.swing.*;
import java.util.*; import java.util.*;
public class ImagesProjectNode extends AbstractTreeNode<VirtualFile> { public class ImagesProjectNode extends AbstractTreeNode<VirtualFile> {
private static final Key<Set<VirtualFile>> IMAGES_PROJECT_DIRS = Key.create("images.files.or.directories"); private static final Key<Set<VirtualFile>> IMAGES_PROJECT_DIRS = Key.create("images.files.or.directories");
public ImagesProjectNode(final Project project) { public ImagesProjectNode(final Project project) {
@ -88,15 +89,21 @@ public class ImagesProjectNode extends AbstractTreeNode<VirtualFile> {
files.add(file); files.add(file);
} }
} }
if (files.isEmpty()) return Collections.emptyList(); if (files.isEmpty()) {
return Collections.emptyList();
}
final List<AbstractTreeNode<?>> nodes = new ArrayList<>(files.size()); final List<AbstractTreeNode<?>> nodes = new ArrayList<>(files.size());
final boolean alwaysOnTop = ProjectView.getInstance(myProject).isFoldersAlwaysOnTop(""); final boolean alwaysOnTop = ProjectView.getInstance(myProject).isFoldersAlwaysOnTop("");
files.sort((o1, o2) -> { files.sort((o1, o2) -> {
if (alwaysOnTop) { if (alwaysOnTop) {
final boolean d1 = o1.isDirectory(); final boolean d1 = o1.isDirectory();
final boolean d2 = o2.isDirectory(); final boolean d2 = o2.isDirectory();
if (d1 && !d2) return -1; if (d1 && !d2) {
if (!d1 && d2) return 1; return -1;
}
if (!d1 && d2) {
return 1;
}
} }
return StringUtil.naturalCompare(o1.getName(), o2.getName()); return StringUtil.naturalCompare(o1.getName(), o2.getName());
@ -162,5 +169,5 @@ public class ImagesProjectNode extends AbstractTreeNode<VirtualFile> {
} }
}); });
} }
}
}

View File

@ -19,6 +19,7 @@ import org.jetbrains.annotations.NotNull;
import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.DefaultTreeModel;
public class ImagesProjectViewPane extends AbstractProjectViewPSIPane { public class ImagesProjectViewPane extends AbstractProjectViewPSIPane {
public static final String ID = "IMAGES"; public static final String ID = "IMAGES";
protected ImagesProjectViewPane(Project project) { protected ImagesProjectViewPane(Project project) {
@ -110,5 +111,5 @@ public class ImagesProjectViewPane extends AbstractProjectViewPSIPane {
protected AbstractTreeUpdater createTreeUpdater(@NotNull AbstractTreeBuilder builder) { protected AbstractTreeUpdater createTreeUpdater(@NotNull AbstractTreeBuilder builder) {
throw new IllegalStateException("ImagesProjectViewPane tree is async now"); throw new IllegalStateException("ImagesProjectViewPane tree is async now");
} }
}
}

View File

@ -2,16 +2,16 @@
<idea-plugin> <idea-plugin>
<!-- Unique id for this plugin. Must stay constant for the life of the plugin. --> <!-- Unique id for this plugin. Must stay constant for the life of the plugin. -->
<id>org.intellij.sdk.view.pane</id> <id>org.intellij.sdk.view.pane</id>
<!-- Text to display as name on Preferences/Settings | Plugin page --> <!-- Text to display as name on Preferences/Settings | Plugin page -->
<name>SDK: Project View Pane Demo</name> <name>SDK: Project View Pane Demo</name>
<!-- Product and plugin compatibility requirements --> <!-- Product and plugin compatibility requirements -->
<depends>com.intellij.modules.platform</depends> <depends>com.intellij.modules.platform</depends>
<!-- Text to display as description on Preferences/Settings | Plugin page --> <!-- Text to display as description on Preferences/Settings | Plugin page -->
<description> <description>
<![CDATA[ <![CDATA[
Demonstrates Project View Pane, listing only image files. Demonstrates Project View Pane, listing only image files.

24
project_wizard/README.md Normal file
View File

@ -0,0 +1,24 @@
# Project Wizard Demo [![JetBrains IntelliJ Platform SDK Docs](https://jb.gg/badges/docs.svg)][docs]
*Reference: [Plugin Services in IntelliJ SDK Docs][docs:project_wizard]*
## Quickstart
This demo project shows how to add an extra step to the Project Wizard to provide additional project configuration settings.
The new step contains a simple `JLabel` element as an example presentation of the new step content.
### Extension Points
| Name | Implementation | Extension Point Class |
| ---------------------------- | ------------------------------------------------- | ---------------------------------- |
| `com.intellij.moduleBuilder` | [DemoModuleWizardStep][file:DemoModuleWizardStep] | [ModuleBuilder][sdk:ModuleBuilder] |
*Reference: [Plugin Extension Points in IntelliJ SDK Docs][docs:ep]*
[docs]: https://www.jetbrains.org/intellij/sdk/docs
[docs:project_wizard]: https://jetbrains.org/intellij/sdk/docs/tutorials/project_wizard.html
[docs:ep]: https://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_extensions.html
[file:DemoModuleWizardStep]: ./src/main/java/org/intellij/sdk/project/wizard/DemoModuleWizardStep.java
[sdk:ModuleBuilder]: upsource:///platform/lang-api/src/com/intellij/ide/util/projectWizard/ModuleBuilder.java

View File

@ -13,11 +13,11 @@ import org.jetbrains.annotations.NotNull;
import javax.swing.*; import javax.swing.*;
public class DemoModuleWizardStep extends ModuleBuilder { public class DemoModuleWizardStep extends ModuleBuilder {
public void setupRootModel(@NotNull ModifiableRootModel modifiableRootModel) {
public void setupRootModel(@NotNull ModifiableRootModel modifiableRootModel) {
} }
public ModuleType getModuleType() { public ModuleType<?> getModuleType() {
return ModuleType.EMPTY; //or it could be other module type return ModuleType.EMPTY; //or it could be other module type
} }
@ -36,4 +36,5 @@ public class DemoModuleWizardStep extends ModuleBuilder {
} }
}}; }};
} }
} }

View File

@ -2,7 +2,7 @@
<idea-plugin> <idea-plugin>
<!-- Unique id for this plugin. Must stay constant for the life of the plugin. --> <!-- Unique id for this plugin. Must stay constant for the life of the plugin. -->
<id>org.intellij.sdk.project.wizard</id> <id>org.intellij.sdk.project.wizard</id>
<!-- Text to display as name on Preferences/Settings | Plugin page --> <!-- Text to display as name on Preferences/Settings | Plugin page -->
@ -11,7 +11,7 @@
<!-- Product and plugin compatibility requirements --> <!-- Product and plugin compatibility requirements -->
<depends>com.intellij.modules.platform</depends> <depends>com.intellij.modules.platform</depends>
<!-- Text to display as description on Preferences/Settings | Plugin page --> <!-- Text to display as description on Preferences/Settings | Plugin page -->
<description> <description>
<![CDATA[ <![CDATA[
Demonstrates working with the Project Wizard. Demonstrates working with the Project Wizard.

27
psi_demo/README.md Normal file
View File

@ -0,0 +1,27 @@
# PSI Demo [![JetBrains IntelliJ Platform SDK Docs](https://jb.gg/badges/docs.svg)][docs]
*Reference: [Navigating the PSI in IntelliJ SDK Docs][docs:navigating_psi]*
## Quickstart
PSI Demo project demonstrates working with the PSI Navigation by implementing `AnAction` that through the message dialog, informs about:
- an element at the caret,
- containing method,
- containing class,
- local variables.
### Actions
| ID | Implementation | Extension Point Class |
| ------------------- | ------------------------------------------------------- | ------------------------ |
| `PsiNavigationDemo` | [PsiNavigationDemoAction][file:PsiNavigationDemoAction] | [AnAction][sdk:AnAction] |
*Reference: [Action System in IntelliJ SDK Docs][docs:actions]*
[docs]: https://www.jetbrains.org/intellij/sdk/docs
[docs:actions]: https://www.jetbrains.org/intellij/sdk/docs/basics/action_system.html
[docs:navigating_psi]: https://jetbrains.org/intellij/sdk/docs/basics/architectural_overview/navigating_psi.html
[file:PsiNavigationDemoAction]: ./src/main/java/org/intellij/sdk/psi/PsiNavigationDemoAction.java
[sdk:AnAction]: upsource:///platform/editor-ui-api/src/com/intellij/openapi/actionSystem/AnAction.java

View File

@ -11,46 +11,50 @@ import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiTreeUtil;
public class PsiNavigationDemoAction extends AnAction { public class PsiNavigationDemoAction extends AnAction {
@Override
public void actionPerformed(AnActionEvent anActionEvent) {
Editor editor = anActionEvent.getData(CommonDataKeys.EDITOR);
PsiFile psiFile = anActionEvent.getData(CommonDataKeys.PSI_FILE);
if (editor == null || psiFile == null) return;
int offset = editor.getCaretModel().getOffset();
final StringBuilder infoBuilder = new StringBuilder(); @Override
PsiElement element = psiFile.findElementAt(offset); public void actionPerformed(AnActionEvent anActionEvent) {
infoBuilder.append("Element at caret: ").append(element).append("\n"); Editor editor = anActionEvent.getData(CommonDataKeys.EDITOR);
if (element != null) { PsiFile psiFile = anActionEvent.getData(CommonDataKeys.PSI_FILE);
PsiMethod containingMethod = PsiTreeUtil.getParentOfType(element, PsiMethod.class); if (editor == null || psiFile == null) {
infoBuilder return;
.append("Containing method: ") }
.append(containingMethod != null ? containingMethod.getName() : "none") int offset = editor.getCaretModel().getOffset();
.append("\n");
if (containingMethod != null) {
PsiClass containingClass = containingMethod.getContainingClass();
infoBuilder
.append("Containing class: ")
.append(containingClass != null ? containingClass.getName() : "none")
.append("\n");
infoBuilder.append("Local variables:\n"); final StringBuilder infoBuilder = new StringBuilder();
containingMethod.accept(new JavaRecursiveElementVisitor() { PsiElement element = psiFile.findElementAt(offset);
@Override infoBuilder.append("Element at caret: ").append(element).append("\n");
public void visitLocalVariable(PsiLocalVariable variable) { if (element != null) {
super.visitLocalVariable(variable); PsiMethod containingMethod = PsiTreeUtil.getParentOfType(element, PsiMethod.class);
infoBuilder.append(variable.getName()).append("\n"); infoBuilder
} .append("Containing method: ")
}); .append(containingMethod != null ? containingMethod.getName() : "none")
} .append("\n");
} if (containingMethod != null) {
Messages.showMessageDialog(anActionEvent.getProject(), infoBuilder.toString(), "PSI Info", null); PsiClass containingClass = containingMethod.getContainingClass();
infoBuilder
.append("Containing class: ")
.append(containingClass != null ? containingClass.getName() : "none")
.append("\n");
infoBuilder.append("Local variables:\n");
containingMethod.accept(new JavaRecursiveElementVisitor() {
@Override
public void visitLocalVariable(PsiLocalVariable variable) {
super.visitLocalVariable(variable);
infoBuilder.append(variable.getName()).append("\n");
}
});
}
} }
Messages.showMessageDialog(anActionEvent.getProject(), infoBuilder.toString(), "PSI Info", null);
}
@Override
public void update(AnActionEvent e) {
Editor editor = e.getData(CommonDataKeys.EDITOR);
PsiFile psiFile = e.getData(CommonDataKeys.PSI_FILE);
e.getPresentation().setEnabled(editor != null && psiFile != null);
}
@Override
public void update(AnActionEvent e) {
Editor editor = e.getData(CommonDataKeys.EDITOR);
PsiFile psiFile = e.getData(CommonDataKeys.PSI_FILE);
e.getPresentation().setEnabled(editor != null && psiFile != null);
}
} }

View File

@ -2,16 +2,16 @@
<idea-plugin> <idea-plugin>
<!-- Unique id for this plugin. Must stay constant for the life of the plugin. --> <!-- Unique id for this plugin. Must stay constant for the life of the plugin. -->
<id>org.intellij.sdk.psi</id> <id>org.intellij.sdk.psi</id>
<!-- Text to display as name on Preferences/Settings | Plugin page --> <!-- Text to display as name on Preferences/Settings | Plugin page -->
<name>SDK: PSI Demo</name> <name>SDK: PSI Demo</name>
<!-- Product and plugin compatibility requirements --> <!-- Product and plugin compatibility requirements -->
<depends>com.intellij.modules.java</depends> <depends>com.intellij.modules.java</depends>
<!-- Text to display as description on Preferences/Settings | Plugin page --> <!-- Text to display as description on Preferences/Settings | Plugin page -->
<description> <description>
<![CDATA[ <![CDATA[
Demonstrates working with the PSI Navigation. Demonstrates working with the PSI Navigation.

View File

@ -0,0 +1,24 @@
# Run Configuration Demo [![JetBrains IntelliJ Platform SDK Docs](https://jb.gg/badges/docs.svg)][docs]
*Reference: [Run Configurations in IntelliJ SDK Docs][docs:run_configurations]*
## Quickstart
Run Configuration example project provides an implementation of the `configurationType` extension point responsible for adding new options for the Run/Debug Configurations.
In this example, a new *Demo* configuration is added together with `ConfigurationFactory` instance that collects run/debug properties - `scriptName` in this case.
### Extension Points
| Name | Implementation | Extension Point Class |
| -------------------------------- | --------------------------------------------------------- | ------------------------------------------ |
| `com.intellij.configurationType` | [DemoRunConfigurationType][file:DemoRunConfigurationType] | [ConfigurationType][sdk:ConfigurationType] |
*Reference: [Plugin Extension Points in IntelliJ SDK Docs][docs:ep]*
[docs]: https://www.jetbrains.org/intellij/sdk/docs
[docs:run_configurations]: https://jetbrains.org/intellij/sdk/docs/basics/run_configurations.html
[docs:ep]: https://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_extensions.html
[file:DemoRunConfigurationType]: ./src/main/java/org/jetbrains/sdk/runConfiguration/DemoRunConfigurationType.java
[sdk:ConfigurationType]: upsource:///platform/lang-api/src/com/intellij/execution/configurations/ConfigurationType.java

View File

@ -2,21 +2,23 @@
<idea-plugin> <idea-plugin>
<!-- Unique id for this plugin. Must stay constant for the life of the plugin. --> <!-- Unique id for this plugin. Must stay constant for the life of the plugin. -->
<id>org.intellij.sdk.runConfiguration</id> <id>org.intellij.sdk.runConfiguration</id>
<!-- Text to display as name on Preferences/Settings | Plugin page --> <!-- Text to display as name on Preferences/Settings | Plugin page -->
<name>SDK: Run Configuration Demo</name> <name>SDK: Run Configuration Demo</name>
<!-- Product and plugin compatibility requirements --> <!-- Product and plugin compatibility requirements -->
<depends>com.intellij.modules.platform</depends> <depends>com.intellij.modules.platform</depends>
<!-- Text to display as description on Preferences/Settings | Plugin page --> <!-- Text to display as description on Preferences/Settings | Plugin page -->
<description> <description>
<![CDATA[ <![CDATA[
Illustration of working with run configurations Illustration of working with run configurations
<br> <br>
See the <a href="https://www.jetbrains.org/intellij/sdk/docs/basics/run_configurations.html">Run Configurations</a> for more information. See the
<a href="https://www.jetbrains.org/intellij/sdk/docs/basics/run_configurations.html">Run Configurations</a>
for more information.
]]> ]]>
</description> </description>
<change-notes> <change-notes>

29
settings/README.md Normal file
View File

@ -0,0 +1,29 @@
# Settings Example [![JetBrains IntelliJ Platform SDK Docs](https://jb.gg/badges/docs.svg)][docs]
*Reference: [Settings Tutorial in IntelliJ SDK Docs][docs:settings_tutorial]*
## Quickstart
This project illustrates a custom Application-level Settings through the implementation of:
- `AppSettingsConfigurable` is analogous to a Controller in the MVC model - it interacts with the other two Settings classes and the IntelliJ Platform,
- `AppSettingsState` is like a Model because it stores the Settings persistently,
- `AppSettingsComponent` is similar to a View because it displays and captures edits to the values of the Settings.
### Extension Points
| Name | Implementation | Extension Point Class |
| -------------------------------------- | ------------------------------------------------------- | -------------------------------------------------------- |
| `com.intellij.applicationConfigurable` | [AppSettingsConfigurable][file:AppSettingsConfigurable] | [Configurable][sdk:Configurable] |
| `com.intellij.applicationService` | [AppSettingsState][file:AppSettingsState] | [PersistentStateComponent][sdk:PersistentStateComponent] |
*Reference: [Plugin Extension Points in IntelliJ SDK Docs][docs:ep]*
[docs]: https://www.jetbrains.org/intellij/sdk/docs
[docs:settings_tutorial]: https://jetbrains.org/intellij/sdk/docs/tutorials/settings_tutorial.html
[docs:ep]: https://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_extensions.html
[file:AppSettingsConfigurable]: ./src/main/java/org/intellij/sdk/settings/AppSettingsConfigurable.java
[file:AppSettingsState]: ./src/main/java/org/intellij/sdk/settings/AppSettingsState.java
[sdk:Configurable]: upsource:///platform/platform-api/src/com/intellij/openapi/options/Configurable.java
[sdk:PersistentStateComponent]: upsource:///platform/projectModel-api/src/com/intellij/openapi/components/PersistentStateComponent.java

View File

@ -1,10 +1,14 @@
// Copyright 2000-2020 JetBrains s.r.o. and other contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
plugins { plugins {
id 'java' id 'java'
id 'org.jetbrains.intellij' version '0.4.21' id 'org.jetbrains.intellij' version '0.4.21'
} }
group 'org.intellij.sdk' group 'org.intellij.sdk'
version '0.1' version '1.0.0'
sourceCompatibility = 1.8
repositories { repositories {
mavenCentral() mavenCentral()

View File

@ -14,16 +14,17 @@ import javax.swing.*;
* Supports creating and managing a JPanel for the Settings Dialog. * Supports creating and managing a JPanel for the Settings Dialog.
*/ */
public class AppSettingsComponent { public class AppSettingsComponent {
private final JPanel myMainPanel; private final JPanel myMainPanel;
private final JBTextField myUserNameText = new JBTextField(); private final JBTextField myUserNameText = new JBTextField();
private final JBCheckBox myIdeaUserStatus = new JBCheckBox("Do You Use IntelliJ IDEA? "); private final JBCheckBox myIdeaUserStatus = new JBCheckBox("Do you use IntelliJ IDEA? ");
public AppSettingsComponent() { public AppSettingsComponent() {
myMainPanel = FormBuilder.createFormBuilder() myMainPanel = FormBuilder.createFormBuilder()
.addLabeledComponent(new JBLabel("Enter User Name: "), myUserNameText, 1, false) .addLabeledComponent(new JBLabel("Enter user name: "), myUserNameText, 1, false)
.addComponent(myIdeaUserStatus, 1) .addComponent(myIdeaUserStatus, 1)
.addComponentFillVertically(new JPanel(), 0) .addComponentFillVertically(new JPanel(), 0)
.getPanel(); .getPanel();
} }
public JPanel getPanel() { public JPanel getPanel() {

View File

@ -3,7 +3,6 @@
package org.intellij.sdk.settings; package org.intellij.sdk.settings;
import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.options.ConfigurationException;
import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -11,8 +10,9 @@ import javax.swing.*;
/** /**
* Provides controller functionality for application settings. * Provides controller functionality for application settings.
*/ */
public class AppSettingsConfigurable implements Configurable { public class AppSettingsConfigurable implements Configurable {
private AppSettingsComponent mySettingsComponent; private AppSettingsComponent mySettingsComponent;
// A default constructor with no arguments is required because this implementation // A default constructor with no arguments is required because this implementation
@ -45,7 +45,7 @@ public class AppSettingsConfigurable implements Configurable {
} }
@Override @Override
public void apply() throws ConfigurationException { public void apply() {
AppSettingsState settings = AppSettingsState.getInstance(); AppSettingsState settings = AppSettingsState.getInstance();
settings.userId = mySettingsComponent.getUserNameText(); settings.userId = mySettingsComponent.getUserNameText();
settings.ideaStatus = mySettingsComponent.getIdeaUserStatus(); settings.ideaStatus = mySettingsComponent.getIdeaUserStatus();

View File

@ -39,4 +39,4 @@ public class AppSettingsState implements PersistentStateComponent<AppSettingsSta
XmlSerializerUtil.copyBean(state, this); XmlSerializerUtil.copyBean(state, this);
} }
} }

View File

@ -2,20 +2,21 @@
<idea-plugin> <idea-plugin>
<!-- Unique id for this plugin. Must stay constant for the life of the plugin. --> <!-- Unique id for this plugin. Must stay constant for the life of the plugin. -->
<id>org.intellij.sdk.settings</id> <id>org.intellij.sdk.settings</id>
<!-- Text to display as name on Preferences/Settings | Plugin page --> <!-- Text to display as name on Preferences/Settings | Plugin page -->
<name>SDK: Settings Example Project</name> <name>SDK: Settings Example</name>
<!-- please see https://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/plugin_compatibility.html <!-- please see https://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/plugin_compatibility.html
on how to target different products --> on how to target different products -->
<depends>com.intellij.modules.platform</depends> <depends>com.intellij.modules.platform</depends>
<!-- Text to display as description on Preferences/Settings | Plugin page --> <!-- Text to display as description on Preferences/Settings | Plugin page -->
<description> <description>
<![CDATA[ <![CDATA[
Demonstrates implementing a custom settings panel.<br>Adds a settings panel to the <b>Settings/Preferences</b> panel under <b>Tools</b>. Demonstrates implementing a custom settings panel.<br>Adds a settings panel to the <b>Settings/Preferences</b>
panel under <b>Tools</b>.
]]> ]]>
</description> </description>
<change-notes> <change-notes>
@ -36,4 +37,4 @@
<applicationService serviceImplementation="org.intellij.sdk.settings.AppSettingsState"/> <applicationService serviceImplementation="org.intellij.sdk.settings.AppSettingsState"/>
</extensions> </extensions>
</idea-plugin> </idea-plugin>

View File

@ -0,0 +1,71 @@
# Simple Language Sample [![JetBrains IntelliJ Platform SDK Docs](https://jb.gg/badges/docs.svg)][docs]
*Reference: [Custom Language Support Tutorial in IntelliJ SDK Docs][docs:custom_language_support_tutorial]*
## Quickstart
Defines a new language, _Simple language_ with support for syntax highlighting, annotations, code completion, and other features.
### Extension Points
| Name | Implementation | Extension Point Class |
| --------------------------------------------- | --------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| `com.intellij.fileType` | [SimpleFileType][file:SimpleFileType] | [LanguageFileType][sdk:LanguageFileType] |
| `com.intellij.lang.parserDefinition` | [SimpleParserDefinition][file:SimpleParserDefinition] | [ParserDefinition][sdk:ParserDefinition] |
| `com.intellij.lang.syntaxHighlighterFactory` | [SimpleSyntaxHighlighterFactory][file:SimpleSyntaxHighlighterFactory] | [SyntaxHighlighterFactory][sdk:SyntaxHighlighterFactory] |
| `com.intellij.colorSettingsPage` | [SimpleColorSettingsPage][file:SimpleColorSettingsPage] | [ColorSettingsPage][sdk:ColorSettingsPage] |
| `com.intellij.annotator` | [SimpleAnnotator][file:SimpleAnnotator] | [Annotator][sdk:Annotator] |
| `com.intellij.codeInsight.lineMarkerProvider` | [SimpleLineMarkerProvider][file:SimpleLineMarkerProvider] | [RelatedItemLineMarkerProvider][sdk:RelatedItemLineMarkerProvider] |
| `com.intellij.completion.contributor` | [SimpleCompletionContributor][file:SimpleCompletionContributor] | [CompletionContributor][sdk:CompletionContributor] |
| `com.intellij.psi.referenceContributor` | [SimpleReferenceContributor][file:SimpleReferenceContributor] | [PsiReferenceContributor][sdk:PsiReferenceContributor] |
| `com.intellij.lang.refactoringSupport` | [SimpleRefactoringSupportProvider][file:SimpleRefactoringSupportProvider] | [RefactoringSupportProvider][sdk:RefactoringSupportProvider] |
| `com.intellij.lang.findUsagesProvider` | [SimpleFindUsagesProvider][file:SimpleFindUsagesProvider] | [FindUsagesProvider][sdk:FindUsagesProvider] |
| `com.intellij.lang.foldingBuilder` | [SimpleFoldingBuilder][file:SimpleFoldingBuilder] | [FoldingBuilderEx][sdk:FoldingBuilderEx] |
| `com.intellij.gotoSymbolContributor` | [SimpleChooseByNameContributor][file:SimpleChooseByNameContributor] | [ChooseByNameContributor][sdk:ChooseByNameContributor] |
| `com.intellij.lang.psiStructureViewFactory` | [SimpleStructureViewFactory][file:SimpleStructureViewFactory] | [PsiStructureViewFactory][sdk:PsiStructureViewFactory] |
| `com.intellij.lang.formatter` | [SimpleFormattingModelBuilder][file:SimpleFormattingModelBuilder] | [FormattingModelBuilder][sdk:FormattingModelBuilder] |
| `com.intellij.codeStyleSettingsProvider` | [SimpleCodeStyleSettingsProvider][file:SimpleCodeStyleSettingsProvider] | [CodeStyleSettingsProvider][sdk:CodeStyleSettingsProvider] |
| `com.intellij.langCodeStyleSettingsProvider` | [SimpleLanguageCodeStyleSettingsProvider][file:SimpleLanguageCodeStyleSettingsProvider] | [LanguageCodeStyleSettingsProvider][sdk:LanguageCodeStyleSettingsProvider] |
| `com.intellij.lang.commenter` | [SimpleCommenter][file:SimpleCommenter] | [Commenter][sdk:Commenter] |
*Reference: [Plugin Extension Points in IntelliJ SDK Docs][docs:ep]*
[docs]: https://www.jetbrains.org/intellij/sdk/docs
[docs:custom_language_support_tutorial]: https://jetbrains.org/intellij/sdk/docs/tutorials/custom_language_support_tutorial.html
[docs:ep]: https://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_extensions.html
[file:SimpleFileType]: ./src/main/java/org/intellij/sdk/language/SimpleFileType.java
[file:SimpleParserDefinition]: ./src/main/java/org/intellij/sdk/language/SimpleParserDefinition.java
[file:SimpleSyntaxHighlighterFactory]: ./src/main/java/org/intellij/sdk/language/SimpleSyntaxHighlighterFactory.java
[file:SimpleColorSettingsPage]: ./src/main/java/org/intellij/sdk/language/SimpleColorSettingsPage.java
[file:SimpleAnnotator]: ./src/main/java/org/intellij/sdk/language/SimpleAnnotator.java
[file:SimpleLineMarkerProvider]: ./src/main/java/org/intellij/sdk/language/SimpleLineMarkerProvider.java
[file:SimpleCompletionContributor]: ./src/main/java/org/intellij/sdk/language/SimpleCompletionContributor.java
[file:SimpleReferenceContributor]: ./src/main/java/org/intellij/sdk/language/SimpleReferenceContributor.java
[file:SimpleRefactoringSupportProvider]: ./src/main/java/org/intellij/sdk/language/SimpleRefactoringSupportProvider.java
[file:SimpleFindUsagesProvider]: ./src/main/java/org/intellij/sdk/language/SimpleFindUsagesProvider.java
[file:SimpleFoldingBuilder]: ./src/main/java/org/intellij/sdk/language/SimpleFoldingBuilder.java
[file:SimpleChooseByNameContributor]: ./src/main/java/org/intellij/sdk/language/SimpleChooseByNameContributor.java
[file:SimpleStructureViewFactory]: ./src/main/java/org/intellij/sdk/language/SimpleStructureViewFactory.java
[file:SimpleFormattingModelBuilder]: ./src/main/java/org/intellij/sdk/language/SimpleFormattingModelBuilder.java
[file:SimpleCodeStyleSettingsProvider]: ./src/main/java/org/intellij/sdk/language/SimpleCodeStyleSettingsProvider.java
[file:SimpleLanguageCodeStyleSettingsProvider]: ./src/main/java/org/intellij/sdk/language/SimpleLanguageCodeStyleSettingsProvider.java
[file:SimpleCommenter]: ./src/main/java/org/intellij/sdk/language/SimpleCommenter.java
[sdk:LanguageFileType]: upsource:///platform/core-api/src/com/intellij/openapi/fileTypes/LanguageFileType.java
[sdk:ParserDefinition]: upsource:///platform/core-api/src/com/intellij/lang/ParserDefinition.java
[sdk:SyntaxHighlighterFactory]: upsource:///platform/editor-ui-api/src/com/intellij/openapi/fileTypes/SyntaxHighlighterFactory.java
[sdk:ColorSettingsPage]: upsource:///platform/platform-api/src/com/intellij/openapi/options/colors/ColorSettingsPage.java
[sdk:Annotator]: upsource:///platform/analysis-api/src/com/intellij/lang/annotation/Annotator.java
[sdk:RelatedItemLineMarkerProvider]: upsource:///platform/lang-api/src/com/intellij/codeInsight/daemon/RelatedItemLineMarkerProvider.java
[sdk:CompletionContributor]: upsource:///platform/analysis-api/src/com/intellij/codeInsight/completion/CompletionContributor.java
[sdk:PsiReferenceContributor]: upsource:///platform/core-api/src/com/intellij/psi/PsiReferenceContributor.java
[sdk:RefactoringSupportProvider]: upsource:///platform/lang-api/src/com/intellij/lang/refactoring/RefactoringSupportProvider.java
[sdk:FindUsagesProvider]: upsource:///platform/indexing-api/src/com/intellij/lang/findUsages/FindUsagesProvider.java
[sdk:FoldingBuilderEx]: upsource:///platform/core-api/src/com/intellij/lang/folding/FoldingBuilderEx.java
[sdk:ChooseByNameContributor]: upsource:///platform/lang-api/src/com/intellij/navigation/ChooseByNameContributor.java
[sdk:PsiStructureViewFactory]: upsource:///platform/editor-ui-api/src/com/intellij/lang/PsiStructureViewFactory.java
[sdk:FormattingModelBuilder]: upsource:///platform/lang-api/src/com/intellij/formatting/FormattingModelBuilder.java
[sdk:CodeStyleSettingsProvider]: upsource:///platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettingsProvider.java
[sdk:LanguageCodeStyleSettingsProvider]: upsource:///platform/lang-api/src/com/intellij/psi/codeStyle/LanguageCodeStyleSettingsProvider.java
[sdk:Commenter]: upsource:///platform/core-api/src/com/intellij/lang/Commenter.java

View File

@ -11,7 +11,7 @@ import com.intellij.psi.TokenType;
/** /**
* This class is a scanner generated by * This class is a scanner generated by
* <a href="http://www.jflex.de/">JFlex</a> 1.7.0 * <a href="https://www.jflex.de/">JFlex</a> 1.7.0
* from the specification file <tt>Simple.flex</tt> * from the specification file <tt>Simple.flex</tt>
*/ */
class SimpleLexer implements FlexLexer { class SimpleLexer implements FlexLexer {

View File

@ -20,6 +20,7 @@ import static com.intellij.lang.annotation.HighlightSeverity.INFORMATION;
public class SimpleAnnotator implements Annotator { public class SimpleAnnotator implements Annotator {
// Define strings for the Simple language prefix - used for annotations, line markers, etc. // Define strings for the Simple language prefix - used for annotations, line markers, etc.
public static final String SIMPLE_PREFIX_STR = "simple"; public static final String SIMPLE_PREFIX_STR = "simple";
public static final String SIMPLE_SEPARATOR_STR = ":"; public static final String SIMPLE_SEPARATOR_STR = ":";
@ -27,12 +28,16 @@ public class SimpleAnnotator implements Annotator {
@Override @Override
public void annotate(@NotNull final PsiElement element, @NotNull AnnotationHolder holder) { public void annotate(@NotNull final PsiElement element, @NotNull AnnotationHolder holder) {
// Ensure the Psi Element is an expression // Ensure the Psi Element is an expression
if (!(element instanceof PsiLiteralExpression)) return; if (!(element instanceof PsiLiteralExpression)) {
return;
}
// Ensure the Psi element contains a string that starts with the key and separator // Ensure the Psi element contains a string that starts with the key and separator
PsiLiteralExpression literalExpression = (PsiLiteralExpression) element; PsiLiteralExpression literalExpression = (PsiLiteralExpression) element;
String value = literalExpression.getValue() instanceof String ? (String) literalExpression.getValue() : null; String value = literalExpression.getValue() instanceof String ? (String) literalExpression.getValue() : null;
if ((value == null) || !value.startsWith(SIMPLE_PREFIX_STR + SIMPLE_SEPARATOR_STR)) return; if ((value == null) || !value.startsWith(SIMPLE_PREFIX_STR + SIMPLE_SEPARATOR_STR)) {
return;
}
// Define the text ranges (start is inclusive, end is exclusive) // Define the text ranges (start is inclusive, end is exclusive)
// "simple:key" // "simple:key"
@ -47,8 +52,10 @@ public class SimpleAnnotator implements Annotator {
List<SimpleProperty> properties = SimpleUtil.findProperties(project, possibleProperties); List<SimpleProperty> properties = SimpleUtil.findProperties(project, possibleProperties);
// Set the annotations using the text ranges - Normally there would be one range, set by the element itself. // Set the annotations using the text ranges - Normally there would be one range, set by the element itself.
holder.newAnnotation(INFORMATION, "").range(prefixRange).textAttributes(DefaultLanguageHighlighterColors.KEYWORD).create(); holder.newAnnotation(INFORMATION, "")
holder.newAnnotation(INFORMATION, "").range(separatorRange).textAttributes(SimpleSyntaxHighlighter.SEPARATOR).create(); .range(prefixRange).textAttributes(DefaultLanguageHighlighterColors.KEYWORD).create();
holder.newAnnotation(INFORMATION, "")
.range(separatorRange).textAttributes(SimpleSyntaxHighlighter.SEPARATOR).create();
if (properties.isEmpty()) { if (properties.isEmpty()) {
// No well-formed property found following the key-separator // No well-formed property found following the key-separator
AnnotationBuilder builder = holder.newAnnotation(ERROR, "Unresolved property").range(keyRange); AnnotationBuilder builder = holder.newAnnotation(ERROR, "Unresolved property").range(keyRange);

View File

@ -13,14 +13,15 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
public class SimpleBlock extends AbstractBlock { public class SimpleBlock extends AbstractBlock {
private final SpacingBuilder spacingBuilder; private final SpacingBuilder spacingBuilder;
protected SimpleBlock(@NotNull ASTNode node, @Nullable Wrap wrap, @Nullable Alignment alignment, protected SimpleBlock(@NotNull ASTNode node, @Nullable Wrap wrap, @Nullable Alignment alignment,
SpacingBuilder spacingBuilder) { SpacingBuilder spacingBuilder) {
super(node, wrap, alignment); super(node, wrap, alignment);
this.spacingBuilder = spacingBuilder; this.spacingBuilder = spacingBuilder;
} }
@Override @Override
protected List<Block> buildChildren() { protected List<Block> buildChildren() {
List<Block> blocks = new ArrayList<>(); List<Block> blocks = new ArrayList<>();
@ -28,27 +29,28 @@ public class SimpleBlock extends AbstractBlock {
while (child != null) { while (child != null) {
if (child.getElementType() != TokenType.WHITE_SPACE) { if (child.getElementType() != TokenType.WHITE_SPACE) {
Block block = new SimpleBlock(child, Wrap.createWrap(WrapType.NONE, false), Alignment.createAlignment(), Block block = new SimpleBlock(child, Wrap.createWrap(WrapType.NONE, false), Alignment.createAlignment(),
spacingBuilder); spacingBuilder);
blocks.add(block); blocks.add(block);
} }
child = child.getTreeNext(); child = child.getTreeNext();
} }
return blocks; return blocks;
} }
@Override @Override
public Indent getIndent() { public Indent getIndent() {
return Indent.getNoneIndent(); return Indent.getNoneIndent();
} }
@Nullable @Nullable
@Override @Override
public Spacing getSpacing(@Nullable Block child1, @NotNull Block child2) { public Spacing getSpacing(@Nullable Block child1, @NotNull Block child2) {
return spacingBuilder.getSpacing(this, child1, child2); return spacingBuilder.getSpacing(this, child1, child2);
} }
@Override @Override
public boolean isLeaf() { public boolean isLeaf() {
return myNode.getFirstChildNode() == null; return myNode.getFirstChildNode() == null;
} }
} }

View File

@ -2,14 +2,17 @@
package org.intellij.sdk.language; package org.intellij.sdk.language;
import com.intellij.navigation.*; import com.intellij.navigation.ChooseByNameContributor;
import com.intellij.navigation.NavigationItem;
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project;
import org.intellij.sdk.language.psi.SimpleProperty; import org.intellij.sdk.language.psi.SimpleProperty;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.*; import java.util.ArrayList;
import java.util.List;
public class SimpleChooseByNameContributor implements ChooseByNameContributor { public class SimpleChooseByNameContributor implements ChooseByNameContributor {
@NotNull @NotNull
@Override @Override
public String[] getNames(Project project, boolean includeNonProjectItems) { public String[] getNames(Project project, boolean includeNonProjectItems) {
@ -22,7 +25,7 @@ public class SimpleChooseByNameContributor implements ChooseByNameContributor {
} }
return names.toArray(new String[names.size()]); return names.toArray(new String[names.size()]);
} }
@NotNull @NotNull
@Override @Override
public NavigationItem[] getItemsByName(String name, String pattern, Project project, boolean includeNonProjectItems) { public NavigationItem[] getItemsByName(String name, String pattern, Project project, boolean includeNonProjectItems) {
@ -30,4 +33,5 @@ public class SimpleChooseByNameContributor implements ChooseByNameContributor {
List<SimpleProperty> properties = SimpleUtil.findProperties(project, name); List<SimpleProperty> properties = SimpleUtil.findProperties(project, name);
return properties.toArray(new NavigationItem[properties.size()]); return properties.toArray(new NavigationItem[properties.size()]);
} }
}
}

View File

@ -2,10 +2,13 @@
package org.intellij.sdk.language; package org.intellij.sdk.language;
import com.intellij.psi.codeStyle.*; import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CustomCodeStyleSettings;
public class SimpleCodeStyleSettings extends CustomCodeStyleSettings { public class SimpleCodeStyleSettings extends CustomCodeStyleSettings {
public SimpleCodeStyleSettings(CodeStyleSettings settings) { public SimpleCodeStyleSettings(CodeStyleSettings settings) {
super("SimpleCodeStyleSettings", settings); super("SimpleCodeStyleSettings", settings);
} }
}
}

View File

@ -2,23 +2,30 @@
package org.intellij.sdk.language; package org.intellij.sdk.language;
import com.intellij.application.options.*; import com.intellij.application.options.CodeStyleAbstractConfigurable;
import com.intellij.psi.codeStyle.*; import com.intellij.application.options.CodeStyleAbstractPanel;
import org.jetbrains.annotations.*; import com.intellij.application.options.TabbedLanguageCodeStylePanel;
import com.intellij.psi.codeStyle.CodeStyleConfigurable;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CodeStyleSettingsProvider;
import com.intellij.psi.codeStyle.CustomCodeStyleSettings;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class SimpleCodeStyleSettingsProvider extends CodeStyleSettingsProvider { public class SimpleCodeStyleSettingsProvider extends CodeStyleSettingsProvider {
@Override @Override
public CustomCodeStyleSettings createCustomSettings(CodeStyleSettings settings) { public CustomCodeStyleSettings createCustomSettings(CodeStyleSettings settings) {
return new SimpleCodeStyleSettings(settings); return new SimpleCodeStyleSettings(settings);
} }
@Nullable @Nullable
@Override @Override
public String getConfigurableDisplayName() { public String getConfigurableDisplayName() {
return "Simple"; return "Simple";
} }
@NotNull @NotNull
public CodeStyleConfigurable createConfigurable(@NotNull CodeStyleSettings settings, @NotNull CodeStyleSettings modelSettings) { public CodeStyleConfigurable createConfigurable(@NotNull CodeStyleSettings settings, @NotNull CodeStyleSettings modelSettings) {
return new CodeStyleAbstractConfigurable(settings, modelSettings, this.getConfigurableDisplayName()) { return new CodeStyleAbstractConfigurable(settings, modelSettings, this.getConfigurableDisplayName()) {
@ -28,10 +35,13 @@ public class SimpleCodeStyleSettingsProvider extends CodeStyleSettingsProvider {
} }
}; };
} }
private static class SimpleCodeStyleMainPanel extends TabbedLanguageCodeStylePanel { private static class SimpleCodeStyleMainPanel extends TabbedLanguageCodeStylePanel {
public SimpleCodeStyleMainPanel(CodeStyleSettings currentSettings, CodeStyleSettings settings) { public SimpleCodeStyleMainPanel(CodeStyleSettings currentSettings, CodeStyleSettings settings) {
super(SimpleLanguage.INSTANCE, currentSettings, settings); super(SimpleLanguage.INSTANCE, currentSettings, settings);
} }
} }
}
}

View File

@ -4,70 +4,75 @@ package org.intellij.sdk.language;
import com.intellij.openapi.editor.colors.TextAttributesKey; import com.intellij.openapi.editor.colors.TextAttributesKey;
import com.intellij.openapi.fileTypes.SyntaxHighlighter; import com.intellij.openapi.fileTypes.SyntaxHighlighter;
import com.intellij.openapi.options.colors.*; import com.intellij.openapi.options.colors.AttributesDescriptor;
import org.jetbrains.annotations.*; import com.intellij.openapi.options.colors.ColorDescriptor;
import com.intellij.openapi.options.colors.ColorSettingsPage;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*; import javax.swing.*;
import java.util.Map; import java.util.Map;
public class SimpleColorSettingsPage implements ColorSettingsPage { public class SimpleColorSettingsPage implements ColorSettingsPage {
private static final AttributesDescriptor[] DESCRIPTORS = new AttributesDescriptor[]{ private static final AttributesDescriptor[] DESCRIPTORS = new AttributesDescriptor[]{
new AttributesDescriptor("Key", SimpleSyntaxHighlighter.KEY), new AttributesDescriptor("Key", SimpleSyntaxHighlighter.KEY),
new AttributesDescriptor("Separator", SimpleSyntaxHighlighter.SEPARATOR), new AttributesDescriptor("Separator", SimpleSyntaxHighlighter.SEPARATOR),
new AttributesDescriptor("Value", SimpleSyntaxHighlighter.VALUE), new AttributesDescriptor("Value", SimpleSyntaxHighlighter.VALUE),
new AttributesDescriptor("Bad Value", SimpleSyntaxHighlighter.BAD_CHARACTER) new AttributesDescriptor("Bad Value", SimpleSyntaxHighlighter.BAD_CHARACTER)
}; };
@Nullable @Nullable
@Override @Override
public Icon getIcon() { public Icon getIcon() {
return SimpleIcons.FILE; return SimpleIcons.FILE;
} }
@NotNull @NotNull
@Override @Override
public SyntaxHighlighter getHighlighter() { public SyntaxHighlighter getHighlighter() {
return new SimpleSyntaxHighlighter(); return new SimpleSyntaxHighlighter();
} }
@NotNull @NotNull
@Override @Override
public String getDemoText() { public String getDemoText() {
return "# You are reading the \".properties\" entry.\n" + return "# You are reading the \".properties\" entry.\n" +
"! The exclamation mark can also mark text as comments.\n" + "! The exclamation mark can also mark text as comments.\n" +
"website = https://en.wikipedia.org/\n" + "website = https://en.wikipedia.org/\n" +
"language = English\n" + "language = English\n" +
"# The backslash below tells the application to continue reading\n" + "# The backslash below tells the application to continue reading\n" +
"# the value onto the next line.\n" + "# the value onto the next line.\n" +
"message = Welcome to \\\n" + "message = Welcome to \\\n" +
" Wikipedia!\n" + " Wikipedia!\n" +
"# Add spaces to the key\n" + "# Add spaces to the key\n" +
"key\\ with\\ spaces = This is the value that could be looked up with the key \"key with spaces\".\n" + "key\\ with\\ spaces = This is the value that could be looked up with the key \"key with spaces\".\n" +
"# Unicode\n" + "# Unicode\n" +
"tab : \\u0009"; "tab : \\u0009";
} }
@Nullable @Nullable
@Override @Override
public Map<String, TextAttributesKey> getAdditionalHighlightingTagToDescriptorMap() { public Map<String, TextAttributesKey> getAdditionalHighlightingTagToDescriptorMap() {
return null; return null;
} }
@NotNull @NotNull
@Override @Override
public AttributesDescriptor[] getAttributeDescriptors() { public AttributesDescriptor[] getAttributeDescriptors() {
return DESCRIPTORS; return DESCRIPTORS;
} }
@NotNull @NotNull
@Override @Override
public ColorDescriptor[] getColorDescriptors() { public ColorDescriptor[] getColorDescriptors() {
return ColorDescriptor.EMPTY_ARRAY; return ColorDescriptor.EMPTY_ARRAY;
} }
@NotNull @NotNull
@Override @Override
public String getDisplayName() { public String getDisplayName() {
return "Simple"; return "Simple";
} }
} }

View File

@ -6,33 +6,35 @@ import com.intellij.lang.Commenter;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
public class SimpleCommenter implements Commenter { public class SimpleCommenter implements Commenter {
@Nullable @Nullable
@Override @Override
public String getLineCommentPrefix() { public String getLineCommentPrefix() {
return "#"; return "#";
} }
@Nullable @Nullable
@Override @Override
public String getBlockCommentPrefix() { public String getBlockCommentPrefix() {
return ""; return "";
} }
@Nullable @Nullable
@Override @Override
public String getBlockCommentSuffix() { public String getBlockCommentSuffix() {
return null; return null;
} }
@Nullable @Nullable
@Override @Override
public String getCommentedBlockCommentPrefix() { public String getCommentedBlockCommentPrefix() {
return null; return null;
} }
@Nullable @Nullable
@Override @Override
public String getCommentedBlockCommentSuffix() { public String getCommentedBlockCommentSuffix() {
return null; return null;
} }
}
}

View File

@ -22,4 +22,5 @@ public class SimpleCompletionContributor extends CompletionContributor {
} }
); );
} }
} }

View File

@ -28,40 +28,41 @@ import org.jetbrains.annotations.NotNull;
import java.util.Collection; import java.util.Collection;
class SimpleCreatePropertyQuickFix extends BaseIntentionAction { class SimpleCreatePropertyQuickFix extends BaseIntentionAction {
private final String key; private final String key;
SimpleCreatePropertyQuickFix(String key) { SimpleCreatePropertyQuickFix(String key) {
this.key = key; this.key = key;
} }
@NotNull @NotNull
@Override @Override
public String getText() { public String getText() {
return "Create property"; return "Create property";
} }
@NotNull @NotNull
@Override @Override
public String getFamilyName() { public String getFamilyName() {
return "Simple properties"; return "Simple properties";
} }
@Override @Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
return true; return true;
} }
@Override @Override
public void invoke(@NotNull final Project project, final Editor editor, PsiFile file) throws public void invoke(@NotNull final Project project, final Editor editor, PsiFile file) throws
IncorrectOperationException { IncorrectOperationException {
ApplicationManager.getApplication().invokeLater(() -> { ApplicationManager.getApplication().invokeLater(() -> {
Collection<VirtualFile> virtualFiles = Collection<VirtualFile> virtualFiles =
FileTypeIndex.getFiles(SimpleFileType.INSTANCE, GlobalSearchScope.allScope(project) ); FileTypeIndex.getFiles(SimpleFileType.INSTANCE, GlobalSearchScope.allScope(project));
if (virtualFiles.size() == 1) { if (virtualFiles.size() == 1) {
createProperty(project, virtualFiles.iterator().next()); createProperty(project, virtualFiles.iterator().next());
} else { } else {
final FileChooserDescriptor descriptor = final FileChooserDescriptor descriptor =
FileChooserDescriptorFactory.createSingleFileDescriptor(SimpleFileType.INSTANCE); FileChooserDescriptorFactory.createSingleFileDescriptor(SimpleFileType.INSTANCE);
descriptor.setRoots(ProjectUtil.guessProjectDir(project)); descriptor.setRoots(ProjectUtil.guessProjectDir(project));
final VirtualFile file1 = FileChooser.chooseFile(descriptor, project, null); final VirtualFile file1 = FileChooser.chooseFile(descriptor, project, null);
if (file1 != null) { if (file1 != null) {
@ -70,7 +71,7 @@ class SimpleCreatePropertyQuickFix extends BaseIntentionAction {
} }
}); });
} }
private void createProperty(final Project project, final VirtualFile file) { private void createProperty(final Project project, final VirtualFile file) {
WriteCommandAction.writeCommandAction(project).run(() -> { WriteCommandAction.writeCommandAction(project).run(() -> {
SimpleFile simpleFile = (SimpleFile) PsiManager.getInstance(project).findFile(file); SimpleFile simpleFile = (SimpleFile) PsiManager.getInstance(project).findFile(file);
@ -86,4 +87,5 @@ class SimpleCreatePropertyQuickFix extends BaseIntentionAction {
FileEditorManager.getInstance(project).getSelectedTextEditor().getCaretModel().moveCaretRelatively(2, 0, false, false, false); FileEditorManager.getInstance(project).getSelectedTextEditor().getCaretModel().moveCaretRelatively(2, 0, false, false, false);
}); });
} }
} }

View File

@ -9,6 +9,7 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*; import javax.swing.*;
public class SimpleFileType extends LanguageFileType { public class SimpleFileType extends LanguageFileType {
public static final SimpleFileType INSTANCE = new SimpleFileType(); public static final SimpleFileType INSTANCE = new SimpleFileType();
private SimpleFileType() { private SimpleFileType() {
@ -38,4 +39,5 @@ public class SimpleFileType extends LanguageFileType {
public Icon getIcon() { public Icon getIcon() {
return SimpleIcons.FILE; return SimpleIcons.FILE;
} }
} }

View File

@ -11,8 +11,10 @@ import org.jetbrains.annotations.NotNull;
* for versions of the IntelliJ Platform prior to v2019.2 * for versions of the IntelliJ Platform prior to v2019.2
*/ */
public class SimpleFileTypeFactory extends FileTypeFactory { public class SimpleFileTypeFactory extends FileTypeFactory {
@Override @Override
public void createFileTypes(@NotNull FileTypeConsumer fileTypeConsumer) { public void createFileTypes(@NotNull FileTypeConsumer fileTypeConsumer) {
fileTypeConsumer.consume(SimpleFileType.INSTANCE); fileTypeConsumer.consume(SimpleFileType.INSTANCE);
} }
} }

View File

@ -2,35 +2,39 @@
package org.intellij.sdk.language; package org.intellij.sdk.language;
import com.intellij.lang.cacheBuilder.*; import com.intellij.lang.cacheBuilder.DefaultWordsScanner;
import com.intellij.lang.cacheBuilder.WordsScanner;
import com.intellij.lang.findUsages.FindUsagesProvider; import com.intellij.lang.findUsages.FindUsagesProvider;
import com.intellij.psi.*; import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiNamedElement;
import com.intellij.psi.tree.TokenSet; import com.intellij.psi.tree.TokenSet;
import org.intellij.sdk.language.psi.SimpleProperty; import org.intellij.sdk.language.psi.SimpleProperty;
import org.intellij.sdk.language.psi.SimpleTypes; import org.intellij.sdk.language.psi.SimpleTypes;
import org.jetbrains.annotations.*; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class SimpleFindUsagesProvider implements FindUsagesProvider { public class SimpleFindUsagesProvider implements FindUsagesProvider {
@Nullable @Nullable
@Override @Override
public WordsScanner getWordsScanner() { public WordsScanner getWordsScanner() {
return new DefaultWordsScanner(new SimpleLexerAdapter(), return new DefaultWordsScanner(new SimpleLexerAdapter(),
TokenSet.create(SimpleTypes.KEY), TokenSet.create(SimpleTypes.KEY),
TokenSet.create(SimpleTypes.COMMENT), TokenSet.create(SimpleTypes.COMMENT),
TokenSet.EMPTY); TokenSet.EMPTY);
} }
@Override @Override
public boolean canFindUsagesFor(@NotNull PsiElement psiElement) { public boolean canFindUsagesFor(@NotNull PsiElement psiElement) {
return psiElement instanceof PsiNamedElement; return psiElement instanceof PsiNamedElement;
} }
@Nullable @Nullable
@Override @Override
public String getHelpId(@NotNull PsiElement psiElement) { public String getHelpId(@NotNull PsiElement psiElement) {
return null; return null;
} }
@NotNull @NotNull
@Override @Override
public String getType(@NotNull PsiElement element) { public String getType(@NotNull PsiElement element) {
@ -40,7 +44,7 @@ public class SimpleFindUsagesProvider implements FindUsagesProvider {
return ""; return "";
} }
} }
@NotNull @NotNull
@Override @Override
public String getDescriptiveName(@NotNull PsiElement element) { public String getDescriptiveName(@NotNull PsiElement element) {
@ -50,7 +54,7 @@ public class SimpleFindUsagesProvider implements FindUsagesProvider {
return ""; return "";
} }
} }
@NotNull @NotNull
@Override @Override
public String getNodeText(@NotNull PsiElement element, boolean useFullName) { public String getNodeText(@NotNull PsiElement element, boolean useFullName) {
@ -60,4 +64,5 @@ public class SimpleFindUsagesProvider implements FindUsagesProvider {
return ""; return "";
} }
} }
}
}

View File

@ -3,62 +3,75 @@
package org.intellij.sdk.language; package org.intellij.sdk.language;
import com.intellij.lang.ASTNode; import com.intellij.lang.ASTNode;
import com.intellij.lang.folding.*; import com.intellij.lang.folding.FoldingBuilderEx;
import com.intellij.openapi.editor.*; import com.intellij.lang.folding.FoldingDescriptor;
import com.intellij.openapi.project.*; import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.FoldingGroup;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*; import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiLiteralExpression;
import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiTreeUtil;
import org.intellij.sdk.language.psi.SimpleProperty; import org.intellij.sdk.language.psi.SimpleProperty;
import org.jetbrains.annotations.*; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*; import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class SimpleFoldingBuilder extends FoldingBuilderEx implements DumbAware { public class SimpleFoldingBuilder extends FoldingBuilderEx implements DumbAware {
@NotNull @NotNull
@Override @Override
public FoldingDescriptor[] buildFoldRegions(@NotNull PsiElement root, @NotNull Document document, boolean quick) { public FoldingDescriptor[] buildFoldRegions(@NotNull PsiElement root, @NotNull Document document, boolean quick) {
// Initialize the group of folding regions that will expand/collapse together. // Initialize the group of folding regions that will expand/collapse together.
FoldingGroup group = FoldingGroup.newGroup(SimpleAnnotator.SIMPLE_PREFIX_STR); FoldingGroup group = FoldingGroup.newGroup(SimpleAnnotator.SIMPLE_PREFIX_STR);
// Initialize the list of folding regions // Initialize the list of folding regions
List< FoldingDescriptor > descriptors = new ArrayList<>(); List<FoldingDescriptor> descriptors = new ArrayList<>();
// Get a collection of the literal expressions in the document below root // Get a collection of the literal expressions in the document below root
Collection< PsiLiteralExpression > literalExpressions = Collection<PsiLiteralExpression> literalExpressions =
PsiTreeUtil.findChildrenOfType(root, PsiLiteralExpression.class); PsiTreeUtil.findChildrenOfType(root, PsiLiteralExpression.class);
// Evaluate the collection // Evaluate the collection
for ( final PsiLiteralExpression literalExpression : literalExpressions ) { for (final PsiLiteralExpression literalExpression : literalExpressions) {
String value = literalExpression.getValue() instanceof String ? (String) literalExpression.getValue() : null; String value = literalExpression.getValue() instanceof String ? (String) literalExpression.getValue() : null;
if ( value != null && value.startsWith(SimpleAnnotator.SIMPLE_PREFIX_STR + SimpleAnnotator.SIMPLE_SEPARATOR_STR) ) { if (value != null && value.startsWith(SimpleAnnotator.SIMPLE_PREFIX_STR + SimpleAnnotator.SIMPLE_SEPARATOR_STR)) {
Project project = literalExpression.getProject(); Project project = literalExpression.getProject();
String key = value.substring(SimpleAnnotator.SIMPLE_PREFIX_STR.length() + SimpleAnnotator.SIMPLE_SEPARATOR_STR.length()); String key = value.substring(
SimpleAnnotator.SIMPLE_PREFIX_STR.length() + SimpleAnnotator.SIMPLE_SEPARATOR_STR.length()
);
// Get a list of all properties for a given key in the project // Get a list of all properties for a given key in the project
final List<SimpleProperty> properties = SimpleUtil.findProperties(project, key); final List<SimpleProperty> properties = SimpleUtil.findProperties(project, key);
if ( properties.size() == 1 ) { if (properties.size() == 1) {
// Add a folding descriptor for the literal expression at this node. // Add a folding descriptor for the literal expression at this node.
descriptors.add(new FoldingDescriptor(literalExpression.getNode(), descriptors.add(new FoldingDescriptor(literalExpression.getNode(),
new TextRange(literalExpression.getTextRange().getStartOffset() + 1, new TextRange(literalExpression.getTextRange().getStartOffset() + 1,
literalExpression.getTextRange().getEndOffset() - 1), literalExpression.getTextRange().getEndOffset() - 1),
group) ); group));
} }
} }
} }
return descriptors.toArray(new FoldingDescriptor[descriptors.size()]); return descriptors.toArray(new FoldingDescriptor[descriptors.size()]);
} }
/** /**
* Gets the Simple Language 'value' string corresponding to the 'key' * Gets the Simple Language 'value' string corresponding to the 'key'
* @param node Node corresponding to PsiLiteralExpression containing a string in the format *
* SIMPLE_PREFIX_STR + SIMPLE_SEPARATOR_STR + Key, where Key is * @param node Node corresponding to PsiLiteralExpression containing a string in the format
* defined by the Simple language file. * SIMPLE_PREFIX_STR + SIMPLE_SEPARATOR_STR + Key, where Key is
* defined by the Simple language file.
*/ */
@Nullable @Nullable
@Override @Override
public String getPlaceholderText(@NotNull ASTNode node) { public String getPlaceholderText(@NotNull ASTNode node) {
String retTxt = "..."; String retTxt = "...";
if ( node.getPsi() instanceof PsiLiteralExpression ) { if (node.getPsi() instanceof PsiLiteralExpression) {
PsiLiteralExpression nodeElement = (PsiLiteralExpression) node.getPsi(); PsiLiteralExpression nodeElement = (PsiLiteralExpression) node.getPsi();
String key = ((String) nodeElement.getValue()).substring(SimpleAnnotator.SIMPLE_PREFIX_STR.length() + SimpleAnnotator.SIMPLE_SEPARATOR_STR.length()); String key = ((String) nodeElement.getValue()).substring(
final List< SimpleProperty > properties = SimpleUtil.findProperties(nodeElement.getProject(), key); SimpleAnnotator.SIMPLE_PREFIX_STR.length() + SimpleAnnotator.SIMPLE_SEPARATOR_STR.length()
);
final List<SimpleProperty> properties = SimpleUtil.findProperties(nodeElement.getProject(), key);
String place = properties.get(0).getValue(); String place = properties.get(0).getValue();
// IMPORTANT: keys can come with no values, so a test for null is needed // IMPORTANT: keys can come with no values, so a test for null is needed
// IMPORTANT: Convert embedded \n to backslash n, so that the string will look // IMPORTANT: Convert embedded \n to backslash n, so that the string will look
@ -67,9 +80,10 @@ public class SimpleFoldingBuilder extends FoldingBuilderEx implements DumbAware
} }
return retTxt; return retTxt;
} }
@Override @Override
public boolean isCollapsedByDefault(@NotNull ASTNode node) { public boolean isCollapsedByDefault(@NotNull ASTNode node) {
return true; return true;
} }
}
}

View File

@ -5,35 +5,39 @@ package org.intellij.sdk.language;
import com.intellij.formatting.*; import com.intellij.formatting.*;
import com.intellij.lang.ASTNode; import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*; import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.codeStyle.CodeStyleSettings;
import org.intellij.sdk.language.psi.SimpleTypes; import org.intellij.sdk.language.psi.SimpleTypes;
import org.jetbrains.annotations.*; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class SimpleFormattingModelBuilder implements FormattingModelBuilder { public class SimpleFormattingModelBuilder implements FormattingModelBuilder {
private static SpacingBuilder createSpaceBuilder(CodeStyleSettings settings) {
return new SpacingBuilder(settings, SimpleLanguage.INSTANCE)
.around(SimpleTypes.SEPARATOR)
.spaceIf(settings.getCommonSettings(SimpleLanguage.INSTANCE.getID()).SPACE_AROUND_ASSIGNMENT_OPERATORS)
.before(SimpleTypes.PROPERTY)
.none();
}
@NotNull @NotNull
@Override @Override
public FormattingModel createModel(PsiElement element, CodeStyleSettings settings) { public FormattingModel createModel(PsiElement element, CodeStyleSettings settings) {
return FormattingModelProvider return FormattingModelProvider
.createFormattingModelForPsiFile(element.getContainingFile(), .createFormattingModelForPsiFile(element.getContainingFile(),
new SimpleBlock(element.getNode(), new SimpleBlock(element.getNode(),
Wrap.createWrap(WrapType.NONE, false), Wrap.createWrap(WrapType.NONE, false),
Alignment.createAlignment(), Alignment.createAlignment(),
createSpaceBuilder(settings)), createSpaceBuilder(settings)),
settings); settings);
} }
private static SpacingBuilder createSpaceBuilder(CodeStyleSettings settings) {
return new SpacingBuilder(settings, SimpleLanguage.INSTANCE)
.around(SimpleTypes.SEPARATOR)
.spaceIf(settings.getCommonSettings(SimpleLanguage.INSTANCE.getID()).SPACE_AROUND_ASSIGNMENT_OPERATORS)
.before(SimpleTypes.PROPERTY)
.none();
}
@Nullable @Nullable
@Override @Override
public TextRange getRangeAffectingIndent(PsiFile file, int offset, ASTNode elementAtOffset) { public TextRange getRangeAffectingIndent(PsiFile file, int offset, ASTNode elementAtOffset) {
return null; return null;
} }
} }

View File

@ -7,5 +7,7 @@ import com.intellij.openapi.util.IconLoader;
import javax.swing.*; import javax.swing.*;
public class SimpleIcons { public class SimpleIcons {
public static final Icon FILE = IconLoader.getIcon("/icons/jar-gray.png"); public static final Icon FILE = IconLoader.getIcon("/icons/jar-gray.png");
}
}

Some files were not shown because too many files have changed in this diff Show More