-
-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Create commands to toggle prose syntax (#384)
The prose syntax is based on the current text selection and the AST. If the selection is within range of the syntax that’s toggled, the syntax is removed. Otherwise the selected text is expanded to include full words and wrapped. The supported syntaxes are: - `delete` - `emphasis` - `inlineCode` - `strong` Closes #353
- Loading branch information
1 parent
16a3240
commit b9a910e
Showing
11 changed files
with
414 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"vscode-mdx": minor | ||
--- | ||
|
||
Support the commands `mdx.toggleDelete`, `mdx.toggleEmphasis`, `mdx.toggleInlineCode`, and `mdx.toggleStrong`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
"@mdx-js/language-service": minor | ||
"@mdx-js/language-server": minor | ||
--- | ||
|
||
Support the commands `mdx/toggleDelete`, `mdx/toggleEmphasis`, `mdx/toggleInlineCode`, and `mdx/toggleStrong`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,7 @@ | ||
/** | ||
* @typedef {import('./lib/service-plugin.js').Commands} Commands | ||
*/ | ||
|
||
export {createMdxLanguagePlugin} from './lib/language-plugin.js' | ||
export {createMdxServicePlugin} from './lib/service-plugin.js' | ||
export {resolveRemarkPlugins} from './lib/tsconfig.js' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
/** | ||
* @typedef {import('@volar/language-service').Range} Range | ||
* @typedef {import('@volar/language-service').ServiceContext} ServiceContext | ||
* @typedef {import('@volar/language-service').TextEdit} TextEdit | ||
* @typedef {import('mdast').Nodes} Nodes | ||
*/ | ||
|
||
/** | ||
* @typedef SyntaxToggleParams | ||
* The request parameters for LSP toggle requests. | ||
* @property {string} uri | ||
* The URI of the document the request is for. | ||
* @property {Range} range | ||
* The range that is selected by the user. | ||
*/ | ||
|
||
/** | ||
* @callback SyntaxToggle | ||
* A function to toggle prose markdown syntax based on the AST. | ||
* @param {SyntaxToggleParams} params | ||
* The input parameters from the LSP request. | ||
* @returns {TextEdit[] | undefined} | ||
* LSP text edits that should be made. | ||
*/ | ||
|
||
import {visitParents} from 'unist-util-visit-parents' | ||
import {getNodeEndOffset, getNodeStartOffset} from './mdast-utils.js' | ||
import {VirtualMdxFile} from './virtual-file.js' | ||
|
||
/** | ||
* Create a function to toggle prose syntax based on the AST. | ||
* | ||
* @param {ServiceContext} context | ||
* The Volar service context. | ||
* @param {Nodes['type']} type | ||
* The type of the mdast node to toggle. | ||
* @param {string} separator | ||
* The mdast node separator to insert. | ||
* @returns {SyntaxToggle} | ||
* An LSP based syntax toggle function. | ||
*/ | ||
export function createSyntaxToggle(context, type, separator) { | ||
return ({range, uri}) => { | ||
const file = getVirtualMdxFile(context, uri) | ||
|
||
if (!file) { | ||
return | ||
} | ||
|
||
const ast = file.ast | ||
|
||
if (!ast) { | ||
return | ||
} | ||
|
||
const doc = context.documents.get(uri, file.languageId, file.snapshot) | ||
const selectionStart = doc.offsetAt(range.start) | ||
const selectionEnd = doc.offsetAt(range.end) | ||
|
||
/** @type {TextEdit[]} */ | ||
const edits = [] | ||
|
||
visitParents(ast, 'text', (node, ancestors) => { | ||
const nodeStart = getNodeStartOffset(node) | ||
const nodeEnd = getNodeEndOffset(node) | ||
|
||
if (selectionStart < nodeStart) { | ||
// Outside of this node | ||
return | ||
} | ||
|
||
if (selectionEnd > nodeEnd) { | ||
// Outside of this node | ||
return | ||
} | ||
|
||
const matchingAncestor = ancestors.find( | ||
(ancestor) => ancestor.type === type | ||
) | ||
|
||
if (matchingAncestor) { | ||
const ancestorStart = getNodeStartOffset(matchingAncestor) | ||
const ancestorEnd = getNodeEndOffset(matchingAncestor) | ||
const firstChildStart = getNodeStartOffset(matchingAncestor.children[0]) | ||
const lastChildEnd = getNodeEndOffset( | ||
/** @type {Nodes} */ (matchingAncestor.children.at(-1)) | ||
) | ||
|
||
edits.push( | ||
{ | ||
newText: '', | ||
range: { | ||
start: doc.positionAt(ancestorStart), | ||
end: doc.positionAt(firstChildStart) | ||
} | ||
}, | ||
{ | ||
newText: '', | ||
range: { | ||
start: doc.positionAt(lastChildEnd), | ||
end: doc.positionAt(ancestorEnd) | ||
} | ||
} | ||
) | ||
} else { | ||
const valueOffset = getNodeStartOffset(node) | ||
let insertStart = valueOffset | ||
let insertEnd = getNodeEndOffset(node) | ||
|
||
for (const match of node.value.matchAll(/\b/g)) { | ||
if (match.index === undefined) { | ||
continue | ||
} | ||
|
||
const matchOffset = valueOffset + match.index | ||
|
||
if (matchOffset <= selectionStart) { | ||
insertStart = matchOffset | ||
continue | ||
} | ||
|
||
if (matchOffset >= selectionEnd) { | ||
insertEnd = matchOffset | ||
break | ||
} | ||
} | ||
|
||
const startPosition = doc.positionAt(insertStart) | ||
const endPosition = doc.positionAt(insertEnd) | ||
edits.push( | ||
{ | ||
newText: separator, | ||
range: {start: startPosition, end: startPosition} | ||
}, | ||
{ | ||
newText: separator, | ||
range: {start: endPosition, end: endPosition} | ||
} | ||
) | ||
} | ||
}) | ||
|
||
if (edits) { | ||
return edits | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Get the virtual MDX file that matches a document uri. | ||
* | ||
* @param {ServiceContext} context | ||
* The Volar service context to use. | ||
* @param {string} uri | ||
* The uri of which to find the matching virtual MDX file. | ||
* @returns {VirtualMdxFile | undefined} | ||
* The matching virtual MDX file, if it exists. Otherwise undefined. | ||
*/ | ||
export function getVirtualMdxFile(context, uri) { | ||
const [file] = context.language.files.getVirtualFile( | ||
context.env.uriToFileName(uri) | ||
) | ||
|
||
if (file instanceof VirtualMdxFile) { | ||
return file | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
/** | ||
* @typedef {import('mdast').Nodes} Nodes | ||
* @typedef {import('unist').Point} Point | ||
* @typedef {import('unist').Position} Position | ||
*/ | ||
|
||
/** | ||
* Get the offset of a parsed unist point. | ||
* | ||
* @param {Point} point | ||
* The unist point of which to get the offset. | ||
* @returns {number} | ||
* The offset of the unist point. | ||
*/ | ||
export function getPointOffset(point) { | ||
return /** @type {number} */ (point.offset) | ||
} | ||
|
||
/** | ||
* Get the start offset of a parsed unist point. | ||
* | ||
* @param {Nodes} node | ||
* The unist point of which to get the start offset. | ||
* @returns {number} | ||
* The start offset of the unist point. | ||
*/ | ||
export function getNodeStartOffset(node) { | ||
return getPointOffset(/** @type {Position} */ (node.position).start) | ||
} | ||
|
||
/** | ||
* Get the end offset of a parsed unist point. | ||
* | ||
* @param {Nodes} node | ||
* The unist point of which to get the end offset. | ||
* @returns {number} | ||
* The end offset of the unist point. | ||
*/ | ||
export function getNodeEndOffset(node) { | ||
return getPointOffset(/** @type {Position} */ (node.position).end) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.