Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add triggerCharacters support for services #76

Merged
merged 1 commit into from
Aug 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 31 additions & 7 deletions packages/ace-linters/src/language-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ export class LanguageProvider {
return new LanguageProvider(messageController, options);
}

private $registerSession = (session: Ace.EditSession, options?: ServiceOptions) => {
this.$sessionLanguageProviders[session["id"]] ??= new SessionLanguageProvider(session, this.$messageController, options);
private $registerSession = (session: Ace.EditSession, editor: Ace.Editor, options?: ServiceOptions) => {
this.$sessionLanguageProviders[session["id"]] ??= new SessionLanguageProvider(session, editor, this.$messageController, options);
}

private $getSessionLanguageProvider(session: Ace.EditSession): SessionLanguageProvider {
Expand All @@ -99,13 +99,13 @@ export class LanguageProvider {
registerEditor(editor: Ace.Editor) {
if (!this.editors.includes(editor))
this.$registerEditor(editor);
this.$registerSession(editor.session);
this.$registerSession(editor.session, editor);
}

$registerEditor(editor: Ace.Editor) {
this.editors.push(editor);
editor.setOption("useWorker", false);
editor.on("changeSession", ({session}) => this.$registerSession(session));
editor.on("changeSession", ({session}) => this.$registerSession(session, editor));
if (this.options.functionality.completion) {
this.$registerCompleters(editor);
}
Expand Down Expand Up @@ -257,15 +257,18 @@ class SessionLanguageProvider {
private $isConnected = false;
private $modeIsChanged = false;
private $options: ServiceOptions;
private $servicesCapabilities: lsp.ServerCapabilities[];

private extensions = {
"typescript": "ts",
"javascript": "js"
}
editor: Ace.Editor;

constructor(session: Ace.EditSession, messageController: IMessageController, options?: ServiceOptions) {
constructor(session: Ace.EditSession, editor: Ace.Editor, messageController: IMessageController, options?: ServiceOptions) {
this.$messageController = messageController;
this.session = session;
this.editor = editor;
this.initFileName();

session.doc["version"] = 0;
Expand All @@ -277,8 +280,9 @@ class SessionLanguageProvider {
this.$messageController.init(this.fileName, session.doc, this.$mode, options, this.$connected, this.$showAnnotations);
}

private $connected = () => {
private $connected = (capabilities: lsp.ServerCapabilities[]) => {
this.$isConnected = true;
this.setServerCapabilities(capabilities);
if (this.$modeIsChanged)
this.$changeMode();
if (this.$deltaQueue)
Expand All @@ -293,8 +297,28 @@ class SessionLanguageProvider {
return;
}
this.$deltaQueue = [];
this.$messageController.changeMode(this.fileName, this.session.getValue(), this.$mode);
this.$messageController.changeMode(this.fileName, this.session.getValue(), this.$mode, this.setServerCapabilities);
};

private setServerCapabilities(capabilities: lsp.ServerCapabilities[]) {
//TODO: this need to take into account all capabilities from all services
this.$servicesCapabilities = capabilities;
if (capabilities.some((capability) => capability?.completionProvider?.triggerCharacters)) {
let completer = this.editor.completers.find((completer) => completer.id === "lspCompleters");
if (completer) {
let allTriggerCharacters = capabilities.reduce((acc, capability) => {
if (capability.completionProvider?.triggerCharacters) {
return [...acc, ...capability.completionProvider.triggerCharacters];
}
return acc;
}, []);

allTriggerCharacters = [...new Set(allTriggerCharacters)];

completer.triggerCharacters = allTriggerCharacters;
}
}
}

private initFileName() {
this.fileName = this.session["id"] + "." + this.$extension;
Expand Down
4 changes: 2 additions & 2 deletions packages/ace-linters/src/message-controller-ws.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ export class MessageControllerWS extends events.EventEmitter implements IMessage
});
}

init(sessionId: string, document: Ace.Document, mode: string, options: any, initCallback: () => void, validationCallback: (annotations: lsp.Diagnostic[]) => void) {
init(sessionId: string, document: Ace.Document, mode: string, options: any, initCallback: (capabilities) => void, validationCallback: (annotations: lsp.Diagnostic[]) => void) {
this["on"]("validate-" + sessionId, validationCallback);

const textDocumentMessage: lsp.DidOpenTextDocumentParams = {
Expand Down Expand Up @@ -247,7 +247,7 @@ export class MessageControllerWS extends events.EventEmitter implements IMessage
});
}

changeMode(sessionId: string, value: string, mode: string, callback?: () => void): void {
changeMode(sessionId: string, value: string, mode: string, callback?: (capabilities) => void): void {
}

changeOptions(sessionId: string, options: any, callback?: () => void): void {//TODO:
Expand Down
4 changes: 2 additions & 2 deletions packages/ace-linters/src/message-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export class MessageController extends EventEmitter implements IMessageControlle

}

init(sessionId: string, document: Ace.Document, mode: string, options: any, initCallback: () => void, validationCallback: (annotations: lsp.Diagnostic[]) => void): void {
init(sessionId: string, document: Ace.Document, mode: string, options: any, initCallback: (capabilities) => void, validationCallback: (annotations: lsp.Diagnostic[]) => void): void {
this.on(MessageType.validate.toString() + "-" + sessionId, validationCallback);

this.postMessage(new InitMessage(sessionId, document.getValue(), document["version"], mode, options), initCallback);
Expand Down Expand Up @@ -68,7 +68,7 @@ export class MessageController extends EventEmitter implements IMessageControlle
this.postMessage(message, callback)
}

changeMode(sessionId: string, value: string, mode: string, callback?: () => void) {
changeMode(sessionId: string, value: string, mode: string, callback?: (capabilities) => void) {
this.postMessage(new ChangeModeMessage(sessionId, value, mode), callback);
}

Expand Down
1 change: 1 addition & 0 deletions packages/ace-linters/src/services/base-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export abstract class BaseService<OptionsType extends ServiceOptions = ServiceOp
options: { [sessionID: string]: OptionsType } = {};
globalOptions: OptionsType = {} as OptionsType;
serviceData: ServiceData;
serviceCapabilities: lsp.ServerCapabilities = {};

protected constructor(mode: string) {
this.mode = mode;
Expand Down
5 changes: 5 additions & 0 deletions packages/ace-linters/src/services/css/css-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ export class CssService extends BaseService implements LanguageService {
spaceAroundSelectorSeparator: false,
braceStyle: "collapse"
}
serviceCapabilities = {
completionProvider: {
triggerCharacters: [":", " ", "-", "/"]
}
}

constructor(mode: string) {
super(mode);
Expand Down
6 changes: 6 additions & 0 deletions packages/ace-linters/src/services/html/html-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ export class HtmlService extends BaseService<HtmlServiceOptions> implements Lang
wrapAttributesIndentSize: 120
}

serviceCapabilities = {
completionProvider: {
triggerCharacters: ['.', ':', '<', '"', '=', '/']
}
}

constructor(mode: string) {
super(mode);
this.$service = htmlService.getLanguageService();
Expand Down
6 changes: 6 additions & 0 deletions packages/ace-linters/src/services/json/json-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ export class JsonService extends BaseService<JsonServiceOptions> implements Lang
$service: VSLanguageService;
schemas: { [schemaUri: string]: string } = {};

serviceCapabilities = {
completionProvider: {
triggerCharacters: ['"', ':']
}
}

constructor(mode: string) {
super(mode);
this.$service = jsonService.getLanguageService({
Expand Down
21 changes: 14 additions & 7 deletions packages/ace-linters/src/services/service-manager.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import {mergeObjects, notEmpty} from "../utils";
import {MessageType} from "../message-types";
import {TextDocumentIdentifier, VersionedTextDocumentIdentifier} from "vscode-languageserver-protocol";
import {LanguageService, ServiceData, ServiceFeatures, ServiceOptions, SupportedFeatures} from "../types/language-service";
import {
LanguageService,
ServiceData,
ServiceFeatures,
ServiceOptions,
SupportedFeatures
} from "../types/language-service";

type Validation = {
(document: TextDocumentIdentifier, servicesInstances?: LanguageService[]): Promise<void>;
Expand All @@ -25,7 +31,7 @@ export class ServiceManager {
let postMessage = {
"type": MessageType.validate,
};

for (let sessionID of sessionIDList) {
let diagnostics = await Promise.all(servicesInstances.map((el) => {
return el.doValidation({uri: sessionID});
Expand All @@ -35,13 +41,13 @@ export class ServiceManager {
ctx.postMessage(postMessage);
}
}

let provideValidationForServiceInstance = async (serviceName) => {
var serviceInstance = this.$services[serviceName].serviceInstance;
if (serviceInstance)
await doValidation(undefined, [serviceInstance]);
}

ctx.addEventListener("message", async (ev) => {
let message = ev.data;
let sessionID = message.sessionId ?? "";
Expand Down Expand Up @@ -101,11 +107,11 @@ export class ServiceManager {
postMessage["value"] = await doValidation(documentIdentifier, serviceInstances);
break;
case MessageType.init: //this should be first message
await this.addDocument(documentIdentifier, message.value, message.mode, message.options);
postMessage["value"] = (await this.addDocument(documentIdentifier, message.value, message.mode, message.options))?.map((el) => el.serviceCapabilities);
await doValidation(documentIdentifier);
break;
case MessageType.changeMode:
await this.changeDocumentMode(documentIdentifier, message.value, message.mode, message.options);
postMessage["value"] = (await this.changeDocumentMode(documentIdentifier, message.value, message.mode, message.options))?.map((el) => el.serviceCapabilities);
await doValidation(documentIdentifier, serviceInstances);
break;
case MessageType.changeOptions:
Expand Down Expand Up @@ -191,11 +197,12 @@ export class ServiceManager {
}
serviceInstances.forEach((el) => el.addDocument(documentItem));
this.$sessionIDToMode[documentIdentifier.uri] = mode;
return serviceInstances;
}

async changeDocumentMode(documentIdentifier: VersionedTextDocumentIdentifier, value: string, mode: string, options: ServiceOptions) {
this.removeDocument(documentIdentifier);
await this.addDocument(documentIdentifier, value, mode, options);
return await this.addDocument(documentIdentifier, value, mode, options);
}

removeDocument(document: TextDocumentIdentifier) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ export class TypescriptService extends BaseService<TsServiceOptions> implements
convertTabsToSpaces: true,
}

serviceCapabilities = {
completionProvider: {
triggerCharacters: ['.', '"', '\'', '`', '/', '@', '<', '#', ' ']
}
}

constructor(mode: string) {
super(mode);
this.$service = ts.createLanguageService(this);
Expand Down
1 change: 1 addition & 0 deletions packages/ace-linters/src/types/language-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface LanguageService {
mode: string;
globalOptions;
serviceData: ServiceData;
serviceCapabilities: lsp.ServerCapabilities;

format(document: lsp.TextDocumentIdentifier, range: lsp.Range, options: lsp.FormattingOptions): lsp.TextEdit[];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as lsp from "vscode-languageserver-protocol";
import {CompletionService, ServiceFeatures, ServiceOptions, SupportedServices} from "./language-service";

export interface IMessageController {
init(sessionId: string, document: Ace.Document, mode: string, options: any, initCallback: () => void, validationCallback: (annotations: lsp.Diagnostic[]) => void): void;
init(sessionId: string, document: Ace.Document, mode: string, options: any, initCallback: (capabilities: lsp.ServerCapabilities[]) => void, validationCallback: (annotations: lsp.Diagnostic[]) => void): void;

doValidation(sessionId: string, callback?: (annotations: lsp.Diagnostic[]) => void)

Expand All @@ -17,7 +17,7 @@ export interface IMessageController {

change(sessionId: string, deltas: lsp.TextDocumentContentChangeEvent[], document: Ace.Document, callback?: () => void): void;

changeMode(sessionId: string, value: string, mode: string, callback?: () => void);
changeMode(sessionId: string, value: string, mode: string, callback?: (capabilities: lsp.ServerCapabilities[]) => void);

changeOptions(sessionId: string, options: ServiceOptions, callback?: () => void);

Expand Down
5 changes: 5 additions & 0 deletions packages/ace-linters/tests/unit/language-provider.tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ describe('LanguageProvider tests', () => {
expect(hoverText).to.equal("Plain text");
})

it('completer should have html trigger characters', () => {
let completer = languageProvider.activeEditor.completers.find((item) => item.id === "lspCompleters");
expect(completer.triggerCharacters).to.eql(['.', ':', '<', '"', '=', '/']);
});

it('do hover or not, depending on service feature state', (done) => {
languageProvider.doHover(editor.session, {row: 2, column: 2}, hover => {
let hoverText = languageProvider.getTooltipText(hover);
Expand Down
4 changes: 2 additions & 2 deletions packages/ace-linters/types/message-controller-ws.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ export declare class MessageControllerWS extends events.EventEmitter implements
private $connectSocket;
private $connectWorker;
private $connect;
init(sessionId: string, document: Ace.Document, mode: string, options: any, initCallback: () => void, validationCallback: (annotations: lsp.Diagnostic[]) => void): void;
init(sessionId: string, document: Ace.Document, mode: string, options: any, initCallback: (capabilities: any) => void, validationCallback: (annotations: lsp.Diagnostic[]) => void): void;
initSession(textDocumentMessage: lsp.DidOpenTextDocumentParams, initCallback: any): void;
close(): void;
sendInitialize(): void;
change(sessionId: string, deltas: lsp.TextDocumentContentChangeEvent[], document: any, callback?: () => void): void;
doHover(sessionId: string, position: lsp.Position, callback?: (hover: lsp.Hover[]) => void): void;
doComplete(sessionId: string, position: lsp.Position, callback?: (completions: CompletionService[]) => void): void;
doResolve(sessionId: string, completion: lsp.CompletionItem, callback?: (completion: lsp.CompletionItem | null) => void): void;
changeMode(sessionId: string, value: string, mode: string, callback?: () => void): void;
changeMode(sessionId: string, value: string, mode: string, callback?: (capabilities: any) => void): void;
changeOptions(sessionId: string, options: any, callback?: () => void): void;
dispose(sessionId: string, callback?: () => void): void;
doValidation(sessionId: string, callback?: (annotations: lsp.Diagnostic[]) => void): void;
Expand Down
4 changes: 2 additions & 2 deletions packages/ace-linters/types/message-controller.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ import EventEmitter from "events";
export declare class MessageController extends EventEmitter implements IMessageController {
private $worker;
constructor(worker: Worker);
init(sessionId: string, document: Ace.Document, mode: string, options: any, initCallback: () => void, validationCallback: (annotations: lsp.Diagnostic[]) => void): void;
init(sessionId: string, document: Ace.Document, mode: string, options: any, initCallback: (capabilities: any) => void, validationCallback: (annotations: lsp.Diagnostic[]) => void): void;
doValidation(sessionId: string, callback?: (annotations: lsp.Diagnostic[]) => void): void;
doComplete(sessionId: string, position: lsp.Position, callback?: (completions: CompletionService[]) => void): void;
doResolve(sessionId: string, completion: lsp.CompletionItem, callback?: (completion: lsp.CompletionItem | null) => void): void;
format(sessionId: string, range: lsp.Range, format: lsp.FormattingOptions, callback?: (edits: lsp.TextEdit[]) => void): void;
doHover(sessionId: string, position: lsp.Position, callback?: (hover: lsp.Hover[]) => void): void;
change(sessionId: string, deltas: any, document: Ace.Document, callback?: () => void): void;
changeMode(sessionId: string, value: string, mode: string, callback?: () => void): void;
changeMode(sessionId: string, value: string, mode: string, callback?: (capabilities: any) => void): void;
changeOptions(sessionId: string, options: ServiceOptions, callback?: () => void, merge?: boolean): void;
dispose(sessionId: string, callback?: () => void): void;
setGlobalOptions<T extends keyof ServiceOptionsMap>(serviceName: T, options: ServiceOptionsMap[T], merge?: boolean): void;
Expand Down
1 change: 1 addition & 0 deletions packages/ace-linters/types/services/base-service.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export declare abstract class BaseService<OptionsType extends ServiceOptions = S
};
globalOptions: OptionsType;
serviceData: ServiceData;
serviceCapabilities: lsp.ServerCapabilities;
protected constructor(mode: string);
addDocument(document: lsp.TextDocumentItem): void;
getDocument(uri: string): TextDocument;
Expand Down
5 changes: 5 additions & 0 deletions packages/ace-linters/types/services/css/css-service.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ export declare class CssService extends BaseService implements LanguageService {
$service: VSLanguageService;
$languageId: string;
$defaultFormatOptions: CSSFormatConfiguration;
serviceCapabilities: {
completionProvider: {
triggerCharacters: string[];
};
};
constructor(mode: string);
private $initLanguageService;
getFormattingOptions(options: CSSFormatConfiguration): CSSFormatConfiguration;
Expand Down
5 changes: 5 additions & 0 deletions packages/ace-linters/types/services/html/html-service.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ export declare class HtmlService extends BaseService<HtmlServiceOptions> impleme
"tag-pair": boolean;
};
$defaultFormatOptions: HTMLFormatConfiguration;
serviceCapabilities: {
completionProvider: {
triggerCharacters: string[];
};
};
constructor(mode: string);
getFormattingOptions(options: HTMLFormatConfiguration): HTMLFormatConfiguration;
format(document: lsp.TextDocumentIdentifier, range: lsp.Range, options: HTMLFormatConfiguration): lsp.TextEdit[];
Expand Down
5 changes: 5 additions & 0 deletions packages/ace-linters/types/services/json/json-service.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ export declare class JsonService extends BaseService<JsonServiceOptions> impleme
schemas: {
[schemaUri: string]: string;
};
serviceCapabilities: {
completionProvider: {
triggerCharacters: string[];
};
};
constructor(mode: string);
private $getJsonSchemaUri;
addDocument(document: TextDocumentItem): void;
Expand Down
4 changes: 2 additions & 2 deletions packages/ace-linters/types/services/service-manager.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ export declare class ServiceManager {
private static $initServiceInstance;
private $getServicesInstancesByMode;
setGlobalOptions(serviceName: string, options: ServiceOptions, merge?: boolean): void;
addDocument(documentIdentifier: VersionedTextDocumentIdentifier, documentValue: string, mode: string, options?: ServiceOptions): Promise<void>;
changeDocumentMode(documentIdentifier: VersionedTextDocumentIdentifier, value: string, mode: string, options: ServiceOptions): Promise<void>;
addDocument(documentIdentifier: VersionedTextDocumentIdentifier, documentValue: string, mode: string, options?: ServiceOptions): Promise<LanguageService[] | undefined>;
changeDocumentMode(documentIdentifier: VersionedTextDocumentIdentifier, value: string, mode: string, options: ServiceOptions): Promise<LanguageService[] | undefined>;
removeDocument(document: TextDocumentIdentifier): void;
getServicesInstances(sessionID: string): LanguageService[];
filterByFeature(serviceInstances: LanguageService[], feature: SupportedFeatures): LanguageService[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ export declare class TypescriptService extends BaseService<TsServiceOptions> imp
newLineCharacter: string;
convertTabsToSpaces: boolean;
};
serviceCapabilities: {
completionProvider: {
triggerCharacters: string[];
};
};
constructor(mode: string);
getCompilationSettings(): ts.CompilerOptions;
getScriptFileNames(): string[];
Expand Down
Loading