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 parsing-only support for typescript #154

Merged
merged 9 commits into from
Oct 17, 2020
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package dev.blachut.svelte.lang

import com.intellij.lang.javascript.index.IndexedFileTypeProvider
import com.intellij.openapi.fileTypes.FileType

class SvelteIndexedFileTypeProvider : IndexedFileTypeProvider {
override fun getFileTypesToIndex(): Array<FileType> = arrayOf(SvelteHtmlFileType.INSTANCE)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package dev.blachut.svelte.lang

import com.intellij.codeInspection.InspectionSuppressor
import com.intellij.codeInspection.SuppressQuickFix
import com.intellij.psi.PsiElement
import dev.blachut.svelte.lang.psi.SvelteHtmlFile

class SvelteInspectionSuppressor : InspectionSuppressor {
override fun isSuppressedFor(element: PsiElement, toolId: String): Boolean {
if (element.containingFile is SvelteHtmlFile) {
if (toolId == "UnnecessaryLabelJS") {
return element.textMatches("$")
}
if (toolId == "BadExpressionStatementJS") {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't see problems from this inspection in JS, is this strictly TS related or did I miss something?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it fixes useless warning for $: name

return true
}
}

return false
}

override fun getSuppressActions(element: PsiElement?, toolId: String): Array<SuppressQuickFix> = emptyArray()
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
package dev.blachut.svelte.lang

import com.intellij.codeInsight.daemon.impl.analysis.HighlightInfoHolder
import com.intellij.codeInspection.InspectionSuppressor
import com.intellij.lang.annotation.AnnotationHolder
import com.intellij.lang.ecmascript6.validation.ES6AnalysisHandlersFactory
import com.intellij.lang.ecmascript6.validation.ES6AnnotatingVisitor
Expand Down Expand Up @@ -33,8 +32,4 @@ class SvelteJSAnalysisHandlersFactory : ES6AnalysisHandlersFactory() {
}
}
}

override fun getInspectionSuppressor(): InspectionSuppressor {
return SvelteJSInspectionSuppressor
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ package dev.blachut.svelte.lang
import com.intellij.codeInsight.daemon.impl.analysis.HighlightInfoHolder
import com.intellij.lang.javascript.JSTokenTypes
import com.intellij.lang.javascript.psi.JSLabeledStatement
import com.intellij.lang.javascript.validation.ES6KeywordHighlighterVisitor
import com.intellij.lang.javascript.validation.TypeScriptKeywordHighlighterVisitor
import dev.blachut.svelte.lang.codeInsight.SvelteReactiveDeclarationsUtil
import dev.blachut.svelte.lang.psi.SvelteInitialTag
import dev.blachut.svelte.lang.psi.SvelteJSLazyPsiElement
import dev.blachut.svelte.lang.psi.SvelteTokenTypes
import dev.blachut.svelte.lang.psi.SvelteVisitor

class SvelteKeywordHighlighterVisitor(holder: HighlightInfoHolder) : ES6KeywordHighlighterVisitor(holder),
class SvelteKeywordHighlighterVisitor(holder: HighlightInfoHolder) : TypeScriptKeywordHighlighterVisitor(holder),
SvelteVisitor {
override fun visitInitialTag(tag: SvelteInitialTag) {
highlightChildKeywordOfType(tag, SvelteTokenTypes.AS_KEYWORD)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package dev.blachut.svelte.lang

import com.intellij.codeInsight.daemon.impl.analysis.HighlightInfoHolder
import com.intellij.lang.annotation.AnnotationHolder
import com.intellij.lang.javascript.DialectOptionHolder
import com.intellij.lang.javascript.ecmascript6.TypeScriptAnalysisHandlersFactory
import com.intellij.lang.javascript.ecmascript6.TypeScriptAnnotatingVisitor
import com.intellij.lang.javascript.psi.JSLabeledStatement
import com.intellij.lang.javascript.validation.JSAnnotatingVisitor
import com.intellij.lang.javascript.validation.JSKeywordHighlighterVisitor
import com.intellij.psi.PsiElement
import dev.blachut.svelte.lang.codeInsight.SvelteReactiveDeclarationsUtil

class SvelteTypeScriptAnalysisHandlersFactory : TypeScriptAnalysisHandlersFactory() {

override fun createKeywordHighlighterVisitor(
holder: HighlightInfoHolder,
dialectOptionHolder: DialectOptionHolder
): JSKeywordHighlighterVisitor {
return SvelteKeywordHighlighterVisitor(holder)
}

override fun createAnnotatingVisitor(psiElement: PsiElement, holder: AnnotationHolder): JSAnnotatingVisitor {
return object : TypeScriptAnnotatingVisitor(psiElement, holder) {
override fun visitJSLabeledStatement(node: JSLabeledStatement) {
if (node.label != SvelteReactiveDeclarationsUtil.REACTIVE_LABEL) {
super.visitJSLabeledStatement(node)
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package dev.blachut.svelte.lang;

import com.intellij.lang.DependentLanguage;
import com.intellij.lang.PsiBuilder;
import com.intellij.lang.javascript.DialectOptionHolder;
import com.intellij.lang.javascript.JSLanguageDialect;
import com.intellij.lang.javascript.JavaScriptSupportLoader;
import com.intellij.lang.javascript.parsing.JavaScriptParser;
import dev.blachut.svelte.lang.parsing.ts.SvelteTypeScriptParser;
import org.jetbrains.annotations.NotNull;

/**
* Always nested inside {@link SvelteHTMLLanguage}
*/
public class SvelteTypeScriptLanguage extends JSLanguageDialect implements DependentLanguage {
public static final SvelteTypeScriptLanguage INSTANCE = new SvelteTypeScriptLanguage();

private SvelteTypeScriptLanguage() {
super("SvelteTS", DialectOptionHolder.TS, JavaScriptSupportLoader.TYPESCRIPT);
}

@Override
public String getFileExtension() {
return "ts";
}

@Override
public JavaScriptParser<?, ?, ?, ?> createParser(@NotNull PsiBuilder builder) {
return new SvelteTypeScriptParser(builder);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package dev.blachut.svelte.lang

import com.intellij.lang.javascript.psi.impl.JSReferenceExpressionImpl
import com.intellij.lang.typescript.TypeScriptSpecificHandlersFactory
import com.intellij.psi.impl.source.resolve.ResolveCache
import dev.blachut.svelte.lang.codeInsight.SvelteTypeScriptReferenceExpressionResolver

class SvelteTypeScriptSpecificHandlersFactory : TypeScriptSpecificHandlersFactory() {
override fun createReferenceExpressionResolver(
referenceExpression: JSReferenceExpressionImpl,
ignorePerformanceLimits: Boolean
): ResolveCache.PolyVariantResolver<JSReferenceExpressionImpl> {
return SvelteTypeScriptReferenceExpressionResolver(referenceExpression, ignorePerformanceLimits)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,33 @@ class SvelteJSReferenceExpressionResolver(
ignorePerformanceLimits: Boolean
) :
JSReferenceExpressionResolver(referenceExpression, ignorePerformanceLimits) {
private val implicitIdentifiers = arrayOf("\$\$props", "\$\$restProps", "\$\$slots")

override fun resolve(expression: JSReferenceExpressionImpl, incompleteCode: Boolean): Array<ResolveResult> {
implicitIdentifiers.forEach {
if (JSSymbolUtil.isAccurateReferenceExpressionName(expression, it)) {
val element = JSImplicitElementImpl.Builder(it, expression)
.forbidAstAccess()
.setType(JSImplicitElement.Type.Variable)
.setProperties(JSImplicitElement.Property.Constant)
.toImplicitElement()
return arrayOf(JSResolveResult(element))
}
}
val resolveImplicits = resolveImplicits(expression)
if (resolveImplicits.isNotEmpty()) return resolveImplicits

return super.resolve(expression, incompleteCode)
}

override fun createLocalResolveProcessor(sink: ResolveResultSink): SinkResolveProcessor<ResolveResultSink> {
return SvelteReactiveDeclarationsUtil.SvelteSinkResolveProcessor(myReferencedName, myRef, sink)
}

companion object {

private val implicitIdentifiers = arrayOf("\$\$props", "\$\$restProps", "\$\$slots")

fun resolveImplicits(expression: JSReferenceExpressionImpl): Array<ResolveResult> {
implicitIdentifiers.forEach {
if (JSSymbolUtil.isAccurateReferenceExpressionName(expression, it)) {
val element = JSImplicitElementImpl.Builder(it, expression)
.forbidAstAccess()
.setType(JSImplicitElement.Type.Variable)
.setProperties(JSImplicitElement.Property.Constant)
.toImplicitElement()
return arrayOf(JSResolveResult(element))
}
}
return emptyArray()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package dev.blachut.svelte.lang.codeInsight

import com.intellij.lang.javascript.ecmascript6.TypeScriptReferenceExpressionResolver
import com.intellij.lang.javascript.psi.impl.JSReferenceExpressionImpl
import com.intellij.lang.javascript.psi.resolve.ResolveResultSink
import com.intellij.lang.javascript.psi.resolve.SinkResolveProcessor
import com.intellij.psi.ResolveResult
import dev.blachut.svelte.lang.codeInsight.SvelteJSReferenceExpressionResolver.Companion.resolveImplicits

class SvelteTypeScriptReferenceExpressionResolver(
referenceExpression: JSReferenceExpressionImpl,
ignorePerformanceLimits: Boolean
) : TypeScriptReferenceExpressionResolver(referenceExpression, ignorePerformanceLimits) {
override fun resolve(expression: JSReferenceExpressionImpl, incompleteCode: Boolean): Array<ResolveResult> {
val resolveImplicits = resolveImplicits(expression)
if (resolveImplicits.isNotEmpty()) return resolveImplicits

return super.resolve(expression, incompleteCode)
}

override fun createLocalResolveProcessor(sink: ResolveResultSink): SinkResolveProcessor<ResolveResultSink> {
return SvelteReactiveDeclarationsUtil.SvelteSinkResolveProcessor(myReferencedName, myRef, sink)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import com.intellij.psi.xml.XmlTokenType
import com.intellij.util.containers.ContainerUtil
import dev.blachut.svelte.lang.SvelteHTMLLanguage
import dev.blachut.svelte.lang.SvelteJSLanguage
import dev.blachut.svelte.lang.SvelteTypeScriptLanguage
import dev.blachut.svelte.lang.psi.SvelteJSReferenceExpression
import kotlin.experimental.or

Expand Down Expand Up @@ -73,6 +74,7 @@ class SvelteFilterLexer(occurrenceConsumer: OccurrenceConsumer, originalLexer: L
HTMLLanguage.INSTANCE,
SvelteHTMLLanguage.INSTANCE,
SvelteJSLanguage.INSTANCE,
SvelteTypeScriptLanguage.INSTANCE,
Language.ANY
)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package dev.blachut.svelte.lang.parsing.html

import com.intellij.lang.javascript.DialectDetector
import com.intellij.lang.javascript.ecmascript6.TypeScriptResolveScopeProvider
import com.intellij.lang.javascript.psi.resolve.JSElementResolveScopeProvider
import com.intellij.lang.javascript.psi.util.JSUtils
import com.intellij.lang.typescript.library.TypeScriptLibraryProvider
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiElement
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.xml.XmlFile
import dev.blachut.svelte.lang.SvelteHtmlFileType

class SvelteElementResolveScopeProvider : JSElementResolveScopeProvider {
Copy link
Owner

@tomblachut tomblachut Oct 17, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

THB I don't understand what this class does

private val tsProvider = object : TypeScriptResolveScopeProvider() {
override fun isApplicable(file: VirtualFile) = true

override fun restrictByFileType(file: VirtualFile, libraryService: TypeScriptLibraryProvider, moduleAndLibraryScope: GlobalSearchScope): GlobalSearchScope {
return super.restrictByFileType(file, libraryService, moduleAndLibraryScope)
.uniteWith(GlobalSearchScope.getScopeRestrictedByFileTypes(moduleAndLibraryScope, file.fileType))
}
}

override fun getElementResolveScope(element: PsiElement): GlobalSearchScope? {
val psiFile = element.containingFile
if (psiFile?.fileType !is SvelteHtmlFileType) return null
if (psiFile !is XmlFile) return null
val scriptTag = JSUtils.findScriptTagContent(psiFile)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Svelte can have two script tags and they could have different languages, this finds first one, is it okay?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can live with it as is. I will try to find a better strategy but I am not sure

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We way we handle this in the language server is to check script and then module script and pick the first lang/type tag we find, so if someone chooses TS in the script tag he will automatically use it in the other one as well - no language mixes.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like a good balance 👍


if (scriptTag != null && DialectDetector.isTypeScript(scriptTag)) {
return tsProvider.getResolveScope(psiFile.viewProvider.virtualFile, element.project)
}
return null
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@ package dev.blachut.svelte.lang.parsing.html

import com.intellij.lang.HtmlScriptContentProvider
import com.intellij.lang.Language
import com.intellij.lang.LanguageHtmlScriptContentProvider
import com.intellij.lang.css.CSSLanguage
import com.intellij.lang.javascript.JavaScriptHighlightingLexer
import com.intellij.lang.javascript.dialects.JSLanguageLevel
import com.intellij.lexer.HtmlHighlightingLexer
import com.intellij.lexer.LayeredLexer
import com.intellij.psi.tree.IElementType
import dev.blachut.svelte.lang.SvelteJSLanguage
import dev.blachut.svelte.lang.psi.SvelteTokenTypes

class SvelteHtmlHighlightingLexer : LayeredLexer(BaseSvelteHtmlHighlightingLexer()) {
Expand All @@ -33,6 +31,12 @@ private open class BaseSvelteHtmlHighlightingLexer : HtmlHighlightingLexer(Inner
this@BaseSvelteHtmlHighlightingLexer.seenStylesheetType = value
}

override var seenContentType: Boolean
get() = this@BaseSvelteHtmlHighlightingLexer.seenContentType
set(value) {
this@BaseSvelteHtmlHighlightingLexer.seenContentType = value
}
override val seenScript: Boolean get() = this@BaseSvelteHtmlHighlightingLexer.seenScript
override val seenStyle: Boolean get() = this@BaseSvelteHtmlHighlightingLexer.seenStyle
override val styleType: String? get() = this@BaseSvelteHtmlHighlightingLexer.styleType
override val inTagState: Boolean get() = (state and BASE_STATE_MASK) == _SvelteHtmlLexer.START_TAG_NAME
Expand All @@ -42,8 +46,8 @@ private open class BaseSvelteHtmlHighlightingLexer : HtmlHighlightingLexer(Inner
}
})

override fun findScriptContentProvider(mimeType: String?): HtmlScriptContentProvider {
return LanguageHtmlScriptContentProvider.getScriptContentProvider(SvelteJSLanguage.INSTANCE)
override fun findScriptContentProvider(mimeType: String?): HtmlScriptContentProvider? {
return SvelteHtmlLexer.getSvelteScriptContentProvider(mimeType)
}

override fun isHtmlTagState(state: Int): Boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.intellij.lexer.HtmlHighlightingLexer
import com.intellij.lexer.HtmlLexer
import com.intellij.psi.tree.IElementType
import dev.blachut.svelte.lang.SvelteJSLanguage
import dev.blachut.svelte.lang.SvelteTypeScriptLanguage

class SvelteHtmlLexer : HtmlLexer(InnerSvelteHtmlLexer(), false) {
private val helper = SvelteHtmlLexerHelper(object : SvelteHtmlLexerHandle {
Expand All @@ -18,13 +19,20 @@ class SvelteHtmlLexer : HtmlLexer(InnerSvelteHtmlLexer(), false) {
this@SvelteHtmlLexer.seenTag = value
}

override var seenContentType: Boolean
get() = this@SvelteHtmlLexer.seenContentType
set(value) {
this@SvelteHtmlLexer.seenContentType = value
}

override var seenStyleType: Boolean
get() = this@SvelteHtmlLexer.seenStylesheetType
set(value) {
this@SvelteHtmlLexer.seenStylesheetType = value
}

override val seenStyle: Boolean get() = this@SvelteHtmlLexer.seenStyle
override val seenScript: Boolean get() = this@SvelteHtmlLexer.seenScript
override val styleType: String? get() = this@SvelteHtmlLexer.styleType
override val inTagState: Boolean get() = (state and HtmlHighlightingLexer.BASE_STATE_MASK) == _SvelteHtmlLexer.START_TAG_NAME

Expand All @@ -46,8 +54,8 @@ class SvelteHtmlLexer : HtmlLexer(InnerSvelteHtmlLexer(), false) {
return (nestingLevel shl 16) or (super.getState() and 0xffff)
}

override fun findScriptContentProvider(mimeType: String?): HtmlScriptContentProvider {
return LanguageHtmlScriptContentProvider.getScriptContentProvider(SvelteJSLanguage.INSTANCE)
override fun findScriptContentProvider(mimeType: String?): HtmlScriptContentProvider? {
return getSvelteScriptContentProvider(mimeType)
}

override fun getStyleLanguage(): Language? =
Expand All @@ -56,4 +64,14 @@ class SvelteHtmlLexer : HtmlLexer(InnerSvelteHtmlLexer(), false) {
override fun isHtmlTagState(state: Int): Boolean {
return state == _SvelteHtmlLexer.START_TAG_NAME || state == _SvelteHtmlLexer.END_TAG_NAME
}

companion object {
fun getSvelteScriptContentProvider(mimeType: String?): HtmlScriptContentProvider? {
if (mimeType == "ts" || mimeType == "typescript") {
return LanguageHtmlScriptContentProvider.getScriptContentProvider(SvelteTypeScriptLanguage.INSTANCE)
}

return LanguageHtmlScriptContentProvider.getScriptContentProvider(SvelteJSLanguage.INSTANCE)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ interface SvelteHtmlLexerHandle {
fun registerHandler(elementType: IElementType, value: BaseHtmlLexer.TokenHandler)

var seenTag: Boolean
val seenScript: Boolean
var seenStyleType: Boolean
var seenContentType: Boolean
val seenStyle: Boolean
val styleType: String?
val inTagState: Boolean
Expand Down
Loading