Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

codemirror/lsp-client

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

42 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@codemirror/lsp-client NPM version

[ WEBSITE | ISSUES | FORUM | CHANGELOG ]

This package implements a language server protocol (LSP) client for the CodeMirror code editor.

The project page has more information, a number of examples and the documentation.

Note that this code does not have a license yet. That should soon change.

We aim to be an inclusive, welcoming community. To make that explicit, we have a code of conduct that applies to communication around the project.

Usage

There are various ways to run a language server and connect it to a web page. You can run it on the server and proxy it through a web socket, or, if it is written in JavaScript or can be compiled to WASM, run it directly in the client. The @codemirror/lsp-client package talks to the server through a (Transport) object, which exposes a small interface for sending and receiving JSON messages.

Responsibility for how to actually talk to the server, how to connect and to handle disconnects are left to the code that implements the transport.

This example uses a crude transport that doesn't handle errors at all.

import {Transport, LSPClient, languageServerSupport} from "@codemirror/lsp-client"
import {basicSetup, EditorView} from "codemirror"
import {typescriptLanguage} from "@codemirror/lang-javascript"

function simpleWebSocketTransport(uri: string): Promise<Transport> {
  let handlers: ((value: string) => void)[] = []
  let sock = new WebSocket(uri)
  sock.onmessage = e => { for (let h of handlers) h(e.data.toString()) }
  return new Promise(resolve => {
    sock.onopen = () => resolve({
      send(message: string) { sock.send(message) },
      subscribe(handler: (value: string) => void) { handlers.push(handler) },
      unsubscribe(handler: (value: string) => void) { handlers = handlers.filter(h => h != handler) }
    })
  })
}

let transport = await simpleWebSocketTransport("ws://host:port")
let client = new LSPClient().connect(transport)

new EditorView({
  extensions: [
    basicSetup,
    typescriptLanguage,
    languageServerSupport(client, "file:///some/file.ts"),
  ],
  parent: document.body
})

API Reference

Client

class LSPClient

An LSP client manages a connection to a language server. It should be explicitly connected before use.

new LSPClient(config⁠?: LSPClientConfig = {})

Create a client object.

workspace: Workspace

The client's workspace.

serverCapabilities: ServerCapabilities | null

The capabilities advertised by the server. Will be null when not connected or initialized.

initializing: Promise<null>

A promise that resolves once the client connection is initialized. Will be replaced by a new promise object when you call disconnect.

connected: boolean

Whether this client is connected (has a transport).

connect(transportTransport) → LSPClient

Connect this client to a server over the given transport. Will immediately start the initialization exchange with the server, and resolve this.initializing (which it also returns) when successful.

disconnect()

Disconnect the client from the server.

didOpen(fileWorkspaceFile)

Send a textDocument/didOpen notification to the server.

didClose(uristring)

Send a textDocument/didClose notification to the server.

request<Params, Result>(methodstring, paramsParams) → Promise<Result>

Make a request to the server. Returns a promise that resolves to the response or rejects with a failure message. You'll probably want to use types from the vscode-languageserver-protocol package for the type parameters.

The caller is responsible for synchronizing state before the request and correctly handling state drift caused by local changes that happend during the request.

notification<Params>(methodstring, paramsParams)

Send a notification to the server.

cancelRequest(params: any)

Cancel the in-progress request with the given parameter value (which is compared by identity).

workspaceMapping() → WorkspaceMapping

Create a workspace mapping that tracks changes to files in this client's workspace, relative to the moment where it was created. Make sure you call destroy on the mapping when you're done with it.

withMapping<T>(f: fn(mappingWorkspaceMapping) → Promise<T>) → Promise<T>

Run the given promise with a workspace mapping active. Automatically release the mapping when the promise resolves or rejects.

sync()

Push any pending changes in the open files to the server. You'll want to call this before most types of requests, to make sure the server isn't working with outdated information.

Configuration options that can be passed to the LSP client.

rootUri⁠?: string

The project root URI passed to the server, when necessary.

workspace⁠?: fn(clientLSPClient) → Workspace

An optional function to create a workspace object for the client to use. When not given, this will default to a simple workspace that only opens files that have an active editor, and only allows one editor per file.

timeout⁠?: number

The amount of milliseconds after which requests are automatically timed out. Defaults to 3000.

sanitizeHTML⁠?: fn(htmlstring) → string

LSP servers can send Markdown code, which the client must render and display as HTML. Markdown can contain arbitrary HTML and is thus a potential channel for cross-site scripting attacks, if someone is able to compromise your LSP server or your connection to it. You can pass an HTML sanitizer here to strip out suspicious HTML structure.

highlightLanguage⁠?: fn(namestring) → Language | null

By default, the Markdown renderer will only be able to highlght code embedded in the Markdown text when its language tag matches the name of the language used by the editor. You can provide a function here that returns a CodeMirror language object for a given language tag to support more languages.

notificationHandlers⁠?: Object<fn(clientLSPClient, params: any) → boolean>

By default, the client will only handle the server notifications window/logMessage (logging warnings and errors to the console) and window/showMessage. You can pass additional handlers here. They will be tried before the built-in handlers, and override those when they return true.

unhandledNotification⁠?: fn(clientLSPClient, methodstring, params: any)

When no handler is found for a notification, it will be passed to this function, if given.

type Transport

An object of this type should be used to wrap whatever transport layer you use to talk to your language server. Messages should contain only the JSON messages, no LSP headers.

send(messagestring)

Send a message to the server. Should throw if the connection is broken somehow.

subscribe(handler: fn(valuestring))

Register a handler for messages coming from the server.

unsubscribe(handler: fn(valuestring))

Unregister a handler registered with subscribe.

class LSPPlugin

A plugin that connects a given editor to a language server client.

client: LSPClient

The client connection.

uri: string

The URI of this file.

view: EditorView

The editor view that this plugin belongs to.

docToHTML(valuestring | MarkupContent, defaultKind⁠?: MarkupKind = "plaintext") → string

Render a doc string from the server to HTML.

toPosition(posnumber, doc⁠?: Text = this.view.state.doc) → Position

Convert a CodeMirror document offset into an LSP {line, character} object. Defaults to using the view's current document, but can be given another one.

fromPosition(posPosition, doc⁠?: Text = this.view.state.doc) → number

Convert an LSP {line, character} object to a CodeMirror document offset.

reportError(messagestring, err: any)

Display an error in this plugin's editor.

unsyncedChanges: ChangeSet

The changes accumulated in this editor that have not been sent to the server yet.

clear()

Reset the unsynced changes. Should probably only be called by a workspace.

static get(viewEditorView) → LSPPlugin | null

Get the LSP plugin associated with an editor, if any.

static create(clientLSPClient, fileURIstring, languageID⁠?: string) → Extension

Create an editor extension that connects that editor to the given LSP client. This extension is necessary to use LSP-related functionality exported by this package. Creating an editor with this plugin will cause openFile to be called on the workspace.

By default, the language ID given to the server for this file is derived from the editor's language configuration via Language.name. You can pass in a specific ID as a third parameter.

A workspace mapping is used to track changes made to open documents, so that positions returned by a request can be interpreted in terms of the current, potentially changed document.

getMapping(uristring) → ChangeDesc | null

Get the changes made to the document with the given URI since the mapping was created. Returns null for documents that aren't open.

mapPos(uristring, posnumber, assoc⁠?: number) → number

Map a position in the given file forward to the current document state.

mapPosition(uristring, posPosition, assoc⁠?: number) → number

Convert an LSP-style position referring to a document at the time the mapping was created to an offset in the current document.

destroy()

Disconnect this mapping from the client so that it will no longer be notified of new changes. You must make sure to call this on every mapping you create, except when you use withMapping, which will automatically schedule a disconnect when the given promise resolves.

Workspaces

abstract class Workspace

Implementing your own workspace class can provide more control over the way files are loaded and managed when interacting with the language server. See LSPClientConfig.workspace.

new Workspace(clientLSPClient)

The constructor, as called by the client when creating a workspace.

abstract files: WorkspaceFile[]

The files currently open in the workspace.

client: LSPClient

The LSP client associated with this workspace.

getFile(uristring) → WorkspaceFile | null

Find the open file with the given URI, if it exists. The default implementation just looks it up in this.files.

abstract syncFiles() → readonly {file: WorkspaceFile, prevDoc: Text, changes: ChangeSet}[]

Check all open files for changes (usually from editors, but they may also come from other sources). When a file is changed, return a record that describes the changes, and update the file's version and doc properties to reflect the new version.

requestFile(uristring) → Promise<WorkspaceFile | null>

Called to request that the workspace open a file. The default implementation simply returns the file if it is open, null otherwise.

abstract openFile(uristring, languageIdstring, viewEditorView)

Called when an editor is created for a file. The implementation should track the file in this.files and, if it wasn't open already, call LSPClient.didOpen.

abstract closeFile(uristring, viewEditorView)

Called when an editor holding this file is destroyed or reconfigured to no longer hold it. The implementation should track this and, when it closes the file, make sure to call LSPClient.didOpen.

connected()

Called when the client for this workspace is connected. The default implementation calls LSPClient.didOpen on all open files.

disconnected()

Called when the client for this workspace is disconnected. The default implementation does nothing.

updateFile(uristring, updateTransactionSpec)

Called when a server-initiated change to a file is applied. The default implementation simply dispatches the update to the file's view, if the file is open and has a view.

displayFile(uristring) → Promise<EditorView | null>

When the client needs to put a file other than the one loaded in the current editor in front of the user, for example in jumpToDefinition, it will call this function. It should make sure to create or find an editor with the file and make it visible to the user, or return null if this isn't possible.

interface WorkspaceFile

A file that is open in a workspace.

uri: string

The file's unique URI.

languageId: string

The LSP language ID for the file's content.

version: number

The current version of the file.

doc: Text

The document corresponding to this.version. Will not reflect changes made after that version was synchronized. Will be updated, along with version, by syncFiles.

getView(main⁠?: EditorView) → EditorView | null

Get an active editor view for this file, if there is one. For workspaces that support multiple views on a file, main indicates a preferred view.

Extensions

languageServerSupport(clientLSPClient, uristring, languageID⁠?: string) → Extension

Returns an extension that enables the LSP plugin and all other features provided by this package. You can also pick and choose individual extensions from the exports. In that case, make sure to also include LSPPlugin.create in your extensions, or the others will not work.

serverCompletion(config⁠?: Object = {}) → Extension

Register the language server completion source as an autocompletion source.

config
override⁠?: boolean

By default, the completion source that asks the language server for completions is added as a regular source, in addition to any other sources. Set this to true to make it replace all completion sources.

serverCompletionSource: CompletionSource

A completion source that requests completions from a language server.

hoverTooltips(config⁠?: {hoverTime⁠?: number} = {}) → Extension

Create an extension that queries the language server for hover tooltips when the user hovers over the code with their pointer, and displays a tooltip when the server provides one.

formatDocument: Command

This command asks the language server to reformat the document, and then applies the changes it returns.

formatKeymap: readonly KeyBinding[]

A keymap that binds Shift-Alt-f to formatDocument.

renameSymbol: Command

This command will, if the cursor is over a word, prompt the user for a new name for that symbol, and ask the language server to perform a rename of that symbol.

Note that this may affect files other than the one loaded into this view. See the Workspace.updateFile method.

renameKeymap: readonly KeyBinding[]

A keymap that binds F2 to renameSymbol.

signatureHelp(config⁠?: {keymap⁠?: boolean} = {}) → Extension

Returns an extension that enables signature help. Will bind the keys in signatureKeymap unless keymap is set to false.

showSignatureHelp: Command

Explicitly prompt the server to provide signature help at the cursor.

nextSignature: Command

If there is an active signature tooltip with multiple signatures, move to the next one.

prevSignature: Command

If there is an active signature tooltip with multiple signatures, move to the previous signature.

signatureKeymap: readonly KeyBinding[]

A keymap that binds

Note that these keys are automatically bound by signatureHelp unless you pass it keymap: false.

jumpToDefinition: Command

Jump to the definition of the symbol at the cursor. To support cross-file jumps, you'll need to implement Workspace.displayFile.

jumpToDeclaration: Command

Jump to the declaration of the symbol at the cursor.

jumpToTypeDefinition: Command

Jump to the type definition of the symbol at the cursor.

jumpToImplementation: Command

Jump to the implementation of the symbol at the cursor.

jumpToDefinitionKeymap: readonly KeyBinding[]

Binds F12 to jumpToDefinition.

findReferences: Command

Ask the server to locate all references to the symbol at the cursor. When the server can provide such references, show them as a list in a panel.

closeReferencePanel: Command

Close the reference panel, if it is open.

findReferencesKeymap: readonly KeyBinding[]

Binds Shift-F12 to findReferences and Escape to closeReferencePanel.

About

Language server protocol client for CodeMirror

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published
Morty Proxy This is a proxified and sanitized view of the page, visit original site.