Skip to content
Merged
1,053 changes: 581 additions & 472 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"tslib": "^2.3.1"
},
"resolutions": {
"vscode-languageserver-types": "3.16.0"
"vscode-languageserver-types": "3.17.1"
},
"scripts": {
"clean": "npm run clean --workspaces",
Expand Down
6 changes: 3 additions & 3 deletions packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,13 @@
],
"dependencies": {
"glob-to-regexp": "^0.4.1",
"vscode-jsonrpc": "6.0.0",
"vscode-languageclient": "7.0.0",
"vscode-jsonrpc": "8.0.1",
"vscode-languageclient": "8.0.1",
"vscode-languageserver-textdocument": "1.0.4",
"vscode-uri": "3.0.3"
},
"devDependencies": {
"@types/vscode": "1.66.0",
"@types/vscode": "1.67.0",
"@types/node": "^16.11.7",
"@types/glob-to-regexp": "^0.4.1"
},
Expand Down
7 changes: 3 additions & 4 deletions packages/client/src/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
NotificationHandler, NotificationHandler0, GenericNotificationHandler,
Trace, Tracer, CancellationToken, MessageConnection, MessageSignature, Disposable, ProgressType
} from 'vscode-jsonrpc';
import { MessageTransports } from 'vscode-languageclient';

import {
InitializeRequest, InitializeParams, InitializeResult,
Expand All @@ -29,8 +30,6 @@ import {

import * as Is from 'vscode-languageserver-protocol/lib/common/utils/is';

import { OutputChannel } from "./services";

export interface IConnection {

listen(): void;
Expand Down Expand Up @@ -90,7 +89,7 @@ export interface ConnectionCloseHandler {
(): void;
}
export interface IConnectionProvider {
get(errorHandler: ConnectionErrorHandler, closeHandler: ConnectionCloseHandler, outputChannel: OutputChannel | undefined): Thenable<IConnection>;
get(encoding: string): Promise<MessageTransports>;
}
export function createConnection(connection: MessageConnection, errorHandler: ConnectionErrorHandler, closeHandler: ConnectionCloseHandler): IConnection {
connection.onError((data) => { errorHandler(data[0], data[1], data[2]) });
Expand All @@ -108,7 +107,7 @@ export function createConnection(connection: MessageConnection, errorHandler: Co
onProgress: <P>(type: ProgressType<P>, token: string | number, handler: NotificationHandler<P>): Disposable => connection.onProgress(type, token, handler),
sendProgress: async <P>(type: ProgressType<P>, token: string | number, value: P) => connection.sendProgress(type, token, value),

trace: (value: Trace, tracer: Tracer, sendNotification: boolean = false): void => connection.trace(value, tracer, sendNotification),
trace: (value: Trace, tracer: Tracer, sendNotification: boolean = false): Promise<void> => connection.trace(value, tracer, sendNotification),

initialize: (params: InitializeParams) => connection.sendRequest(InitializeRequest.type, params),
shutdown: () => connection.sendRequest(ShutdownRequest.type, undefined),
Expand Down
98 changes: 96 additions & 2 deletions packages/client/src/monaco-converter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
Command, CodeLens, FormattingOptions, TextEdit, WorkspaceEdit, DocumentLinkParams, DocumentLink,
MarkedString, MarkupContent, ColorInformation, ColorPresentation, FoldingRange, FoldingRangeKind,
DiagnosticRelatedInformation, MarkupKind, SymbolKind, DocumentSymbol, CodeAction, SignatureHelpContext, SignatureHelpTriggerKind,
SemanticTokens, InsertTextMode, AnnotatedTextEdit, ChangeAnnotation, CodeDescription
SemanticTokens, InsertTextMode, AnnotatedTextEdit, ChangeAnnotation, CodeDescription, InlayHint, InlayHintLabelPart
} from './services';

export type RecursivePartial<T> = {
Expand Down Expand Up @@ -76,6 +76,16 @@ export namespace ProtocolCodeAction {
}
}

export interface ProtocolInlayHint extends monaco.languages.InlayHint {
data?: unknown;
}
export namespace ProtocolInlayHint {
export function is(item: any): item is ProtocolInlayHint {
return !!item && 'data' in item;
}
}


type RangeReplace = { insert: monaco.IRange; replace: monaco.IRange }

function isRangeReplace(v: Partial<monaco.IRange> | RangeReplace): v is RangeReplace {
Expand Down Expand Up @@ -127,6 +137,21 @@ export class MonacoToProtocolConverter {
}
}

asLocation(item: monaco.languages.Location): Location;
asLocation(item: undefined | null): undefined;
asLocation(item: monaco.languages.Location | undefined | null): Location | undefined;
asLocation(item: monaco.languages.Location | undefined | null): Location | undefined {
if (!item) {
return undefined;
}
const uri = item.uri.toString();
const range = this.asRange(item.range);
return {
uri,
range
}
}

asTextDocumentIdentifier(model: monaco.editor.IReadOnlyModel): TextDocumentIdentifier {
return {
uri: model.uri.toString()
Expand Down Expand Up @@ -493,7 +518,7 @@ export class MonacoToProtocolConverter {
result.diagnostics = this.asDiagnostics(item.diagnostics);
}
if (item.edit) {
throw new Error (`VS Code code actions can only be converted to a protocol code action without an edit.`);
throw new Error(`VS Code code actions can only be converted to a protocol code action without an edit.`);
}
if (item.command) {
result.command = this.asCommand(item.command);
Expand All @@ -511,6 +536,34 @@ export class MonacoToProtocolConverter {
}
return result;
}

asInlayHintLabelPart(part: monaco.languages.InlayHintLabelPart): InlayHintLabelPart {
return {
value: part.label,
command: this.asCommand(part.command),
location: this.asLocation(part.location),
tooltip: this.asMarkupContent(part.tooltip)
}
}

asInlayHintLabel(label: string | monaco.languages.InlayHintLabelPart[]): string | InlayHintLabelPart[] {
if (Array.isArray(label)) {
return label.map(part => this.asInlayHintLabelPart(part))
}
return label
}

asInlayHint(item: monaco.languages.InlayHint): InlayHint {
let result = InlayHint.create(
this.asPosition(item.position.lineNumber, item.position.column),
this.asInlayHintLabel(item.label),
item.kind
);
if (ProtocolInlayHint.is(item)) {
if (item.data) { result.data = item.data };
}
return result;
}
}

export class ProtocolToMonacoConverter {
Expand Down Expand Up @@ -1235,4 +1288,45 @@ export class ProtocolToMonacoConverter {
}
}

asInlayHintLabelPart(part: InlayHintLabelPart): monaco.languages.InlayHintLabelPart {
return {
label: part.value,
command: this.asCommand(part.command),
location: this.asLocation(part.location),
tooltip: part.tooltip && this.asMarkdownString(part.tooltip)
}
}

asInlayHintLabel(label: string | InlayHintLabelPart[]): string | monaco.languages.InlayHintLabelPart[] {
if (Array.isArray(label)) {
return label.map(part => this.asInlayHintLabelPart(part))
}
return label
}

asInlayHint(inlayHint: InlayHint): ProtocolInlayHint {
return {
data: inlayHint.data,
label: this.asInlayHintLabel(inlayHint.label),
position: this.asPosition(inlayHint.position),
kind: inlayHint.kind,
paddingLeft: inlayHint.paddingLeft,
paddingRight: inlayHint.paddingRight,
tooltip: inlayHint.tooltip && this.asMarkdownString(inlayHint.tooltip)
}
}

asInlayHintList(items: InlayHint[]): monaco.languages.InlayHintList;
asInlayHintList(items: undefined | null): undefined;
asInlayHintList(items: InlayHint[] | undefined | null): monaco.languages.InlayHintList | undefined;
asInlayHintList(items: InlayHint[] | undefined | null): monaco.languages.InlayHintList | undefined {
if (!items) {
return undefined;
}
return {
hints: items.map((hint) => this.asInlayHint(hint)),
dispose: () => { }
};
}

}
56 changes: 6 additions & 50 deletions packages/client/src/monaco-language-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,25 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
* ------------------------------------------------------------------------------------------ */
import {
BaseLanguageClient, MessageTransports, LanguageClientOptions,
StaticFeature, DynamicFeature
} from "vscode-languageclient/lib/common/client";
import { TypeDefinitionFeature } from "vscode-languageclient/lib/common/typeDefinition";
import { ConfigurationFeature as PullConfigurationFeature } from "vscode-languageclient/lib/common/configuration";
import { ImplementationFeature } from "vscode-languageclient/lib/common/implementation";
import { ColorProviderFeature } from "vscode-languageclient/lib/common/colorProvider";
import { WorkspaceFoldersFeature } from "vscode-languageclient/lib/common/workspaceFolders";
import { FoldingRangeFeature } from "vscode-languageclient/lib/common/foldingRange";
import { CallHierarchyFeature } from "vscode-languageclient/lib/common/callHierarchy";
import { ProgressFeature } from "vscode-languageclient/lib/common/progress";
import { SemanticTokensFeature } from "vscode-languageclient/lib/common/semanticTokens";
BaseLanguageClient, MessageTransports, LanguageClientOptions
} from "vscode-languageclient";
import * as p2c from 'vscode-languageclient/lib/common/protocolConverter';
import * as c2p from 'vscode-languageclient/lib/common/codeConverter';
import { IConnectionProvider, IConnection } from './connection';
import { DeclarationFeature } from "vscode-languageclient/lib/common/declaration";
import { IConnectionProvider } from './connection';
import { CompletionParams, WillSaveTextDocumentParams } from './services'

export * from 'vscode-languageclient/lib/common/client';
import type * as vscode from 'vscode'

export class MonacoLanguageClient extends BaseLanguageClient {

static bypassConversion = (result: any) => result || undefined;
static bypassConversion = (result: any, token?: vscode.CancellationToken) => token != null ? Promise.resolve(result || undefined) : (result || undefined);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want to have a strict null check here, then change to !==?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure to understand what you mean, can you provide the code you want to be there?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't token != null betoken !== null?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can replace != null by !== undefined if you want.

I'm just used to using == null because it allows to check for undefined and null, and it's allowed by the standartjs eslint rules.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want me to change it?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a boolean expression, if token is defined, then it is true: token ? Promise.resolve(result || undefined) : (result || undefined);

I'm not sure to understand what you mean, token is a boolean?

Should we consider investigating the new issue that came up before. WDYT

Are you talking about #351? I don't think it's a new issue, but I'm currently investigating. There is probably no reason for it to block the next release tho

I mean a real new version 0.19.0 or shall we call it 1.0.0?

I never understand why so many libraries are using a version number < 1 even after years (even monaco!).
But it's not a big deal, as you wish!

@kaisalmen kaisalmen May 19, 2022

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure to understand what you mean, token is a boolean?

You can rewrite this as such (token is undefined => false or defined => true). That's what I meant:

static bypassConversion = (result: any, token?: vscode.CancellationToken) => {
    if (token) {
        return Promise.resolve(result || undefined);
    } else {
        return (result || undefined);
    }
};

The method signature is identical to:

static bypassConversion = (result: any, token?: vscode.CancellationToken) => token != null ? Promise.resolve(result || undefined) : (result || undefined);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's aim for 1.0.0. webpack example is almost working again. monaco workers are not working properly. Will fix this today. Release today or tomorrow.

@CGNonofr CGNonofr May 19, 2022

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can rewrite this as such (token is undefined => false or defined => true)

That's what I meant when talking about if on anything else than boolean, relying on the trythiness of an object and the falsiness of undefined seems wrong and dangerous, no big deal tho since the whole project is done this way

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please press the merge button if you like. 👍


protected readonly connectionProvider: IConnectionProvider;

constructor({ id, name, clientOptions, connectionProvider }: MonacoLanguageClient.Options) {
super(id || name.toLowerCase(), name, clientOptions);
this.connectionProvider = connectionProvider;
(this as any).createConnection = this.doCreateConnection.bind(this);

// bypass LSP <=> VS Code conversion
const self: {
Expand Down Expand Up @@ -77,41 +66,8 @@ export class MonacoLanguageClient extends BaseLanguageClient {
});
}

protected doCreateConnection(): Thenable<IConnection> {
const errorHandler = (this as any).handleConnectionError.bind(this);
const closeHandler = this.handleConnectionClosed.bind(this);
return this.connectionProvider.get(errorHandler, closeHandler, this.outputChannel);
}

protected createMessageTransports(encoding: string): Promise<MessageTransports> {
throw new Error('Unsupported');
}

protected registerBuiltinFeatures(): void {
super.registerBuiltinFeatures();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the base implementation now perform this registration and it is therefore no longer needed here?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I looked at vscode-languageclient\lib\common\client.js and this is done there. Was that an error to register it twicebefore or was that really an change in the lc lib?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really know, it seems there isn't any consequence of registering a feature twice, so I don't know for how long it was there.

I think these features were proposed back then, so we added them and forgot to remove it when they became builtin

this.registerFeature(new PullConfigurationFeature(this));
this.registerFeature(new TypeDefinitionFeature(this));
this.registerFeature(new ImplementationFeature(this));
this.registerFeature(new ColorProviderFeature(this));
this.registerFeature(new WorkspaceFoldersFeature(this));
FoldingRangeFeature['asFoldingRanges'] = MonacoLanguageClient.bypassConversion;
this.registerFeature(new FoldingRangeFeature(this));
this.registerFeature(new DeclarationFeature(this));
this.registerFeature(new SemanticTokensFeature(this));
this.registerFeature(new CallHierarchyFeature(this));
this.registerFeature(new ProgressFeature(this));

const features = this['_features'] as ((StaticFeature | DynamicFeature<any>)[]);
for (const feature of features) {
if (feature instanceof ColorProviderFeature) {
feature['asColor'] = MonacoLanguageClient.bypassConversion;
feature['asColorInformations'] = MonacoLanguageClient.bypassConversion;
feature['asColorPresentations'] = MonacoLanguageClient.bypassConversion;
}
}
}

public registerProposedFeatures() {
return this.connectionProvider.get(encoding);
}

protected getLocale(): string {
Expand Down
39 changes: 36 additions & 3 deletions packages/client/src/monaco-languages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
OnTypeFormattingEditProvider, RenameProvider,
DocumentFilter, DocumentSelector, DocumentLinkProvider, ImplementationProvider, TypeDefinitionProvider, DocumentColorProvider,
FoldingRangeProvider, SemanticTokensLegend,
DocumentSemanticTokensProvider, DocumentRangeSemanticTokensProvider
DocumentSemanticTokensProvider, DocumentRangeSemanticTokensProvider, TextDocumentFilter, InlayHintsProvider
} from "./services";

import { MonacoDiagnosticCollection } from './monaco-diagnostic-collection';
Expand Down Expand Up @@ -436,11 +436,41 @@ export class MonacoLanguages implements Languages {
}
}

registerInlayHintsProvider(selector: DocumentSelector, provider: InlayHintsProvider): Disposable {
const inlayHintsProvider = this.createInlayHintsProvider(provider);
return this._monaco.languages.registerInlayHintsProvider(selector, inlayHintsProvider);
}

protected createInlayHintsProvider(provider: InlayHintsProvider): monaco.languages.InlayHintsProvider {
return {
onDidChangeInlayHints: provider.onDidChangeInlayHints,
provideInlayHints: async (model, range, token) => {
const textDocument = this.m2p.asTextDocumentIdentifier(model);
const result = await provider.provideInlayHints({
textDocument,
range: this.m2p.asRange(range)
}, token);
return result && this.p2m.asInlayHintList(result)
},
resolveInlayHint: async (hint: monaco.languages.InlayHint, token) => {
if (provider.resolveInlayHint) {
const documentLink = this.m2p.asInlayHint(hint);
const result = await provider.resolveInlayHint(documentLink, token);
if (result) {
const resolvedInlayHint = this.p2m.asInlayHint(result);
Object.assign(hint, resolvedInlayHint);
}
}
return hint;
}
}
}

protected matchModel(selector: string | DocumentFilter | DocumentSelector, model: MonacoModelIdentifier): boolean {
if (Array.isArray(selector)) {
return selector.some(filter => this.matchModel(filter, model));
}
if (DocumentFilter.is(selector)) {
if (TextDocumentFilter.is(selector)) {
if (!!selector.language && selector.language !== model.languageId) {
return false;
}
Expand All @@ -451,8 +481,11 @@ export class MonacoLanguages implements Languages {
return false;
}
return true;
} else if (typeof selector === 'string') {
return selector === model.languageId;
} else {
return false
}
return selector === model.languageId;
}

}
Loading