Skip to content

Commit

Permalink
feat(language-service): supports auto insertion of multiple cursors
Browse files Browse the repository at this point in the history
close #4140
  • Loading branch information
johnsoncodehk committed Apr 2, 2024
1 parent 8f04760 commit 5e521f2
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 37 deletions.
37 changes: 16 additions & 21 deletions packages/language-service/lib/plugins/vue-autoinsert-dotvalue.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { ServiceContext, LanguageServicePlugin, LanguageServicePluginInstance } from '@volar/language-service';
import { hyphenateAttr } from '@vue/language-core';
import type * as ts from 'typescript';
import type * as vscode from 'vscode-languageserver-protocol';
import type { TextDocument } from 'vscode-languageserver-textdocument';

const asts = new WeakMap<ts.IScriptSnapshot, ts.SourceFile>();
Expand All @@ -25,13 +24,17 @@ export function create(
const tsPluginClient = getTsPluginClient?.(context);
let currentReq = 0;
return {
async provideAutoInsertionEdit(document, position, lastChange) {
async provideAutoInsertionEdit(document, selection, change) {
// selection must at end of change
if (document.offsetAt(selection) !== change.rangeOffset + change.text.length) {
return;
}

if (!isTsDocument(document)) {
return;
}

if (!isCharacterTyping(document, lastChange)) {
if (!isCharacterTyping(document, change)) {
return;
}

Expand All @@ -55,7 +58,7 @@ export function create(
}

let ast: ts.SourceFile | undefined;
let sourceCodeOffset = document.offsetAt(position);
let sourceCodeOffset = document.offsetAt(selection);

const fileName = context.env.typescript!.uriToFileName(sourceScript.id);

Expand All @@ -67,7 +70,7 @@ export function create(
ast = getAst(ts, fileName, virtualCode.snapshot, serviceScript.scriptKind);
let mapped = false;
for (const [_1, [_2, map]] of context.language.maps.forEach(virtualCode)) {
const sourceOffset = map.getSourceOffset(document.offsetAt(position));
const sourceOffset = map.getSourceOffset(document.offsetAt(selection));
if (sourceOffset !== undefined) {
sourceCodeOffset = sourceOffset[0];
mapped = true;
Expand All @@ -82,7 +85,7 @@ export function create(
ast = getAst(ts, fileName, sourceScript.snapshot);
}

if (isBlacklistNode(ts, ast, document.offsetAt(position), false)) {
if (isBlacklistNode(ts, ast, document.offsetAt(selection), false)) {
return;
}

Expand All @@ -109,26 +112,18 @@ function isTsDocument(document: TextDocument) {

const charReg = /\w/;

export function isCharacterTyping(document: TextDocument, lastChange: { range: vscode.Range; text: string; }) {

const lastCharacter = lastChange.text[lastChange.text.length - 1];
const rangeStart = lastChange.range.start;
const position: vscode.Position = {
line: rangeStart.line,
character: rangeStart.character + lastChange.text.length,
};
const nextCharacter = document.getText({
start: position,
end: { line: position.line, character: position.character + 1 },
});

export function isCharacterTyping(document: TextDocument, change: { text: string; rangeOffset: number; rangeLength: number; }) {
const lastCharacter = change.text[change.text.length - 1];
const nextCharacter = document.getText().substring(
change.rangeOffset + change.text.length,
change.rangeOffset + change.text.length + 1,
);
if (lastCharacter === undefined) { // delete text
return false;
}
if (lastChange.text.indexOf('\n') >= 0) { // multi-line change
if (change.text.indexOf('\n') >= 0) { // multi-line change
return false;
}

return charReg.test(lastCharacter) && !charReg.test(nextCharacter);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@ export function create(ts: typeof import('typescript')): LanguageServicePlugin {
name: 'vue-autoinsert-parentheses',
create(context): LanguageServicePluginInstance {
return {
async provideAutoInsertionEdit(document, position, lastChange) {
async provideAutoInsertionEdit(document, selection, change) {
// selection must at end of change
if (document.offsetAt(selection) !== change.rangeOffset + change.text.length) {
return;
}

const enabled = await context.env.getConfiguration?.<boolean>('vue.autoInsert.parentheses') ?? false;
if (!enabled) {
return;
}

if (!isCharacterTyping(document, lastChange)) {
if (!isCharacterTyping(document, change)) {
return;
}

Expand All @@ -24,7 +28,7 @@ export function create(ts: typeof import('typescript')): LanguageServicePlugin {
return;
}

const offset = document.offsetAt(position);
const offset = document.offsetAt(selection);

for (const mappedRange of virtualCode.mappings) {
const generatedCodeEnd = mappedRange.generatedOffsets[mappedRange.generatedOffsets.length - 1]
Expand Down
18 changes: 5 additions & 13 deletions packages/language-service/lib/plugins/vue-autoinsert-space.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export function create(): LanguageServicePlugin {
name: 'vue-autoinsert-space',
create(context): LanguageServicePluginInstance {
return {
async provideAutoInsertionEdit(document, _, lastChange) {
async provideAutoInsertionEdit(document, selection, change) {

if (document.languageId === 'html' || document.languageId === 'jade') {

Expand All @@ -15,19 +15,11 @@ export function create(): LanguageServicePlugin {
}

if (
lastChange.text === '{}'
&& document.getText({
start: { line: lastChange.range.start.line, character: lastChange.range.start.character - 1 },
end: { line: lastChange.range.start.line, character: lastChange.range.start.character + 3 }
}) === '{{}}'
change.text === '{}'
&& document.getText().substring(change.rangeOffset - 1, change.rangeOffset + 3) === '{{}}'
&& document.offsetAt(selection) === change.rangeOffset + 1
) {
return {
newText: ` $0 `,
range: {
start: { line: lastChange.range.start.line, character: lastChange.range.start.character + 1 },
end: { line: lastChange.range.start.line, character: lastChange.range.start.character + 1 }
},
};
return ` $0 `;
}
}
},
Expand Down

0 comments on commit 5e521f2

Please sign in to comment.