Persisting State of Components
The IntelliJ Platform provides an API that allows components or services to persist their state between restarts of the IDE. The API allows for persisting simple key-value entries and complex state classes.
PersistentStateComponent interface allows for persisting state classes and gives the most flexibility for defining the values to be persisted, their format, and storage location.
To use it:
mark a service as implementing the
define the state class
specify the storage location using
Note that instances of extensions cannot persist their state by implementing
PersistentStateComponent. If an extension needs to have a persistent state, define a separate service responsible for managing that state.
Implementing the PersistentStateComponent Interface
The easiest way to implement a persistent state component in Kotlin is extending
SimplePersistentStateComponent, which implements
SimplePersistentStateComponent is parameterized by a subclass of
BaseState provides a set of handy property delegates, which make it easy to create properties with default values. In addition, delegates track property modifications internally, which helps decrease calling
PersistentStateComponent.getState() by the platform.
It is recommended to create separate classes for a component and its state:
The implementation of
PersistentStateComponent must be parameterized with the type of state class. The state class can either be a separate class, or the class implementing
Persistent Component with Separate State Class
In this case, the state class instance is typically stored as a field in the
PersistentStateComponent class. When the state is loaded from the storage, it is assigned to the state field (see
Using a separate state class is the recommended approach.
Persistent Component Being a State Class
In this case,
getState() returns the component itself, and
loadState() copies properties of the state loaded from storage to the component instance:
Implementing the State Class
The implementation of
PersistentStateComponent works by serializing public fields, annotated private fields (see also Customizing the XML format of persisted values), and bean properties into an XML format.
To exclude a public field or bean property from serialization, annotate the field or getter with
Note that the state class must have a default constructor. It should return the component's default state: the one used if there is nothing persisted in the XML files yet.
State class should have an
equals() method, but state objects are compared by fields if it is not implemented.
The following types of values can be persisted:
numbers (both primitive types, such as
int, and boxed types, such as
For other types, extend
Converter. See the example below.
Defining the Storage Location
To specify where precisely the persisted values are stored, add
@State annotation to the
It has the following fields:
name(required) — specifies the name of the state (name of the root tag in XML).
storages— one or more of
@Storageannotations to specify the storage locations. Optional for project-level values — standard project file is used in this case.
reloadable(optional) — if set to false, a full project (or application) reload is required when the XML file is changed externally, and the state has changed.
The simplest ways of specifying the
@Storage annotation are as follows:
@Storage("yourName.xml")If a component is project-level — for .ipr based projects standard project file is used automatically - no need to specify anything.
@Storage(StoragePathMacros.WORKSPACE_FILE)for values stored in the workspace file.
The state is persisted in a separate file by specifying a different setting for the
value parameter, which was the
file parameter before 2016.x.
StoragePathMacros for commonly used values.
roamingType parameter of the
@Storage annotation specifies the roaming type when the settings are shared:
RoamingType.DEFAULT- settings are shared
RoamingType.PER_OS- settings are shared per operating system
RoamingType.DISABLED- settings sharing is disabled
Sharing Settings Between IDE Installations
It is possible to share the persistent state of components between different IDE installations. This allows users to have the same settings on every development machine or to share their settings within a team.
Settings can be shared via the following functionalities:
Settings Sync plugin that allows synchronizing settings on JetBrains servers. Users can choose the category of settings that are synchronized.
Settings Repository plugin that allows synchronizing settings in a Git repository created and configured by a user.
Export Settings feature that allows for the manual import and export of settings.
The decision about making a specific component's state shareable should be made carefully. Only the settings that are not specific to a given machine should be shared, e.g., paths to user-specific directories shouldn't be shared. If a component contains both shareable and non-shareable data, it should be split into two separate components.
Settings Sync Plugin
To include a plugin's component state in the Settings Sync plugin synchronization, the following requirements must be met:
RoamingTypeis defined via the
roamingTypeattribute of the
@Storageannotation and is not equal to
SettingsCategoryis defined via the
categoryattribute of the
@Stateannotation and is not equal to
There is no other
PersistentStateComponent, which is stored to the same XML file and has a different
If the component state is OS-dependent, the
roamingType of the
@Storage annotation must be set to
Settings Repository Plugin and Export Settings Feature
Persistent components can be shared via the Settings Repository plugin and Export Settings feature, depending on the
roamingType of the
@Storage annotation. See the Defining the Storage Location for more details.
Customizing the XML Format of Persisted Values
If you want to use the default bean serialization but need to customize the storage format in XML (for example, for compatibility with previous versions of a plugin or externally defined XML formats), use the
If the state to serialize doesn't map cleanly to a JavaBean, then
org.jdom.Element can be used as the state class. In that case, use the
getState() method to build an XML element with an arbitrary structure, which then is saved directly in the state XML file. In the
loadState() method, deserialize the JDOM element tree using any custom logic. This is not recommended and should be avoided whenever possible.
Migrating Persisted Values
If the underlying persistence model or storage format has changed, a
ConverterProvider can provide
getAdditionalAffectedFiles() method returns affected files to migrate and performs programmatic migration of stored values.
Persistent Component Lifecycle
PersistentStateComponent.loadState() method is called after the component has been created (only if there is some non-default state persisted for the component), and after the XML file with the persisted state is changed externally (for example, if the project file was updated from the version control system). In the latter case, the component is responsible for updating the UI and other related components according to the changed state.
PersistentStateComponent.getState() method is called every time the settings are saved (for example, on frame deactivation or when closing the IDE). If the state returned from
getState() is equal to the default state (obtained by creating the state class with a default constructor), nothing is persisted in the XML. Otherwise, the returned state is serialized in XML and stored.
Using PropertiesComponent for Simple Non-Roamable Persistence
If the plugin needs to persist just a few simple values, the easiest way to do so is to use the
PropertiesComponent service. It can save both application-level values and project-level values in the workspace file. Roaming is disabled for
PropertiesComponent, so use it only for temporary, non-roamable properties.
PropertiesComponent.getInstance() method for storing application-level values, and the
PropertiesComponent.getInstance(Project) method for storing project-level values.
Since all plugins share the same namespace, it is highly recommended prefixing key names (e.g., using plugin ID
Legacy API (JDOMExternalizable)
Older components use the
JDOMExternalizable interface for persisting state. It uses the
readExternal() method for reading the state from a JDOM element, and
writeExternal() to write the state.
Implementations can manually store the state in attributes and sub-elements or use the
DefaultJDOMExternalizer class to store the values of all public fields automatically.
Components save their state in the following files:
Project-level: project (.ipr) file. However, if the workspace option in the plugin.xml file is set to
true, then the workspace (.iws) file is used instead.
Module-level: module (.iml) file.