Skip to content

Commit

Permalink
Improve Qute self-closing tags support
Browse files Browse the repository at this point in the history
Fixes redhat-developer#596

Signed-off-by: azerr <[email protected]>
  • Loading branch information
angelozerr committed Jul 21, 2023
1 parent 9dd744c commit e7242f2
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 2 deletions.
80 changes: 80 additions & 0 deletions src/qute/languageServer/autoInsertion.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { window, workspace, Disposable, TextDocument, Position, SnippetString, TextDocumentChangeEvent, TextDocumentChangeReason, TextDocumentContentChangeEvent } from 'vscode';

export function activateAutoInsertion(provider: (kind: 'autoQuote' | 'autoClose', document: TextDocument, position: Position) => Thenable<string>): Disposable {
const disposables: Disposable[] = [];
workspace.onDidChangeTextDocument(onDidChangeTextDocument, null, disposables);

let anyIsEnabled = false;
const isEnabled = {
'autoQuote': false,
'autoClose': false
};
updateEnabledState();
window.onDidChangeActiveTextEditor(updateEnabledState, null, disposables);

let timeout: NodeJS.Timer | undefined = undefined;

function updateEnabledState() {
anyIsEnabled = false;
const editor = window.activeTextEditor;
if (!editor) {
return;
}
const document = editor.document;
const configurations = workspace.getConfiguration(undefined, document.uri);
isEnabled['autoQuote'] = configurations.get<boolean>('qute.autoCreateQuotes') ?? false;
isEnabled['autoClose'] = configurations.get<boolean>('qute.autoClosingTags') ?? false;
anyIsEnabled = isEnabled['autoQuote'] || isEnabled['autoClose'];
}

function onDidChangeTextDocument({ document, contentChanges, reason }: TextDocumentChangeEvent) {
if (!anyIsEnabled || contentChanges.length === 0 || reason === TextDocumentChangeReason.Undo || reason === TextDocumentChangeReason.Redo) {
return;
}
const activeDocument = window.activeTextEditor && window.activeTextEditor.document;
if (document !== activeDocument) {
return;
}
if (typeof timeout !== 'undefined') {
clearTimeout(timeout);
}
const lastChange = contentChanges[contentChanges.length - 1];
const lastCharacter = lastChange.text[lastChange.text.length - 1];
if (isEnabled['autoQuote'] && lastChange.rangeLength === 0 && lastCharacter === '=') {
doAutoInsert('autoQuote', document, lastChange);
} else if (isEnabled['autoClose'] && lastChange.rangeLength === 0 && (lastCharacter === '}' || lastCharacter === '/')) {
doAutoInsert('autoClose', document, lastChange);
}
}

function doAutoInsert(kind: 'autoQuote' | 'autoClose', document: TextDocument, lastChange: TextDocumentContentChangeEvent) {
const rangeStart = lastChange.range.start;
const version = document.version;
timeout = setTimeout(() => {
const position = new Position(rangeStart.line, rangeStart.character + lastChange.text.length);
provider(kind, document, position).then(text => {
if (text && isEnabled[kind]) {
const activeEditor = window.activeTextEditor;
if (activeEditor) {
const activeDocument = activeEditor.document;
if (document === activeDocument && activeDocument.version === version) {
const selections = activeEditor.selections;
if (selections.length && selections.some(s => s.active.isEqual(position))) {
activeEditor.insertSnippet(new SnippetString(text), selections.map(s => s.active));
} else {
activeEditor.insertSnippet(new SnippetString(text), position);
}
}
}
}
});
timeout = undefined;
}, 100);
}
return Disposable.from(...disposables);
}
45 changes: 43 additions & 2 deletions src/qute/languageServer/client.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import { commands, ConfigurationTarget, ExtensionContext, window, workspace } from 'vscode';
import { DidChangeConfigurationNotification, LanguageClientOptions } from 'vscode-languageclient';
import { commands, ConfigurationTarget, Disposable, ExtensionContext, Position, TextDocument, window, workspace } from 'vscode';
import { DidChangeConfigurationNotification, LanguageClientOptions, RequestType, TextDocumentIdentifier, Position as LspPosition } from 'vscode-languageclient';
import { LanguageClient } from 'vscode-languageclient/node';
import { JavaExtensionAPI } from '../../extension';
import { QuteClientCommandConstants } from '../commands/commandConstants';
import { registerQuteLSDependentCommands, registerVSCodeQuteCommands, synchronizeQuteValidationButton } from '../commands/registerCommands';
import { activateAutoInsertion } from './autoInsertion';
import { prepareExecutable } from './quteServerStarter';
import { resolveRequirements } from './requirements';
import { QuteSettings } from './settings';

export interface Runtime {
TextDecoder: { new(encoding?: string): { decode(buffer: ArrayBuffer): string } };
readonly timer: {
setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): Disposable;
};
}

export async function connectToQuteLS(context: ExtensionContext, api: JavaExtensionAPI): Promise<LanguageClient> {
registerVSCodeQuteCommands(context);
Expand Down Expand Up @@ -90,6 +97,8 @@ export async function connectToQuteLS(context: ExtensionContext, api: JavaExtens
bindQuteRequest('qute/java/documentLink');
bindQuteNotification('qute/dataModelChanged');

context.subscriptions.push(registerAutoInsertionrequest(quteLanguageClient));

registerQuteLSDependentCommands(context, quteLanguageClient);
// Refresh the Qute context when editor tab has the focus
context.subscriptions.push(
Expand Down Expand Up @@ -204,3 +213,35 @@ async function showQuteValidationPopUp(context: ExtensionContext) {
export async function setQuteValidationEnabledContext() {
await commands.executeCommand('setContext', 'editorQuteValidationEnabled', workspace.getConfiguration().get(QuteSettings.QUTE_VALIDATION_ENABLED));
}

interface AutoInsertParams {
/**
* The auto insert kind
*/
kind: 'autoQuote' | 'autoClose';
/**
* The text document.
*/
textDocument: TextDocumentIdentifier;
/**
* The position inside the text document.
*/
position: LspPosition;
}

namespace AutoInsertRequest {
export const type: RequestType<AutoInsertParams, string, any> = new RequestType('qute/autoInsert');
}

function registerAutoInsertionrequest(client : LanguageClient) : Disposable{
const insertRequestor = (kind: 'autoQuote' | 'autoClose', document: TextDocument, position: Position): Promise<string> => {
const param: AutoInsertParams = {
kind,
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document),
position: client.code2ProtocolConverter.asPosition(position)
};
return client.sendRequest(AutoInsertRequest.type, param);
};

return activateAutoInsertion(insertRequestor);
}

0 comments on commit e7242f2

Please sign in to comment.