I made a VSCode extension: "Visible Whitespace"
Tags: typescript, vscode, extension
Published: 2023-04-29 20:57:00

Last month, I made [a VSCode extension](https://dev.to/yoshi389111/i-made-a-vscode-extension-markdown-table-rainbow-4l27) and learned about VSCode Decorators for it.
At that time, I wanted to create another VSCode extension even if I used a certain function of Decorator.

And what I made is "Visible Whitespace".
This is an extension that makes whitespace visible.

A similar extension already exists.
However, in order to output the results of learning, I decided to make one myself.

This extension is published on the marketplace.
* marketplace: [Visible Whitespace](https://marketplace.visualstudio.com/items?itemName=yoshi389111.visible-whitespace)

The source code is on GitHub.

{% embed https://github.com/yoshi389111/visible-whitespace %}

## How to install

Download it from the marketplace or search for `Visible Whitespace` in VSCode Extensions and install it.

## Features

This extension visually displays the following whitespace characters:

* H-TAB (U+0009) - Disabled by default.
* LF (U+000A) or CRLF (U+000D + U+000A)
* Space (U+0020) - Disabled by default.
* No-Break Space (U+00A0)
* Ideographic Space (U+3000)
* other whitespace (U+180E, U+2000-U+200A, U+202F, U+205F, U+2800)
* EOF

![screenshot](https://github.com/yoshi389111/visible-whitespace/raw/main/assets/screenshot.png)

## Extension Settings

* `visibleWhitespace.enabledLanguageIds`: A list of languageIds for which this extension should be enabled. If empty, it will be enabled for all languageIds. e.g. `[ "plaintext", "markdown" ]`
* `visibleWhitespace.updateDelay`: Delay time in milliseconds before updating. Smaller values will color faster, but will increase processing cost.
* `visibleWhitespace.overlayColor`: Specify the color of the overlay text. eg. `#RRGGBB`, `rgb(R,G,B)` or `rgba(R,G,B,A)` format.
* `visibleWhitespace.htab.enable`: Enables making horizontal tabs visible. (default: false)
* `visibleWhitespace.htab.text`: Text to overlay on horizontal tabs.
* `visibleWhitespace.newLine.enable`: Enables making new line visible.
* `visibleWhitespace.newLine.lf`: Text of LF.
* `visibleWhitespace.newLine.crLf`: Text of CRLF.
* `visibleWhitespace.newLine.color`: Color of text that represents new line.  eg. `#RRGGBB`, `rgb(R,G,B)` or `rgba(R,G,B,A)` format.
* `visibleWhitespace.space.enable`: Enables making spaces visible. (default: false)
* `visibleWhitespace.space.render`: Controls how the editor should render spaces. (default: "all")
* `visibleWhitespace.space.text`: Text to overlay on spaces.
* `visibleWhitespace.nbsp.enable`: Enables making no-break spaces visible.
* `visibleWhitespace.nbsp.text`: Text to overlay on no-break spaces.
* `visibleWhitespace.widespace.enable`: Enables making wide-spaces visible.
* `visibleWhitespace.widespace.text`: Text to overlay on wide-spaces.
* `visibleWhitespace.other.enable`: Enables making other whitespaces visible.
* `visibleWhitespace.other.borderColor`: Border color of other whitespace.  eg. `#RRGGBB`, `rgb(R,G,B)` or `rgba(R,G,B,A)` format.
* `visibleWhitespace.eof.enable`: Enables making EOF visible.
* `visibleWhitespace.eof.text`: Text of EOF.
* `visibleWhitespace.eof.color`: Color of text that represents EOF.  eg. `#RRGGBB`, `rgb(R,G,B)` or `rgba(R,G,B,A)` format.

The default for `visibleWhitespace.htab.enable` and `visibleWhitespace.space.enable` is disable because the standard `editor.renderWhitespace` is used.

If you want to use this extension instead of the standard `editor.renderWhitespace`, set `"editor.renderWhitespace": "none"` and set the above definition to `true`.

## Sites used as reference for creation

- [official VSCode API](https://code.visualstudio.com/api/references/vscode-api)
- [Official sample extension (Decorator)](https://github.com/microsoft/vscode-extension-samples/tree/main/decorator-sample)

## Miscellaneous Notes

### overlay characters

I didn't understand it just by looking at the API specifications, but I learned that you can use character decorations to overlay characters.

If you specify the content in `before` and `"0"` in `width`, it will be displayed overlapping.

```typescript
    const regex = /\u3000/g;
    const decorationType = vscode.window.createTextEditorDecorationType({
        rangeBehavior: vscode.DecorationRangeBehavior.ClosedClosed,
        before: {
            contentText: "\u2395",
            width: "0",
            color: "rgb(127,127,127)",
        },
    });
```

This overlay feature made me want to make this extension.

### Interference with other extensions

Originally, the newline code was displayed as a normal `before`.
In other words, you didn't set `widht` to `"0"`.
However, when I used it in conjunction with some other extension, it behaved strangely.

Specifically, with this extension only, the cursor does not move behind the before code of the new line code.
However, when used with some extensions, the cursor was not positioned in front of the arrow of the new line code, and moved behind the arrow. .

Therefore, I specified `width: "0"` for the new line code and EOF just in case.

I have not been able to investigate what specific extensions were hit.

### APL symbols, etc.

Currently, wide-spaces are displayed by overlapping squares by default.

The overlayed square "&#x2395;" (U+2395) is a character called "APL Functional Symbol Quad" in Unicode, and it seems to be a symbol for functions in the APL language.

At first I was going to use "□" (U+25A1; "White Square").
However, this character is designed to be wide in fonts such as Japanese, but is designed to be narrow (small because it is square) in fonts such as English.
In [East Asian Width](https://www.unicode.org/reports/tr11/), this character is treated as A (ambiguous).
That is, characters that may or may not be wide depending on the context in which they appear.
Therefore, I thought that it might not be displayed well depending on the setting.
So we use U+2395 above.
It's a character added in Unicode 3.0, so I think it's already displayable in most environments.

If there seems to be an environment where the APL symbol is not displayed well, please define to display it with a different character.

Similarly, No-Break Space is also an APL symbol. If this is also not displayed well, please define so that it is displayed with some suitable character.

If there are any other problems related to East Asian character widths, etc., please define them so that they are displayed with appropriate characters as well.

### Text decoration border

Above, "other whitespaces" are marked with a vermilion border.
However, if these characters are consecutive, the border range will join.

Why?

### Display of before/after elements remains temporarily

When deleting the new line code, the arrow representing the new line code may remain for a moment.
Then it disappears after the display update delay time.

At that time, `width: "0"` was not specified for the new line code.
In that state, if you delete multiple lines at once, multiple arrows representing new line codes are displayed together for a moment immediately after deletion, and then disappear.

In my environment, after setting the display width of the newline code to `width: "0"`, it doesn't bother me much.
If it bothers you, please turn off the new line code display.

### Mixed new line code

Even if he opens a file with line endings mixed with LF and CRLF, VSCode seems to compensate for either when opening the file.
So, I don't think there is basically a case where new line codes are mixed on one editor.

### Defining activationEvents

Initially, this extension did not narrow down the file type and set `"activationEvents": ["*"]`, so when the extension was published (when the `vsce publish` command was executed), a warning was issued every time. I was.

I reread the [documentation](https://code.visualstudio.com/api/references/activation-events#onStartupFinished) and found something called `onStartupFinished`.

> This activation event is emitted and interested extensions will be activated some time after VS Code starts up. This is similar to the `*` activation event, but it will not slow down VS Code startup. Currently, this event is emitted after all the `*` activated extensions have finished activating.

`*` slows down the startup performance, but `onStartupFinished` is called after the startup process is finished, so I decided that the performance problem would disappear.

### With or without BOM

For files with BOM, I wanted to place the BOM gutter icon on the first line.

However, it seems that VSCode cannot acquire the character code and the presence or absence of her BOM, so I gave up.

### EOF range specification

The EOF range specifies a range of 0 characters after the last character of the document.
Feeling like the following.

```typescript
            const eofPosition = editor.document.positionAt(text.length);
            const range = new vscode.Range(eofPosition, eofPosition);
```

Such specifications were also possible.

### color specification format

There are some items in `settings.json` that let you specify colors.
There is no color in VSCode's definition type, so I'm trying to have it defined as a `string`.

If it is just `string`, it will be in free format, so we specify a regular expression for `pattern` in `package.json` to check the color specification format.

Currently, the following three patterns are allowed.

* `#RRGGBB` format. RR/GG/BB are two hexadecimal digits
* `rgb(R,G,B)` format. R/G/B ranges from 0 to 255. A cannot be specified
* `rgba(R,G,B,A)` format. R/G/B ranges from 0 to 255. A is from 0 to 1, and the decimal point format is also possible (in fact, `1.999` etc. may also be specified)

No other formats are currently accepted.
This is because only one regular expression is specified in the `pattern` of `package.json`, so it is difficult to perform complicated checks.

  Is it better to specify `#RGB`, `#RRGGBBAA` and `hsv()`?
Also, would it be better if `rgb()` could specify the alpha channel as well?

## Japanese version of this document

* Qiita: [全角スペース/改行/EOFとかを可視化するVSCode拡張機能を作ったよ](https://qiita.com/yoshi389111/items/0ae999c73acb2887424b)