4.11 Working with XML-like files (R7)

Skip to end of metadata
Go to start of metadata

ReSharper comes with support for XML and XML-specific notations such as HTML, XAML and Web.config editing. Consequently, you can use the ReSharper API in order to manipulate XML documents.

Web vs XML

The ReSharper code model distinguishes between two types of documents - web documents (i.e., documents concerning HTML) and XML documents. The distinction is important because, while XML documents are typically concerned with XML only, web documents can include many different notations in addition to HTML. Examples of such notations include JavaScript as well as mark-up for the MVC WebForms or Razor view engines.

Let's take a look at the XML format first.

XML

Compared to HTML, XML is a lot easier to work with - it has a predictable structure and fairly limited variability in terms of content. Consequently, the API structures for manipulating XML are very straightforward.

Let's take a look at how one would write a context action to support the conversion of an empty tag such as <test></test> to a self-closing tag <test/>.

The first thing you do is create a class that is marked with the ContextAction attribute and is set to inherit from XmlContextAction.

XML Context Action Body

As you can see, the context action is very similar to the way you would define it for, e.g., the C# language. The only major difference is that instead of supporting a general IContextAction interface, you need to inherit from a special class. (And even that is optional - XmlContextAction is simply an intermediate class that ensures you pick the right data provider.)

Now that you have a context action, it becomes a matter of defining the familiar Text, IsAvailable() and ExecutePsiTransaction() members. The IsAvailable() method is the place where you would check that the tag you're on is empty. For example:

Checking that we're on an empty tag

Manipulation of XML tokens is very important if you want to do fine-tuning of your XML-related actions. To get at the token at the caret location, for example, you would write:

Now, you can, for example, call GetTokenType() to determine the type of the token you're on. Keep in mind that IXmlToken is inherited by many other interfaces such as e.g., IXmlIdentifier. After determining the type of token, you can cast to the right type to get at the token-specific properties and methods.

Here's a brief example: suppose you want to remove all inner nodes of a particular tag. To do this, you would take the tag's header node and then take its next sibling. Then you would also take its last child. Finally, you would use the ModificationUtil class in order to delete all children within that range:

The ModificationUtil class contains several methods for adding and removing elements from the XML token tree.

Example: working with an XML attribute

Here's a simple example: let's suppose that you have a tag, and you want to add an attribute to it - for example, an attribute for an XML namespace. The first thing you do is acquire an XMLElementFactory.

Then, you can synthetically prepare the attribute declaration:

With the declaration, you can use the factory to create a whole XML file. This may sound 'heavyweight', but is in fact a convenient way to get the structures parsed and ready for subsequent insertion.

Finally, you can get at the attribute in the created file and insert it into the tag you're trying to change:

HTML

Work with HTML files is a bit different than XML. The first thing to note is that, unlike with XML, an HTML context action, for instance, would have no special base class of its own. Rather, making an HTML-related context action is simply a matter of implementing the IContextAction interface. The only difference is that this context action would take an IWebContextActionDataProvider<T> as a parameter.

The type parameter T has to be either of type IHtmlFile or one of its inheritors - IAspFile and IRazorFile. Here's an outline of a context action for HTML files:

Now, you'll notice that the code for creating an id-inserting context action is very similar to what we did for XML. The only differences are in the types of tags and factories that we are using:

Now, when it comes to modifying the document tree, with Web files there is one difference: you need to explicitly create the transaction before modifying the HTML file. Here's a piece of code that, just like in the XML example, inserts the id attribute into a tag:

As you can see, the principle for creating the attribute is the similar to what we had before, with the difference that instead of creating a whole file, we can simply create a tag. From then on, getting the first attribute and inserting it into our element is easy.