VisualEditor/Gadgets/Creating a custom command

This pages shows you by a commented example how to adapt a gadget that currently works with the old wikitext editor to use it with VisualEditor, especially with the 2017 wikitext editor. For comparison, this page also shows you how to solve the same task in visual mode, but note that while the wikitext part is generic and can be used with any existing code, the visual part is specific to the action the code actually should do. But it is possible to write a source code only tool, so you don't have to rewrite your code for visual mode if you don't want to.

Example code
To test the following code, you can execute it in your browser's console before VE is loaded and then start editing in VE, i.e., click "Edit". It will show up in the "Page options" menu and unlink all years (i.e. remove all links to pages with a numeric title, keeping the label), either in the selected part of the page, or (if nothing is selected) in the whole page.

Function to modify wikitext
First of all, we define our function to modify wikitext. This is not specific to VisualEditor, if you're reading this because you want to adapt your scripts to VisualEditor, you should already have this function.

Just as a minor note: In the early days of Wikipedia linking every year in a text was very popular. After some time editors realized that these links didn't really help anyone in most cases, so they tried to eliminate all those links. During that time scripts to do so automatically were very popular. Nowadays the remaining links to years are sensible in most cases, so scripts to remove them should no longer be needed, but it's still a nice example.

Create and register command
Next, we create a command to unlink all years. Most commands work by invoking a method from an  (documentation, see this example about how to create a command this way), but here it's easier to provide our own execution method instead.

So we inherit from  (documentation), and override the   method.

Get fragment to work on
First, we get a  (documentation) from the current selection to work on. If it is collapsed (i.e. nothing is selected) we select the whole document.

Visual mode
When we are in visual mode, we use the following way to remove links to years: At the end we  to indicate we executed the command.
 * Links are s (documentation), so we use   to get them all.
 * From this  (documentation) we filter the links the are interested in: Internal links have the type , the linked page is in the   attribute.
 * We use  to remove the links.

Source mode
In source mode we first get the wikitext from the fragment. The result from the  method is almost what we want, but we have to fix the newline characters: In source mode, every line is wrapped in   tags, and the method returns   for both the opening and closing tags. So we have to strip the first one and replace two consecutive linebreaks with just one.

Now we can apply our function to change the wikitext. When we work on the whole text, we can actually insert it just as it is. But otherwise we have to transform it back to the format VisualEditor uses, so we replace every linebreak with a closing and an opening paragraph tag. We could do so for the whole text as well, but just using  with a string is easier. Just note that inserting a string with linebreaks in it directly will force a newline before and after the inserted text. When you're replacing whole lines only (as you do when working on the complete text) this doesn't matter, but when you're inside a line you won't get the desired result when inserting the string directly.

If we worked on the whole text, we collapse the selection, i.e. we set the cursor to the start of the text. Note that if we started with a selection it is automatically adapted to the changed text (in our case: it is made shorter for the removed links).

Create, register and insert tool
Now that we have our command, we need a way to execute it. One way is to create a tool that will be shown in the toolbar. This is very similar to the example adding a tool to insert a template, which also has some more ways to execute a command. Here, we inherit directly from  (documentation), and override some of its properties: We register our tool and then add it (using its name) to the "Page options" menu. This part of the toolbar is defined in ve.init.mw.DesktopArticleTarget.js, and we just push our tool to the right list.
 * is the name of the tool (which coincides with the name of the command, but this isn't required).
 * is the name of the group the tool belongs to and actually doesn't really matter here.
 * is the text shown for that tool.
 * is a name from this list of available icons.
 * is the name of the command and links the tool to our command.
 * prevents the tool from being added automatically to the toolbar, we want to choose the place ourselves.
 * allows to execute the tool multiple times.
 * makes sure the tool isn't shown as active after execution.

Initialize
The code to initialize is the same as in VisualEditor/Gadgets/Add a tool.

More ideas
Here are some more ideas to try out yourself.

Source mode only
When you are porting an existing script for the old wikitext editor to the new one, you might at first want to only run the tool in source mode, not in visual mode. To do so, just add the following method:

This will disable your tool in visual mode. The execute method shouldn't be called in this case, but just to make sure, you should  when it is called in visual mode to indicate that nothing has been executed.

Better icon
The icon we chose doesn't really express what the tool does, and for other commands you might not find a suitable icon on that list. That's no problem, just use your own icon. All you need to do is to set the  property to something else (e.g.  ) and add a little bit of CSS: You can use any SVG image you want to. To add the CSS you can use.

Keep cursor position
When we work on the whole text, the cursor is set to the start of the text when we're done. It would be nice if it stayed in the original place, only shifted by the applied changes. Three steps are necessary:
 * 1) Get the original position:   tells you the starting position of a   (documentation), which is the cursor position for a collapsed selection. Note that linebreaks are counted the same way as above, so you have to do some additional calculations to get the actual position in the wikitext if you need it.
 * 2) Calculate the new position. This may be difficult, but this has nothing to do with VisualEditor.
 * 3) Set the new position: You can use the   method the same way it is used above to select the whole content, just pass a collapsed range here.