VisualEditor/Gadgets/Creating a custom command

This is an example for how to create a custom command. 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). Currently it only works in the 2017 wikitext editor and only when text is selected.

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 two methods:

In, we check whether we are in source code mode or not. As the command currently only works with wikitext, we return  if we are in visual mode.

The  method is the place where we do the actual work: First, we check again the mode. This shouldn't be necessary, as no code should try to execute a command that isn't executable. But just in case we return  to indicate that nothing has been executed.

Next, we get the current selection. If it is collapsed (i.e. nothing is selected) we select the whole document. From this selection we get the wikitext. 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 than 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.

Visual mode
It would be really cool if someone could make this tool work in visual mode, too. If you succeeded to do so, please share your code!

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.