microsoft / microsoft/vscode

Markdown smart select: a single-asterisk italic span on the line discards all Markdown selection ranges

Open
#335,382 0 comments 0 reactions 1 assignee Claimed by @hediet View on GitHub
Dominant language
TypeScript
Stars
193k
Forks
42.4k
PR merge metrics
PR metrics pending

Description

Type: Bug

VS Code version: 1.137.0 (x64, Windows 11)
Extensions: reproduces with no extension contributing Markdown selection ranges

## Steps to reproduce

1. Create a Markdown file:

```md
# Section

alpha *beta* gamma delta

# Next
```

2. Put the cursor on the word `alpha` (anywhere on that line *outside* the `*beta*` span).
3. Press Expand Selection (Shift+Alt+RightArrow) repeatedly.

## Expected

`alpha` → the line → the paragraph → the section → the document.

## Actual

`alpha` → the line → **the whole document**. Every Markdown range (paragraph, section) is missing. The Extension Host log shows:

```
INVALID selection range, must contain the previous range: Error: INVALID selection range, must contain the previous range
```

Putting the cursor inside `*beta*` gives the full chain, so the loss depends only on where the cursor is relative to the italic span. `**bold**` and `` `code` `` on the line do not trigger it; a single-asterisk span does. A line without any `*` is fine.

## Cause

[`createOtherInlineRange`](https://github.com/microsoft/vscode-markdown-languageservice/blob/main/src/languageFeatures/smartSelect.ts#L315) matches the italic span *together with the text around it* — the pattern is `(?:[^*]+)(\*…\*)(?:[^*]+)`, with the span itself in group 1 — but then tests the cursor against the whole match, `match[0]`, instead of against group 1:

```ts
matches = [...lineText.matchAll(italicRegexes[0])].filter(match =>
lineText.indexOf(match[0]) <= cursorChar &&
lineText.indexOf(match[0]) + match[0].length >= cursorChar);
```

So a cursor anywhere on the line counts as being inside the italic, and the provider returns a chain whose innermost range (`*beta*`) does not contain the cursor. The extension host's selection-range adapter validates the chain and throws on the first range that fails to contain the position, which discards the provider's entire result for that position — hence not just a wrong innermost range, but no Markdown ranges at all.

The same `lineText.indexOf(...)` is then used to place the range, which mislocates it when the same text occurs earlier on the line.

`createBoldRange` is unaffected: its regex matches only the bold span, so the same containment test is correct there.

## Suggested fix

Filter and position on group 1's own offsets rather than `match[0]`'s — e.g. by adding the `d` flag to the italic regexes and using `match.indices[1]`:

```ts
const [start, end] = match.indices[1];
return start <= cursorChar && cursorChar <= end;
```

## Impact

Any single-asterisk emphasis in a paragraph breaks Expand Selection for the rest of that line, and with it anything built on `vscode.executeSelectionRangeProvider` for those positions.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.