Skip to content
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
20 changes: 14 additions & 6 deletions packages/language-server/src/lib/documents/Document.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { urlToPath } from '../../utils';
import { WritableDocument } from './DocumentBase';
import { extractScriptTags, extractStyleTag, TagInformation } from './utils';
import { extractScriptTags, extractStyleTag, extractTemplateTag, TagInformation } from './utils';
import { parseHtml } from './parseHtml';
import { SvelteConfig, configLoader } from './configLoader';
import { HTMLDocument } from 'vscode-html-languageservice';
Expand All @@ -13,6 +13,7 @@ export class Document extends WritableDocument {
scriptInfo: TagInformation | null = null;
moduleScriptInfo: TagInformation | null = null;
styleInfo: TagInformation | null = null;
templateInfo: TagInformation | null = null;
configPromise: Promise<SvelteConfig | undefined>;
config?: SvelteConfig;
html!: HTMLDocument;
Expand All @@ -25,8 +26,8 @@ export class Document extends WritableDocument {

private updateDocInfo() {
this.html = parseHtml(this.content);
const scriptTags = extractScriptTags(this.content, this.html);
const update = (config: SvelteConfig | undefined) => {
const scriptTags = extractScriptTags(this.content, this.html);
this.config = config;
this.scriptInfo = this.addDefaultLanguage(config, scriptTags?.script || null, 'script');
this.moduleScriptInfo = this.addDefaultLanguage(
Expand All @@ -39,6 +40,11 @@ export class Document extends WritableDocument {
extractStyleTag(this.content, this.html),
'style'
);
this.templateInfo = this.addDefaultLanguage(
config,
extractTemplateTag(this.content, this.html),
'markup'
);
};

const config = configLoader.getConfig(this.getFilePath() || '');
Expand Down Expand Up @@ -82,22 +88,24 @@ export class Document extends WritableDocument {
}

/**
* Returns the language associated to either script or style.
* Returns the language associated to script, style or template.
* Returns an empty string if there's nothing set.
*/
getLanguageAttribute(tag: 'script' | 'style'): string {
getLanguageAttribute(tag: 'script' | 'style' | 'template'): string {
const attrs =
(tag === 'style'
? this.styleInfo?.attributes
: this.scriptInfo?.attributes || this.moduleScriptInfo?.attributes) || {};
: tag === 'script'
? this.scriptInfo?.attributes || this.moduleScriptInfo?.attributes
: this.templateInfo?.attributes) || {};
const lang = attrs.lang || attrs.type || '';
return lang.replace(/^text\//, '');
}

private addDefaultLanguage(
config: SvelteConfig | undefined,
tagInfo: TagInformation | null,
tag: 'style' | 'script'
tag: 'style' | 'script' | 'markup'
): TagInformation | null {
if (!tagInfo || !config) {
return tagInfo;
Expand Down
16 changes: 15 additions & 1 deletion packages/language-server/src/lib/documents/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,11 @@ function blankIfBlocks(text: string): string {
* @param source text content to extract tag from
* @param tag the tag to extract
*/
function extractTags(text: string, tag: 'script' | 'style', html?: HTMLDocument): TagInformation[] {
function extractTags(
text: string,
tag: 'script' | 'style' | 'template',
html?: HTMLDocument
): TagInformation[] {
text = blankIfBlocks(text);
const rootNodes = html?.roots || parseHtml(text).roots;
const matchedNodes = rootNodes
Expand Down Expand Up @@ -173,6 +177,16 @@ export function extractStyleTag(source: string, html?: HTMLDocument): TagInforma
return styles[0];
}

export function extractTemplateTag(source: string, html?: HTMLDocument): TagInformation | null {
const templates = extractTags(source, 'template', html);
if (!templates.length) {
return null;
}

// There should only be one style tag
return templates[0];
}

/**
* Get the line and character based on the offset
* @param offset The index of the position
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import ts from 'typescript';
import { Diagnostic, DiagnosticSeverity, DiagnosticTag } from 'vscode-languageserver';
import { Document, mapObjWithRangeToOriginal, getTextInRange } from '../../../lib/documents';
import {
Document,
mapObjWithRangeToOriginal,
getTextInRange,
isRangeInTag
} from '../../../lib/documents';
import { DiagnosticsProvider } from '../../interfaces';
import { LSAndTSDocResolver } from '../LSAndTSDocResolver';
import { convertRange, mapSeverity } from '../utils';
Expand Down Expand Up @@ -52,7 +57,7 @@ export class DiagnosticsProviderImpl implements DiagnosticsProvider {
}))
.map((diagnostic) => mapObjWithRangeToOriginal(fragment, diagnostic))
.filter(hasNoNegativeLines)
.filter(isNoFalsePositive(document.getText(), tsDoc))
.filter(isNoFalsePositive(document, tsDoc))
.map(enhanceIfNecessary)
.map(swapRangeStartEndIfNecessary);
}
Expand Down Expand Up @@ -82,16 +87,32 @@ function hasNoNegativeLines(diagnostic: Diagnostic): boolean {
return diagnostic.range.start.line >= 0 && diagnostic.range.end.line >= 0;
}

function isNoFalsePositive(text: string, tsDoc: SvelteDocumentSnapshot) {
return (diagnostic: Diagnostic, idx: number) => {
function isNoFalsePositive(document: Document, tsDoc: SvelteDocumentSnapshot) {
const text = document.getText();
const usesPug = document.getLanguageAttribute('template') === 'pug';

return (diagnostic: Diagnostic) => {
return (
isNoJsxCannotHaveMultipleAttrsError(diagnostic) &&
isNoUnusedLabelWarningForReactiveStatement(diagnostic) &&
isNoUsedBeforeAssigned(diagnostic, text, tsDoc)
isNoUsedBeforeAssigned(diagnostic, text, tsDoc) &&
(!usesPug || isNoPugFalsePositive(diagnostic, document))
);
};
}

/**
* All diagnostics inside the template tag and the unused import/variable diagnostics
* are marked as false positive.
*/
function isNoPugFalsePositive(diagnostic: Diagnostic, document: Document): boolean {
return (
!isRangeInTag(diagnostic.range, document.templateInfo) &&
diagnostic.code !== 6133 &&
diagnostic.code !== 6192
);
}

/**
* Variable used before being assigned, can happen when you do `export let x`
* without assigning a value in strict mode. Should not throw an error here
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -871,4 +871,63 @@ describe('DiagnosticsProvider', () => {
}
]);
});

it('Pug: Ignores errors in template and unused warnings in script', async () => {
const { plugin, document } = setup('diagnostics-pug.svelte');
const diagnostics = await plugin.getDiagnostics(document);

assert.deepStrictEqual(diagnostics, [
{
code: 2307,
message: "Cannot find module '.' or its corresponding type declarations.",
range: {
end: {
character: 22,
line: 1
},
start: {
character: 19,
line: 1
}
},
severity: 1,
source: 'ts',
tags: []
},
{
code: 2307,
message: "Cannot find module '.' or its corresponding type declarations.",
range: {
end: {
character: 30,
line: 2
},
start: {
character: 27,
line: 2
}
},
severity: 1,
source: 'ts',
tags: []
},
{
code: 2322,
message: "Type 'boolean' is not assignable to type 'string | number'.",
range: {
end: {
character: 10,
line: 4
},
start: {
character: 9,
line: 4
}
},
severity: 1,
source: 'ts',
tags: []
}
]);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<script lang="ts">
import Foo from '.';
import { A, B, C } from '.';

const a: string | number = true;
</script>

<template lang="pug">
+if('typeof a === "number"')
A(a='{a.toFixed(2)}')
</template>