addddd

Virtual Files

A virtual file com.intellij.openapi.vfs.VirtualFile is the IntelliJ Platform’s representation of a file in a file system (VFS). Most commonly, a virtual file is a file in your local file system. However, the IntelliJ Platform supports multiple pluggable file system implementations, so virtual files can also represent classes in a JAR file, old revisions of files loaded from a version control repository, and so on.

The VFS level deals only with binary content. You can get or set the contents of a VirtualFile as a stream of bytes, but concepts like encodings and line separators are handled on higher system levels.

How do I get a virtual file?

  • From an action: e.getData(PlatformDataKeys.VIRTUAL_FILE). If you are interested in multiple selection, you can also use e.getData(PlatformDataKeys.VIRTUAL_FILE_ARRAY).
  • From a path in the local file system: LocalFileSystem.getInstance().findFileByIoFile()
  • From a PSI file: psiFile.getVirtualFile() (may return null if the PSI file exists only in memory)
  • From a document: FileDocumentManager.getInstance().getFile()

What can I do with it?

Typical file operations are available, such as traverse the file system, get file contents, rename, move, or delete. Recursive iteration should be performed using VfsUtilCore.iterateChildrenRecursively to prevent endless loops caused by recursive symlinks.

Where does it come from?

The VFS is built incrementally, by scanning the file system up and down starting from the project root. New files appearing in the file system are detected by VFS refreshes. A refresh operation can be initiated programmatically using (VirtualFileManager.getInstance().refresh() or VirtualFile.refresh()). VFS refreshes are also triggered whenever file system watchers receive file system change notifications (available on the Windows and Mac operating systems).

As a plugin developer, you may want to invoke a VFS refresh if you need to access a file that has just been created by an external tool through the IntelliJ Platform APIs.

How long does a virtual file persist?

A particular file on disk is represented by equal VirtualFile instances for the entire lifetime of the IDEA process. There may be several instances corresponding to the same file, and they can be garbage-collected. The file is aUserDataHolder, and the user data is shared between those equal instances. If a file is deleted, its corresponding VirtualFile instance becomes invalid (the isValid() method returns false and operations cause exceptions).

How do I create a virtual file?

Usually you don’t. As a rule, files are created either through the PSI API or through the regular java.io.File API.

If you do need to create a file through VFS, you can use the VirtualFile.createChildData() method to create aVirtualFile instance and the VirtualFile.setBinaryContent() method to write some data to the file.

How do I get notified when VFS changes?

The VirtualFileManager.addVirtualFileListener() method allows you to receive notifications about all changes in the VFS.

How do I extend VFS?

To provide an alternative file system implementation (for example, an FTP file system), implement the VirtualFileSystemclass (most likely you’ll also need to implement VirtualFile), and register your implementation as an application component.

To hook into operations performed in the local file system (for example, if you are developing a version control system integration that needs custom rename/move handling), implement the LocalFileOperationsHandler interface and register it through theLocalFileSystem.registerAuxiliaryFileOperationsHandler method.

What are the rules for working with VFS?

See IntelliJ Platform Virtual File System for a detailed description of the VFS architecture and usage guidelines.

Documents

A document is an editable sequence of Unicode characters, which typically corresponds to the text contents of a virtual file. Line breaks in a document are always normalized to  . The IntelliJ Platform handles encoding and line break conversions when loading and saving documents transparently.

How do I get a document?

  • From an action: e.getData(PlatformDataKeys.EDITOR).getDocument()
  • From a virtual file: FileDocumentManager.getDocument(). This call forces the document content to be loaded from disk if it wasn’t loaded previously; if you’re only interested in open documents or documents which may have been modified, use FileDocumentManager.getCachedDocument() instead.
  • From a PSI file: PsiDocumentManager.getInstance().getDocument() orPsiDocumentManager.getInstance().getCachedDocument()

What can I do with a Document?

You may perform any operations that access or modify the file contents on “plain text” level (as a sequence of characters, not as a tree of Java elements).

Where does a Document come from?

Document instances are created when some operation needs to access the text contents of a file (in particular, this is needed to build the PSI for a file). Also, document instances not linked to any virtual files can be created temporarily, for example, to represent the contents of a text editor field in a dialog.

How long does a Document persist?

Document instances are weakly referenced from VirtualFile instances. Thus, an unmodified Document instance can be garbage-collected if it isn’t referenced by anyone, and a new instance will be created if the document contents is accessed again later. Storing Document references in long-term data structures of your plugin will cause memory leaks.

How do I create a Document?

If you need to create a new file on disk, you don’t create a Document: you create a PSI file and then get its Document. If you need to create a Document instance which isn’t bound to anything, you can use EditorFactory.createDocument.

How do I get notified when Documents change?

  • Document.addDocumentListener allows you to receive notifications about changes in a particular Document instance.
  • EditorFactory.getEventMulticaster().addDocumentListener allows you to receive notifications about changes in all open documents.
  • FileDocumentManager.addFileDocumentManagerListener allows you to receive notifications when any Document is saved or reloaded from disk.

What are the rules of working with Documents?

The general read/write action rules are in effect. In addition to that, any operations which modify the contents of the document must be wrapped in a command (CommandProcessor.getInstance().executeCommand()). executeCommand()calls can be nested, and the outermost executeCommand call is added to the undo stack. If multiple documents are modified within a command, undoing this command will by default show a confirmation dialog to the user.

If the file corresponding to a Document is read-only (for example, not checked out from the version control system), document modifications will fail. Thus, before modifying the Document, it is necessary to callReadonlyStatusHandler.getInstance(project).ensureFilesWritable() to check out the file if necessary.

All text strings passed to Document modification methods (setTextinsertStringreplaceString) must use only as line separators.

PSI Files

A PSI (Program Structure Interface) file is the root of a structure representing the contents of a file as a hierarchy of elements in a particular programming language.

The PsiFile class is the common base class for all PSI files, while files in a specific language are usually represented by its subclasses. For example, the PsiJavaFile class represents a Java file, and the XmlFile class represents an XML file.

Unlike VirtualFile and Document, which have application scope (even if multiple projects are open, each file is represented by the same VirtualFile instance), PSI has project scope (the same file is represented by multiplePsiFile instances if the file belongs to multiple projects open at the same time).

How do I get a PSI file?

  • From an action: e.getData(LangDataKeys.PSI_FILE).
  • From a VirtualFile: PsiManager.getInstance(project).findFile()
  • From a Document: PsiDocumentManager.getInstance(project).getPsiFile()
  • From an element inside the file: psiElement.getContainingFile()
  • To find files with a specific name anywhere in the project, use FilenameIndex.getFilesByName(project, name, scope)

What can I do with a PSI file?

Most interesting modification operations are performed on the level of individual PSI elements, not files as a whole.

To iterate over the elements in a file, use psiFile.accept(new PsiRecursiveElementWalkingVisitor()...);

Where does a PSI file come from?

As PSI is language-dependent, PSI files are created through the Language object, by using theLanguageParserDefinitions.INSTANCE.forLanguage(language).createFile(fileViewProvider) method.

Like documents, PSI files are created on demand when the PSI is accessed for a particular file.

How long do PSI files persist?

Like documents, PSI files are weakly referenced from the corresponding VirtualFile instances and can be garbage-collected if not referenced by anyone.

How do I create a PSI file?

The PsiFileFactory.getInstance(project).createFileFromText() method creates an in-memory PSI file with the specified contents.

To save the PSI file to disk, use the PsiDirectory.add() method.

How do I get notified when PSI files change?

PsiManager.getInstance(project).addPsiTreeChangeListener() allows you to receive notifications about all changes to the PSI tree of a project.

How do I extend PSI?

PSI can be extended to support additional languages through custom language plugins. For more details on developing custom language plugins, see the Custom Language Support reference guide.

What are the rules for working with PSI?

Any changes done to the content of PSI files are reflected in documents, so all rules for working with documents (read/write actions, commands, read-only status handling) are in effect.

File View Providers

A file view provider (see the FileViewProvider class) was introduced in IntelliJ IDEA 6.0. Its main purpose is to manage access to multiple PSI trees within a single file.

For example, a JSPX page has a separate PSI tree for the Java code in it (PsiJavaFile), a separate tree for the XML code (XmlFile), and a separate tree for JSP as a whole JspFile).

Each of the PSI trees covers the entire contents of the file, and contains special “outer language elements” in the places where contents in a different language can be found.

FileViewProvider instance corresponds to a single VirtualFile, a single Document, and can be used to retrieve multiple PsiFile instances.

How do I get an FVP?

  • From a VirtualFile: PsiManager.getInstance(project).findViewProvider()
  • From a PSI file: psiFile.getViewProvider()

What can I do with an FVP?

  • To get the list of all languages for which PSI trees exist in a file: fileViewProvider.getLanguages()
  • To get the PSI tree for a particular language: fileViewProvider.getPsi(language), where the language parameter can take values of the Language type defined in StdLanguages class. For example, to get the PSI tree for XML, usefileViewProvider.getPsi(StdLanguages.XML).
  • To find an element of a particular language at the specified offset in the file:fileViewProvider.findElementAt(offset,language)

How do I extend the FileViewProvider?

To create a file type that has multiple interspersing trees for different languages, your plugin must contain an extension to the fileType.fileViewProviderFactory extension point available in the IntelliJ Platform core.

This extension point is declared using the FileTypeExtensionPoint bean class.

To access this extension point, create a Java class that implements the FileViewProviderFactory interface, and in this class, override the createFileViewProvider method.

To declare the extension to the fileType.fileViewProviderFactory extension point, to the <extensions> section of the plugin.xml file, add the following syntax:

<extensions>
  <fileType.fileViewProviderFactory filetype="%file_type%" implementationClass="%class_name%" />
</extensions>

Where %file_type% refers to the type of the file being created (for example, “JFS”), and the %class_name% refers to the name of your Java class that implements the FileViewProviderFactory interface.

PSI Elements

A PSI (Program Structure Interface) file represents a hierarchy of PSI elements (so-called PSI trees). A single PSI file may include several PSI trees in a particular programming language. A PSI element, in its turn, can have child PSI elements.

PSI elements and operations on the level of individual PSI elements are used to explore the internal structure of source code as it is interpreted by the IntelliJ Platform. For example, you can use PSI elements to perform code analysis, such as code inspections or intention actions.

The PsiElement class is the common base class for PSI elements.

How do I get a PSI element?

  • From an action: e.getData(LangDataKeys.PSI_ELEMENT). Note: if an editor is currently open and the element under caret is a reference, this will return the result of resolving the reference. This may or may not be what you need.
  • From a file by offset: PsiFile.findElementAt(). Note: this returns the lowest level element at the specified offset, which is normally a lexer token. Most likely you should use PsiTreeUtil.getParentOfType() to find the element you really need.
  • By iterating through a PSI file: using a PsiRecursiveElementWalkingVisitor.
  • By resolving a reference: PsiReference.resolve()

What can I do with PSI elements?

See PSI Cook Book

Project Structure

This topic considers the concept of projects based on IntelliJ Platform and related subjects, such as modulesfacets,librariesSDK. The project structure and Java classes you can use to manage projects and modules have been considered.

Project and its components

This section briefly discusses the IDEA project structure, project components and related terms. For more information about projects and their components, refer to ProjectModuleLibraryFacet in the IntelliJ IDEA Web Help.

Project

In the IntelliJ Platform, a project encapsulates all your source code, libraries, and build instructions into a single organizational unit. Everything you do using the IntelliJ Platform SDK is done within the context of a project. A project defines collections referred to as modules and libraries. Depending on the logical and functional requirements to the project, you can create a single-module or a multi-module project.

Module

module is a discrete unit of functionality that can be run, tested, and debugged independently. Modules includes such things as source code, build scripts, unit tests, deployment descriptors, etc. In the project, each module can use a specific SDK or inherit SDK defined on the project level (see the SDK section later in this document). A module can depend on other modules of the project.

Library

library is an archive of compiled code (such as JAR files) that your modules depend on. The IntelliJ Platform supports three types of libraries:

  • Module Library: the library classes are visible only in this module and the library information is recorded in the module .iml file.
  • Project Library: the library classes are visible within the project and the library information is recorded in the project.ipr file or in .idea/libraries.
  • Global Library: the library information is recorded in the applicationLibraries.xml file into the~/.IntelliJIdea/config/options directory. Global libraries are similar to project libraries, but are visible for the different projects.

For more information about libraries, refer to Library.

SDK

Every project uses a Software Development Kit (SDK). For Java projects, SDK is referred to as JDK (Java Development Kit).

The SDK determines which API library is used to build the project. If your project is multi-module, the project SDK by default is common for all modules within the project.

Optionally, you can configure individual SDK for each module.

For more information about SDKs, see Configuring Global, Project and Module SDKs in the IntelliJ IDEA Web Help.

Facet

facet represents certain configuration, specific for a particular framework/technology, associated with a module. A module can have multiple facets. E.g. Spring specific configuration is stored in a Spring facet.

For more information about facets see Facet and Facet Dependencies in the IntelliJ IDEA Web Help.

Project Structure

From the plugin developer’s point of view, a project can be thought of as follows:

Project Structure

A project consists of one or several modules. Each module includes the plugin source code and so called order entries that refer to SDK and libraries the module uses. By default, all modules uses the project SDK. In addition, amodule can optionally have a set of facets.

This document explains how you can explore and change the structure of projects using API.

Working with Projects

This section explains how to complete some common tasks related to management of projects. The Java classes and interfaces that you can use to explore and change the project contents are discussed.

How to Work with Project Files?

The IntelliJ Platform stores the project configuration data in XML files. The list of those files depends on the pluginproject format.

For file-based format projects, the information core to the project itself (e.g. location of the component modules, compiler settings, etc.) is stored in the %project_name%.ipr file.

The information about modules the project includes is stored in %module_name%.iml files. Module files are created for each module.

For directory-based format projects, the project and workspace settings are stored in a number of XML files under the%project_home_directory%/.idea directory. Each XML file is responsible for its own set of settings and can be recognized by its name: projectCodeStyle.xmlencodings.xmlvcs.xml etc.

As for the file-based format projects, .iml files describe modules.

To work with projects and project files, you can use the following classes and interfaces:

Note that you don’t need to access project files directly to load or save settings. See Persisting State of Components for more information.

Note that hereafter, the project variable is of the Project type. For example, for the opened project, you can get it from an action: Project project = e.getProject();

How do I get a list of source roots for all modules in my project?

Use the ProjectRootManager.getContentSourceRoots() method. To clarify this, consider the following code snippet:

String projectName = project.getName();
StringBuilder sourceRootsList = new StringBuilder();
VirtualFile[] vFiles = ProjectRootManager.getInstance(project).getContentSourceRoots();
for (VirtualFile file : vFiles) {
  sourceRootsList.append(file.getUrl()).append("
");
}

Messages.showInfoMessage("Source roots for the " + projectName + " plugin:
" + sourceRootsList, "Project Properties");

How do I check whether a file is related to a project?

The IntelliJ Platform provides the ProjectFileIndex interface you can use to verify whether a file or directory is related to the specified IDEA project. This section explains how you can use this interface.

How do I get an instance of the ProjectFileIndex interface?

Use the ProjectRootManager.getFileIndex() method. For example: ProjectFileIndex projectFileIndex = ProjectRootManager.getInstance(project).getFileIndex();

How do I get a module to which a file belongs?

To determine a module in the project in question to which the specified virtual file belongs, use theProjectFileIndex.getModuleForFile(virtualFile) method:

Module module = ProjectRootManager.getInstance(project).getFileIndex().getModuleForFile(virtualFile);

Note that this method returns null if the file does not belong to any module.

You can also use the ProjectFileIndex.getContentRootForFile method to get the module content root to which the specified file or directory belongs:

VirtualFile moduleContentRoot = ProjectRootManager.getInstance(project).getFileIndex().getContentRootForFile(virtualFileorDirectory);

How do I get the module source root or library source root to which the specified file or directory belongs?

Use the ProjectFileIndex.getSourceRootForFile method. For example:

VirtualFile moduleSourceRoot = ProjectRootManager.getInstance(project).getFileIndex().getSourceRootForFile(virtualFileorDirectory);

Note that this method returns null if the file or directory does not belong to any source root of modules in the project.

How do I check whether a file or directory is related to the project libraries?

The ProjectFileIndex interface implements a number of methods you can use to check whether the specified file belongs to the project library classes or library sources.

You can use the following methods:

  • ProjectFileIndex.isLibraryClassFile(virtualFile): Returns true if the specified virtualFile is a compiled class file.
  • ProjectFileIndex.isInLibraryClasses(virtualFileorDirectory): Returns true if the specifiedvirtualFileorDirectory belongs to library classes.
  • ProjectFileIndex.isInLibrarySource(virtualFileorDirectory): Returns true if the specifiedvirtualFileorDirectory belongs to library sources.

How do I get the project SDK?

  • To get the project-level SDK: Sdk projectSDK = ProjectRootManager.getInstance(project).getProjectSdk();
  • To get the project-level SDK name: String projectSDKName = ProjectRootManager.getInstance(project).getProjectSdkName();

How do I set the project SDK?

  • To set the project-level SDK: ProjectRootManager.getInstance(project).setProjectSdk(Sdk jdk);
  • To set the project-level SDK name: ProjectRootManager.getInstance(project).setProjectSdkName(String name);

Note that by default, the project modules use the project SDK. Optionally, you can configure individual SDK for each module.

Working with Modules

The IntelliJ Platform provides a number of Java classes and interfaces you can use to work with modules:

This section discusses how to complete some common tasks related to management of modules.

How do I get a list of modules the project includes?

Use the ModuleManager.getModules() method.

How do I get dependencies and classpath of a module?

Order entries include SDK, libraries and other modules the module uses. With the IntelliJ IDEA UI, you can view order entries for a module on the Dependencies tab of the Project Structure dialog box.

To explore the module dependencies, use the OrderEnumerator class.

The following code snippet illustrates how you can get classpath (classes root of all dependencies) for a module:

VirtualFile[] roots = ModuleRootManager.getInstance(module).orderEntries().classes().getRoots();

How do I get the SDK the module uses?

Use the ModuleRootManager.getSdk() method. This method returns a value of the Sdk type.

The following code snippet illustrates how you can get detailed information on SDK the specified module uses:

ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);
Sdk SDK = moduleRootManager.getSdk();
String jdkInfo = "Module: " + module.getName() + " SDK: " + SDK.getName() + " SDK version: "
                 + SDK.getVersionString() + " SDK home directory: " + SDK.getHomePath();

How do I get a list of modules on which this module directly depends?

Use the ModuleRootManager.getDependencies() method to get an array of the Module type values or theModuleRootManager.getDependencyModuleNames() to get an array of module names. To clarify, consider the following code snippet:

ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);
Module[] dependentModules = moduleRootManager.getDependencies();
String[] dependentModulesNames = moduleRootManager.getDependencyModuleNames();

How do I get a list of modules that depend on this module?

Use the ModuleManager.getModuleDependentModules(module) method.

Note that you can also check whether a module (module1) depends on another specified module (module2) using theModuleManager.isModuleDependent method in the following way:

boolean isDependent = ModuleManager.getInstance(project).isModuleDependent(module1,module2);

How do I get a module to which the specified file or PSI element belongs?

  • To get the project module to which the specified file belongs, use the ModuleUtil.findModuleForFile() static method.

    To clarify, consider the following code snippet:

    java String pathToFile = "C:\users\firstName.LastName\plugins\myPluginsrcMyAction.java"; VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByPath(pathToFile); Module module = ModuleUtil.findModuleForFile(virtualFile,myProject); String moduleName = module == null ? "Module not found" : module.getName();

  • To get the project module to which the specified PSI element belongs, use theModuleUtil.findModuleForPsiElement(psiElement) method.

How do I work with libraries available within a module?

How do I get a list of libraries available within a module?

To get the list of libraries, use OrderEnumerator.forEachLibrary method. To clarify this, consider the following code snippet that illustrates how to output the list of libraries for the specified module:

final List<String> libraryNames = new ArrayList<String>();
ModuleRootManager.getInstance(module).orderEntries().forEachLibrary(new Processor<Library>() {
  @Override
  public boolean process(Library library) {
    libraryNames.add(library.getName());
    return true;
  }
});
Messages.showInfoMessage(StringUtil.join(libraryNames, "
"), "Libraries in Module");

This sample code outputs a list of libraries for the module module.

How do I get the library content?

The Library class provides the getUrls method you can use to get a list of source roots and classes the library includes. To clarify, consider the following code snippet:

StringBuilder roots = new StringBuilder("The " + lib.getName() + " library includes:
");
roots.append("Sources:
");
for (String each : lib.getUrls(OrderRootType.SOURCES)) {
  roots.append(each).append("
");
}
roots.append"Classes:
");
for (String each : lib.getUrls(OrderRootType.CLASSES)) {
  strRoots.append(each).append("
");
}
Messages.showInfoMessage(roots.toString(), "Library Info");

In this sample code, lib is of the Library type.

How do I get a set of facets the module includes?

Use the FacetManager and Facet classes.

Run Configuration Management

This document describes main classes to work with run configurations and common use case.

Configuration type

The starting point for implementing any run configuration type is the ConfigurationType interface. The list of available configuration types is shown when a user opens the ‘Edit run configurations’ dialog and executes ‘Add’ action:

Create

Every type there is represented as an instance of ConfigurationType and registered like below:

<configurationType implementation="org.jetbrains.plugins.gradle.service.execution.GradleExternalTaskConfigurationType" />

The easiest way to implement this interface is to use the ConfigurationTypeBase base class. In order to use it, you need to inherit from it and to provide the configuration type parameters (ID, name, description and icon) as constructor parameters. In addition to that, you need to call the addFactory() method to add a configuration factory.

Configuration factory

All run configurations are created by the ConfigurationFactory registered for a particular ConfigurationType. It’s possible that one ConfigurationType has more than one ConfigurationFactory:

Configuration Factory

The key API of ConfigurationFactory, and the only method that you’re required to implement, is thecreateTemplateConfiguration method. This method is called once per project to create the template run configuration.

All real run configurations (loaded from the workspace or created by the user) are called by cloning the template through the createConfiguration method.

You can customize additional aspects of your configuration factory by overriding the getIcongetAddIcongetNameand the default settings methods. These additional overrides are optional.

Run configuration

The run configuration itself is represented by the RunConfiguration interface. A ‘run configuration’ here is some named profile which can be executed, e.g. application started via main() class, test, remote debug to particular machine/port etc.

Here is an example of a Java run configurations defined for a particular project:

Run Configuration

When implementing a run configuration, you may want to use one of the common base classes:

  • RunConfigurationBase is a general-purpose superclass that contains the most basic implementation of a run configuration.
  • LocatableConfigurationBase is a common base class that should be used for configurations that can be created from context by a RunConfigurationProducer. It supports automatically generating a name for a configuration from its settings and keeping track of whether the name was changed by the user.
  • ModuleBasedConfiguration is a base class for a configuration that is associated with a specific module (for example, Java run configurations use the selected module to determine the run classpath).

Settings editor

That common run configuration settings might be modified via:

RunConfiguration-specific UI. That is handled by SettingsEditor:

  • getComponent() method is called by the IDE and shows run configuration specific UI.
  • resetFrom() is called to discard all non-confirmed user changes made via that UI.
  • applyTo() is called to confirm the changes, i.e. copy current UI state into the target settings object.

Persistence

That run configuration settings are persistent, i.e. they are stored at file system and loaded back on the IDE startup. That is performed via writeExternal() and readExternal() methods of RunConfiguration class correspondingly.

The actual configurations stored by the IntelliJ Platform are represented by instances of theRunnerAndConfigurationSettings class, which combines a run configuration with runner-specific settings, as well as keeping track of certain run configuration flags such as “temporary” or “singleton”.

Dealing with instances of this class becomes necessary when you need to create run configurations from code. This is accomplished with the following two steps:

  • RunManager.createConfiguration() creates an instance of RunnerAndConfigurationSettings.
  • RunManager.addConfiguration() makes it persistent by adding it to either the list of shared configurations stored in a project, or to the list of local configurations stored in the workspace file.

Refactoring support

Most run configurations contain references to classes, files or directories in their settings, and these settings usually need to be updated when the corresponding element is renamed or moved.

In order to support that, your run configuration needs to implement the RefactoringListenerProvider interface.

In your implementation of getRefactoringElementListener(), you need to check whether the element being refactored is the one that your run configuration refers to, and if it is, you return a RefactoringElementListener that updates your configuration according to the new name and location of the element.

Creating configurations from context

Many plugins support automatic creation of run configurations from context, so that the user can click, for example, on an application or test class and automatically run it using the correct run configuration type. In order to support that, you need to provide an implementation of the RunConfigurationProducer interface and to register it as<runConfigurationProducer> in your plugin.xml. (Note that this API has been redesigned in IntelliJ IDEA 13; the oldRuntimeConfigurationProducer is a much more confusing version of the same API).

The two main methods that you need to implement are:

  • setupConfigurationFromContext receives a blank configuration of your type and a ConfigurationContext containing information about a source code location (accessible by calling getLocation() or getPsiLocation()). Your implementation needs to check whether the location is applicable for your configuration type (for example, if it’s in a file of the language you’re supporting). If not, you need to return false, and if it is, you need to put the correct context-specific settings into the run configuration and return true.
  • isConfigurationFromContext checks if the specified configuration of your type was created from the specified context. Implementing this method allows you to reuse an existing run configuration which is applicable to the current context instead of creating a new one and possibly ignoring the customisations the user has performed in the existing one.

Note that, in order to support automatic naming of configurations created from context, your configuration should useLocatableConfigurationBase as the base class.

Execution

The standard execution of a run action goes through the following steps:

  • The user selects a run configuration (for example, by choosing one from the run configurations combobox) and anexecutor (for example, by pressing a toolbar button created by the executor).
  • The program runner that will actually execute the process is selected, by polling all registered program runners and asking whether they can run the specified run profile with the specified executor ID.
  • The ExecutionEnvironment object is created. This object aggregates all the settings required to execute the process, as well as the selected ProgramRunner.
  • ProgramRunner.execute() is called, receiving the executor and the execution environment.

Implementations of ProgramRunner.execute() go through the following steps to execute the process:

  • RunProfile.getState() method is called to create a RunProfileState object, describing a process about to be started. At this stage, the command line parameters, environment variables and other information required to start the process is initialized.
  • RunProfileState.execute() is called. It starts the process, attaches a ProcessHandler to its input and output streams, creates a console to display the process output, and returns an ExecutionResult object aggregating the console and the process handler.
  • The RunContentBuilder object is created and invoked to display the execution console in a tab of the Run or Debug toolwindow.

Executor

The Executor interface describes a specific way of executing any possible run configuration.

The three default executors provided by the IntelliJ Platform by default are RunDebug and Run with Coverage. Each executor gets its own toolbar button, which starts the selected run configuration using this executor, and its own context menu item for starting a configuration using this executor.

As a plugin developer, you normally don’t need to implement the Executor interface. However, it can be useful, for example, if you’re implementing a profiler integration and want to provide the possibility to execute any configuration with profiling.

Running a process

The RunProfileState interface comes up in every run configuration implementation as the return valueRunProfile.getState(). It describes a process which is ready to be started and holds the information like the command line, current working directory, and environment variables for the process to be started. (The existence ofRunProfileState as a separate step in the execution flow allows run configuration extensions and other components to patch the configuration and to modify the parameters before it gets executed.)

The standard base class used as implementation of RunProfileState is CommandLineState. It contains the logic for putting together a running process and a console into an ExecutionResult, but doesn’t know anything how the process is actually started. For starting the process, it’s best to use the GeneralCommandLine class, which takes care of setting up the command line parameters and executing the process.

Alternatively, if the process you need to run is a JVM-based one, you can use the JavaCommandLineState base class. It knows about the command line parameters of the JVM and can take care of details like calculating the classpath for the JVM.

To monitor the execution of a process and capture its output, the OSProcessHandler class is normally used. Once you’ve created an instance of OSProcessHandler from either a command line or a Process object, you need to call thestartNotify() method to start capturing its output. You may also want to attach a ProcessTerminatedListener to theOSProcessHandler, so that the exit status of the process will be displayed in the console.

Displaying the process output

If you’re using CommandLineState, a console view will be automatically created and attached to the output of the process. Alternatively, you can arrange this yourself:

  • TextConsoleBuilderFactory.createBuilder(project).getConsole() creates a ConsoleView instance
  • ConsoleView.attachToProcess() attaches it to the output of a process.

If the process you’re running uses ANSI escape codes to color its output, the ColoredProcessHandler class will parse it and display the colors in the IntelliJ console.

Console filters allow you to convert certain strings found in the process output to clickable hyperlinks. To attach a filter to the console, use CommandLineState.addConsoleFilters() or, if you’re creating a console manually,TextConsoleBuilder.addFilter().

Two common filter implementations you may want to reuse are RegexpFilter and UrlFilter.

Starting a run configuration from code

If you have an existing run configuration that you need to execute, the easiest way to do so is to useProgramRunnerUtil.executeConfiguration(). The method takes a Project, a RunnerAndConfigurationSettings, as well as an Executor. To get the RunnerAndConfigurationSettings for an existing configuration, you can use, for example,RunManager.getConfigurationSettings(ConfigurationType). As the last parameter, you normally pass eitherDefaultRunExecutor.getRunExecutorInstance() or DefaultDebugExecutor.getDebugExecutorInstance().

原文地址:https://www.cnblogs.com/liqiking/p/6810444.html