Skip to content

Commit

Permalink
Change custom request types to LSP commands
Browse files Browse the repository at this point in the history
LSP commands are the intended way to implement custom behaviour.
  • Loading branch information
remcohaszing committed Aug 30, 2024
1 parent 284608b commit 535fa9c
Show file tree
Hide file tree
Showing 11 changed files with 146 additions and 138 deletions.
6 changes: 6 additions & 0 deletions .changeset/large-shoes-applaud.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@mdx-js/language-service': minor
'@mdx-js/language-server': minor
---

Convert the custom MDX syntax toggle request types into LSP commands.
19 changes: 19 additions & 0 deletions packages/language-server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,23 @@ This language server supports all features supported by
[`volar-service-typescript`][volar-service-typescript], plus some additional
features specific to MDX.

#### Commands

The language server supports the following [LSP commands][]:

* `mdx.toggleDelete` — Toggle delete syntax at the cursor position.
This takes the URI as its first argument, and the LSP selection range as its
second argument.
* `mdx.toggleEmphasis` — Toggle emphasis syntax at the cursor position.
This takes the URI as its first argument, and the LSP selection range as its
second argument.
* `mdx.toggleInlineCode` — Toggle inline code syntax at the cursor position.
This takes the URI as its first argument, and the LSP selection range as its
second argument.
* `mdx.toggleStrong` — Toggle strong syntax at the cursor position.
This takes the URI as its first argument, and the LSP selection range as its
second argument.

### Initialize Options

MDX language server supports the following LSP initialization options:
Expand Down Expand Up @@ -271,6 +288,8 @@ Detailed changes for each release are documented in [CHANGELOG.md](./CHANGELOG.m

[lsp]: https://microsoft.github.io/language-server-protocol

[lsp commands]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#command

[mdx]: https://mdxjs.com

[mit]: LICENSE
Expand Down
31 changes: 0 additions & 31 deletions packages/language-server/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#!/usr/bin/env node

/**
* @import {Commands} from '@mdx-js/language-service'
* @import {PluggableList, Plugin} from 'unified'
*/

Expand All @@ -26,7 +25,6 @@ import remarkGfm from 'remark-gfm'
import {create as createMarkdownServicePlugin} from 'volar-service-markdown'
import {create as createTypeScriptServicePlugin} from 'volar-service-typescript'
import {create as createTypeScriptSyntacticServicePlugin} from 'volar-service-typescript/lib/plugins/syntactic.js'
import {URI} from 'vscode-uri'

process.title = 'mdx-language-server'

Expand Down Expand Up @@ -123,26 +121,6 @@ connection.onInitialize(async (parameters) => {
}
})

connection.onRequest('mdx/toggleDelete', async (parameters) => {
const commands = await getCommands(parameters.uri)
return commands.toggleDelete(parameters)
})

connection.onRequest('mdx/toggleEmphasis', async (parameters) => {
const commands = await getCommands(parameters.uri)
return commands.toggleEmphasis(parameters)
})

connection.onRequest('mdx/toggleInlineCode', async (parameters) => {
const commands = await getCommands(parameters.uri)
return commands.toggleInlineCode(parameters)
})

connection.onRequest('mdx/toggleStrong', async (parameters) => {
const commands = await getCommands(parameters.uri)
return commands.toggleStrong(parameters)
})

connection.onInitialized(() => {
const extensions = ['mdx']
if (tsEnabled) {
Expand All @@ -164,12 +142,3 @@ connection.onInitialized(() => {
})

connection.listen()

/**
* @param {string} uri
* @returns {Promise<Commands>}
*/
async function getCommands(uri) {
const service = await server.project.getLanguageService(URI.parse(uri))
return service.context.inject('mdxCommands')
}
55 changes: 30 additions & 25 deletions packages/language-server/test/syntax-toggle.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
/**
* @import {LanguageServerHandle} from '@volar/test-utils'
* @import {SyntaxToggleParams} from '@mdx-js/language-service'
* @import {Range} from '@volar/language-server'
*/

import assert from 'node:assert/strict'
import {afterEach, beforeEach, test} from 'node:test'
import {createServer, fixtureUri, tsdk} from './utils.js'
Expand All @@ -22,12 +23,13 @@ afterEach(() => {

test('delete', async () => {
await serverHandle.openInMemoryDocument('memory://1', 'mdx', 'Hello\n')
const result = await serverHandle.connection.sendRequest(
'mdx/toggleDelete',
/** @satisfies {SyntaxToggleParams} */ ({
uri: 'memory://1',
range: {end: {character: 3, line: 0}, start: {character: 3, line: 0}}
})
const result = await serverHandle.sendExecuteCommandRequest(
'mdx.toggleDelete',
[
'memory://1',
/** @satisfies {Range} */
({end: {character: 3, line: 0}, start: {character: 3, line: 0}})
]
)

assert.deepEqual(result, [
Expand All @@ -44,12 +46,13 @@ test('delete', async () => {

test('emphasis', async () => {
await serverHandle.openInMemoryDocument('memory://1', 'mdx', 'Hello\n')
const result = await serverHandle.connection.sendRequest(
'mdx/toggleEmphasis',
/** @satisfies {SyntaxToggleParams} */ ({
uri: 'memory://1',
range: {end: {character: 3, line: 0}, start: {character: 3, line: 0}}
})
const result = await serverHandle.sendExecuteCommandRequest(
'mdx.toggleEmphasis',
[
'memory://1',
/** @satisfies {Range} */
({end: {character: 3, line: 0}, start: {character: 3, line: 0}})
]
)

assert.deepEqual(result, [
Expand All @@ -66,12 +69,13 @@ test('emphasis', async () => {

test('inlineCode', async () => {
await serverHandle.openInMemoryDocument('memory://1', 'mdx', 'Hello\n')
const result = await serverHandle.connection.sendRequest(
'mdx/toggleInlineCode',
/** @satisfies {SyntaxToggleParams} */ ({
uri: 'memory://1',
range: {end: {character: 3, line: 0}, start: {character: 3, line: 0}}
})
const result = await serverHandle.sendExecuteCommandRequest(
'mdx.toggleInlineCode',
[
'memory://1',
/** @satisfies {Range} */
({end: {character: 3, line: 0}, start: {character: 3, line: 0}})
]
)

assert.deepEqual(result, [
Expand All @@ -88,12 +92,13 @@ test('inlineCode', async () => {

test('strong', async () => {
await serverHandle.openInMemoryDocument('memory://1', 'mdx', 'Hello\n')
const result = await serverHandle.connection.sendRequest(
'mdx/toggleStrong',
/** @satisfies {SyntaxToggleParams} */ ({
uri: 'memory://1',
range: {end: {character: 3, line: 0}, start: {character: 3, line: 0}}
})
const result = await serverHandle.sendExecuteCommandRequest(
'mdx.toggleStrong',
[
'memory://1',
/** @satisfies {Range} */
({end: {character: 3, line: 0}, start: {character: 3, line: 0}})
]
)

assert.deepEqual(result, [
Expand Down
18 changes: 16 additions & 2 deletions packages/language-service/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,22 @@ The service supports:

* Reporting diagnostics for parsing errors.
* Document drop support for images.
* Custom commands for toggling `delete`, `emphasis`, `inlineCode`, and `strong`
text.
* Custom commands.

The following commands are supported:

* `mdx.toggleDelete` — Toggle delete syntax at the cursor position.
This takes the URI as its first argument, and the LSP selection range as its
second argument.
* `mdx.toggleEmphasis` — Toggle emphasis syntax at the cursor position.
This takes the URI as its first argument, and the LSP selection range as its
second argument.
* `mdx.toggleInlineCode` — Toggle inline code syntax at the cursor position.
This takes the URI as its first argument, and the LSP selection range as its
second argument.
* `mdx.toggleStrong` — Toggle strong syntax at the cursor position.
This takes the URI as its first argument, and the LSP selection range as its
second argument.

#### Parameters

Expand Down
6 changes: 1 addition & 5 deletions packages/language-service/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
/**
* @typedef {import('./lib/commands.js').SyntaxToggleParams} SyntaxToggleParams
* @typedef {import('./lib/service-plugin.js').Commands} Commands
*/

export {commands} from './lib/commands.js'
export {createMdxLanguagePlugin} from './lib/language-plugin.js'
export {createMdxServicePlugin} from './lib/service-plugin.js'
export {resolveRemarkPlugins} from './lib/tsconfig.js'
32 changes: 17 additions & 15 deletions packages/language-service/lib/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,15 @@
* @import {Nodes} from 'mdast'
*/

/**
* @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.
* @param {LanguageServiceContext} context
* The Volar service context.
* @param {string} uri
* The URI of the document the request is for.
* @param {Range} range
* The range that is selected by the user.
* @returns {TextEdit[] | undefined}
* LSP text edits that should be made.
*/
Expand All @@ -29,17 +24,15 @@ import {VirtualMdxCode} from './virtual-code.js'
/**
* Create a function to toggle prose syntax based on the AST.
*
* @param {LanguageServiceContext} 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}) => {
function createSyntaxToggle(type, separator) {
return (context, uri, range) => {
const parsedUri = URI.parse(uri)
const sourceScript = context.language.scripts.get(parsedUri)
const root = sourceScript?.generated?.root
Expand Down Expand Up @@ -146,3 +139,12 @@ export function createSyntaxToggle(context, type, separator) {
}
}
}

export const implementations = {
'mdx.toggleDelete': createSyntaxToggle('delete', '~'),
'mdx.toggleEmphasis': createSyntaxToggle('emphasis', '_'),
'mdx.toggleInlineCode': createSyntaxToggle('inlineCode', '`'),
'mdx.toggleStrong': createSyntaxToggle('strong', '**')
}

export const commands = Object.keys(implementations)
37 changes: 13 additions & 24 deletions packages/language-service/lib/service-plugin.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,12 @@
/**
* @import {DataTransferItem, LanguageServicePlugin} from '@volar/language-service'
* @import {SyntaxToggle} from './commands.js'
*/

/**
* @typedef Commands
* @property {SyntaxToggle} toggleDelete
* @property {SyntaxToggle} toggleEmphasis
* @property {SyntaxToggle} toggleInlineCode
* @property {SyntaxToggle} toggleStrong
*/

/**
* @typedef Provide
* @property {() => Commands} mdxCommands
* @import {DataTransferItem, LanguageServicePlugin, Range} from '@volar/language-service'
*/

import path from 'node:path/posix'
import {toMarkdown} from 'mdast-util-to-markdown'
import {fromPlace} from 'unist-util-lsp'
import {URI, Utils} from 'vscode-uri'
import {createSyntaxToggle} from './commands.js'
import {commands, implementations} from './commands.js'
import {VirtualMdxCode} from './virtual-code.js'

// https://github.com/microsoft/vscode/blob/1.83.1/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts#L29-L41
Expand Down Expand Up @@ -62,19 +48,22 @@ export function createMdxServicePlugin() {
interFileDependencies: false,
workspaceDiagnostics: false
},
executeCommandProvider: {
commands
},
documentDropEditsProvider: true
},

create(context) {
return {
provide: {
mdxCommands() {
return {
toggleDelete: createSyntaxToggle(context, 'delete', '~'),
toggleEmphasis: createSyntaxToggle(context, 'emphasis', '_'),
toggleInlineCode: createSyntaxToggle(context, 'inlineCode', '`'),
toggleStrong: createSyntaxToggle(context, 'strong', '**')
}
executeCommand(command, args) {
if (
command in implementations &&
Object.hasOwn(implementations, command)
) {
const fn =
implementations[/** @type {keyof implementations} */ (command)]
return fn(context, .../** @type {[string, Range]} */ (args))
}
},

Expand Down
1 change: 1 addition & 0 deletions packages/vscode-mdx/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"vscode:prepublish": "node ./script/build.mjs"
},
"devDependencies": {
"@mdx-js/language-service": "^0.5.0",
"@types/node": "^22.0.0",
"@types/vscode": "^1.82.0",
"@volar/language-server": "~2.4.0",
Expand Down
Loading

0 comments on commit 535fa9c

Please sign in to comment.