Skip to content

Commit

Permalink
refactor for volarjs/volar.js#100
Browse files Browse the repository at this point in the history
  • Loading branch information
johnsoncodehk committed Dec 4, 2023
1 parent 9ad0855 commit 7b9e601
Show file tree
Hide file tree
Showing 20 changed files with 2,172 additions and 2,198 deletions.
487 changes: 241 additions & 246 deletions packages/css/src/index.ts

Large diffs are not rendered by default.

10 changes: 7 additions & 3 deletions packages/emmet/src/empty.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import type { Service } from '@volar/language-service';
import type { ServicePlugin } from '@volar/language-service';

console.warn('volar-service-emmet: this module is not yet supported for web.');

export function create(): Service {
return () => ({});
export function create(): ServicePlugin {
return {
create() {
return {};
},
};
}

export default create;
131 changes: 63 additions & 68 deletions packages/emmet/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,84 +1,79 @@
import type { Service } from '@volar/language-service';
import type { ServicePlugin } from '@volar/language-service';
import * as emmet from '@vscode/emmet-helper';
import { getHtmlDocument } from 'volar-service-html';

// https://docs.emmet.io/abbreviations/syntax/
const triggerCharacters = '>+^*()#.[]$@-{}'.split('');
export function create(): ServicePlugin {
return {
// https://docs.emmet.io/abbreviations/syntax/
triggerCharacters: '>+^*()#.[]$@-{}'.split(''),
create(context) {

export function create(): Service {
return (context): ReturnType<Service> => {

if (!context) {
return { triggerCharacters };
}

return {

triggerCharacters,

isAdditionalCompletion: true,

async provideCompletionItems(textDocument, position) {

const syntax = emmet.getEmmetMode(textDocument.languageId === 'vue' ? 'html' : textDocument.languageId);
if (!syntax)
return;
return {

// fix https://github.com/vuejs/language-tools/issues/1329
if (syntax === 'html') {
const htmlDocument = getHtmlDocument(textDocument);
const node = htmlDocument.findNodeAt(textDocument.offsetAt(position));
if (node.tag) {
let insideBlock = false;
if (node.startTagEnd !== undefined && node.endTagStart !== undefined) {
insideBlock = textDocument.offsetAt(position) >= node.startTagEnd && textDocument.offsetAt(position) <= node.endTagStart;
}
if (!insideBlock) {
return;
isAdditionalCompletion: true,

async provideCompletionItems(textDocument, position) {

const syntax = emmet.getEmmetMode(textDocument.languageId === 'vue' ? 'html' : textDocument.languageId);
if (!syntax)
return;

// fix https://github.com/vuejs/language-tools/issues/1329
if (syntax === 'html') {
const htmlDocument = getHtmlDocument(textDocument);
const node = htmlDocument.findNodeAt(textDocument.offsetAt(position));
if (node.tag) {
let insideBlock = false;
if (node.startTagEnd !== undefined && node.endTagStart !== undefined) {
insideBlock = textDocument.offsetAt(position) >= node.startTagEnd && textDocument.offsetAt(position) <= node.endTagStart;
}
if (!insideBlock) {
return;
}
}
}
}

// monkey fix https://github.com/johnsoncodehk/volar/issues/1105
if (syntax === 'jsx')
return;
// monkey fix https://github.com/johnsoncodehk/volar/issues/1105
if (syntax === 'jsx')
return;

const emmetConfig = await getEmmetConfig(syntax);
const emmetConfig = await getEmmetConfig(syntax);

return emmet.doComplete(textDocument, position, syntax, emmetConfig);
},
};

async function getEmmetConfig(syntax: string): Promise<emmet.VSCodeEmmetConfig> {

const emmetConfig: any = await context?.env.getConfiguration?.<emmet.VSCodeEmmetConfig>('emmet') ?? {};
const syntaxProfiles = Object.assign({}, emmetConfig['syntaxProfiles'] || {});
const preferences = Object.assign({}, emmetConfig['preferences'] || {});
return emmet.doComplete(textDocument, position, syntax, emmetConfig);
},
};

// jsx, xml and xsl syntaxes need to have self closing tags unless otherwise configured by user
if (syntax === 'jsx' || syntax === 'xml' || syntax === 'xsl') {
syntaxProfiles[syntax] = syntaxProfiles[syntax] || {};
if (typeof syntaxProfiles[syntax] === 'object'
&& !syntaxProfiles[syntax].hasOwnProperty('self_closing_tag') // Old Emmet format
&& !syntaxProfiles[syntax].hasOwnProperty('selfClosingStyle') // Emmet 2.0 format
) {
syntaxProfiles[syntax] = {
...syntaxProfiles[syntax],
selfClosingStyle: 'xml'
};
async function getEmmetConfig(syntax: string): Promise<emmet.VSCodeEmmetConfig> {

const emmetConfig: any = await context.env.getConfiguration?.<emmet.VSCodeEmmetConfig>('emmet') ?? {};
const syntaxProfiles = Object.assign({}, emmetConfig['syntaxProfiles'] || {});
const preferences = Object.assign({}, emmetConfig['preferences'] || {});

// jsx, xml and xsl syntaxes need to have self closing tags unless otherwise configured by user
if (syntax === 'jsx' || syntax === 'xml' || syntax === 'xsl') {
syntaxProfiles[syntax] = syntaxProfiles[syntax] || {};
if (typeof syntaxProfiles[syntax] === 'object'
&& !syntaxProfiles[syntax].hasOwnProperty('self_closing_tag') // Old Emmet format
&& !syntaxProfiles[syntax].hasOwnProperty('selfClosingStyle') // Emmet 2.0 format
) {
syntaxProfiles[syntax] = {
...syntaxProfiles[syntax],
selfClosingStyle: 'xml'
};
}
}
}

return {
preferences,
showExpandedAbbreviation: emmetConfig['showExpandedAbbreviation'],
showAbbreviationSuggestions: emmetConfig['showAbbreviationSuggestions'],
syntaxProfiles,
variables: emmetConfig['variables'],
excludeLanguages: emmetConfig['excludeLanguages'],
showSuggestionsAsSnippets: emmetConfig['showSuggestionsAsSnippets']
};
}
return {
preferences,
showExpandedAbbreviation: emmetConfig['showExpandedAbbreviation'],
showAbbreviationSuggestions: emmetConfig['showAbbreviationSuggestions'],
syntaxProfiles,
variables: emmetConfig['variables'],
excludeLanguages: emmetConfig['excludeLanguages'],
showSuggestionsAsSnippets: emmetConfig['showSuggestionsAsSnippets']
};
}
},
};
}

Expand Down
183 changes: 93 additions & 90 deletions packages/eslint/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,105 +1,108 @@
import type { Service, Diagnostic, CodeAction, ServiceContext } from '@volar/language-service';
import type { CodeAction, Diagnostic, ServiceContext, ServicePlugin } from '@volar/language-service';
import { ESLint, Linter } from 'eslint';
import type * as ts from 'typescript/lib/tsserverlibrary';
import type { Provide } from 'volar-service-typescript';

export function create(resolveConfig?: (program: ts.Program) => Linter.Config): Service {
export function create(resolveConfig?: (program: ts.Program) => Linter.Config): ServicePlugin {

const instances = new WeakMap<ts.Program, ESLint>();
const uriToLintResult = new Map<string, ESLint.LintResult[]>();

return (context: ServiceContext<Provide> | undefined): ReturnType<Service> => ({

async provideSemanticDiagnostics(document) {

const languageService = context!.inject('typescript/languageService');
const eslint = getEslint(languageService.getProgram()!);
const lintResult = await eslint.lintText(
document.getText(),
{ filePath: context!.env.uriToFileName(document.uri) },
);
uriToLintResult.set(document.uri, lintResult);
const diagnostics: Diagnostic[] = [];

for (let i = 0; i < lintResult.length; i++) {
const result = lintResult[i];
for (let j = 0; j < result.messages.length; j++) {
const message = result.messages[j];
if (message.severity === 0) {
continue;
}
if (!message.line || !message.column) {
message.line = 1;
message.column = 1;
return {
create(context: ServiceContext<Provide>) {
return {
async provideSemanticDiagnostics(document) {

const languageService = context.inject('typescript/languageService');
const eslint = getEslint(languageService.getProgram()!);
const lintResult = await eslint.lintText(
document.getText(),
{ filePath: context.env.uriToFileName(document.uri) },
);
uriToLintResult.set(document.uri, lintResult);
const diagnostics: Diagnostic[] = [];

for (let i = 0; i < lintResult.length; i++) {
const result = lintResult[i];
for (let j = 0; j < result.messages.length; j++) {
const message = result.messages[j];
if (message.severity === 0) {
continue;
}
if (!message.line || !message.column) {
message.line = 1;
message.column = 1;
}
diagnostics.push({
source: 'eslint',
code: message.ruleId ?? undefined,
message: message.message,
severity: message.severity === 1 ? 2 : 1,
range: {
start: {
line: message.line - 1,
character: message.column - 1,
},
end: {
line: message.endLine ? message.endLine - 1 : message.line - 1,
character: message.endColumn ? message.endColumn - 1 : message.column - 1,
},
},
data: {
uri: document.uri,
version: document.version,
indexes: [i, j],
},
});
}
}
diagnostics.push({
source: 'eslint',
code: message.ruleId ?? undefined,
message: message.message,
severity: message.severity === 1 ? 2 : 1,
range: {
start: {
line: message.line - 1,
character: message.column - 1,
},
end: {
line: message.endLine ? message.endLine - 1 : message.line - 1,
character: message.endColumn ? message.endColumn - 1 : message.column - 1,
},
},
data: {
uri: document.uri,
version: document.version,
indexes: [i, j],
},
});
}
}

return diagnostics;
},

provideCodeActions(document, _range, codeActionContext) {

const result: CodeAction[] = [];

for (const diagnostic of codeActionContext.diagnostics) {

if (diagnostic.source !== 'eslint') {
continue;
}

if (diagnostic.data?.uri !== document.uri || diagnostic.data?.version !== document.version) {
continue;
}

const lintResult = uriToLintResult.get(document.uri);
const message = lintResult?.[diagnostic.data.indexes[0]]?.messages[diagnostic.data.indexes[1]];
if (!message?.fix) {
continue;
}

const codeAction: CodeAction = {
title: 'Fix ESLint: ' + message.message,
kind: 'quickfix',
edit: {
changes: {
[document.uri]: [{
range: {
start: document.positionAt(message.fix.range[0]),
end: document.positionAt(message.fix.range[1]),
return diagnostics;
},

provideCodeActions(document, _range, codeActionContext) {

const result: CodeAction[] = [];

for (const diagnostic of codeActionContext.diagnostics) {

if (diagnostic.source !== 'eslint') {
continue;
}

if (diagnostic.data?.uri !== document.uri || diagnostic.data?.version !== document.version) {
continue;
}

const lintResult = uriToLintResult.get(document.uri);
const message = lintResult?.[diagnostic.data.indexes[0]]?.messages[diagnostic.data.indexes[1]];
if (!message?.fix) {
continue;
}

const codeAction: CodeAction = {
title: 'Fix ESLint: ' + message.message,
kind: 'quickfix',
edit: {
changes: {
[document.uri]: [{
range: {
start: document.positionAt(message.fix.range[0]),
end: document.positionAt(message.fix.range[1]),
},
newText: message.fix.text,
}],
},
newText: message.fix.text,
}],
},
},
};
result.push(codeAction);
}

return result;
},
};
result.push(codeAction);
}

return result;
},
};
},
});
};

function getEslint(program: ts.Program) {
return instances.get(program) ?? instances.set(program, new ESLint(
Expand Down
Loading

0 comments on commit 7b9e601

Please sign in to comment.