mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-03-22 15:19:59 +07:00
syntax: introduce lazy parseables
GitOrigin-RevId: dfb8f5593c78ecae135b1d014707ac48d3ffbf74
This commit is contained in:
committed by
intellij-monorepo-bot
parent
a58b3405e5
commit
16ea12d6ae
@@ -31,6 +31,7 @@ jvm_library(
|
||||
"//platform/util/base/multiplatform",
|
||||
"//platform/util/multiplatform",
|
||||
"//platform/syntax/syntax-util:util",
|
||||
"//platform/syntax/syntax-extensions:extensions",
|
||||
],
|
||||
exports = [
|
||||
"//platform/syntax/syntax-api:syntax",
|
||||
|
||||
@@ -32,5 +32,6 @@
|
||||
<orderEntry type="module" module-name="intellij.platform.util.base.multiplatform" />
|
||||
<orderEntry type="module" module-name="intellij.platform.util.multiplatform" />
|
||||
<orderEntry type="module" module-name="intellij.platform.syntax.util" exported="" />
|
||||
<orderEntry type="module" module-name="intellij.platform.syntax.extensions" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -7,12 +7,16 @@ import com.intellij.java.syntax.lexer.JavaDocLexer
|
||||
import com.intellij.java.syntax.lexer.JavaLexer
|
||||
import com.intellij.java.syntax.lexer.JavaTypeEscapeLexer
|
||||
import com.intellij.platform.syntax.SyntaxElementTypeSet
|
||||
import com.intellij.platform.syntax.element.SyntaxTokenTypes
|
||||
import com.intellij.platform.syntax.extensions.SyntaxLanguage
|
||||
import com.intellij.platform.syntax.lexer.Lexer
|
||||
import com.intellij.platform.syntax.syntaxElementTypeSetOf
|
||||
import com.intellij.pom.java.LanguageLevel
|
||||
import kotlin.jvm.JvmStatic
|
||||
|
||||
object JavaSyntaxDefinition {
|
||||
val language: SyntaxLanguage = SyntaxLanguage("com.intellij.java")
|
||||
|
||||
@JvmStatic
|
||||
fun createLexer(languageLevel: LanguageLevel): Lexer = JavaLexer(languageLevel)
|
||||
|
||||
@@ -27,4 +31,6 @@ object JavaSyntaxDefinition {
|
||||
JavaSyntaxTokenType.C_STYLE_COMMENT,
|
||||
JavaDocSyntaxElementType.DOC_COMMENT
|
||||
)
|
||||
|
||||
val whitespaces: SyntaxElementTypeSet = syntaxElementTypeSetOf(SyntaxTokenTypes.WHITE_SPACE)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.java.syntax
|
||||
|
||||
import com.intellij.platform.syntax.Logger
|
||||
import com.intellij.platform.syntax.util.log.logger
|
||||
|
||||
internal object JavaSyntaxLog {
|
||||
val log: Logger = logger<JavaSyntaxLog>()
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.java.syntax.element
|
||||
|
||||
import com.intellij.platform.syntax.SyntaxElementType
|
||||
import com.intellij.platform.syntax.element.SyntaxTokenTypes
|
||||
import com.intellij.platform.syntax.parser.WhitespaceOrCommentBindingPolicy
|
||||
import com.intellij.platform.syntax.syntaxElementTypeSetOf
|
||||
|
||||
internal object JavaBindingPolicy : WhitespaceOrCommentBindingPolicy {
|
||||
private val leftBoundTokens = syntaxElementTypeSetOf(
|
||||
SyntaxTokenTypes.ERROR_ELEMENT, // todo move somewhere?
|
||||
JavaSyntaxElementType.TYPE_PARAMETER_LIST,
|
||||
JavaSyntaxElementType.NAME_VALUE_PAIR,
|
||||
JavaSyntaxElementType.ANNOTATION_PARAMETER_LIST,
|
||||
JavaSyntaxElementType.EXTENDS_LIST,
|
||||
JavaSyntaxElementType.IMPLEMENTS_LIST,
|
||||
JavaSyntaxElementType.EXTENDS_BOUND_LIST,
|
||||
JavaSyntaxElementType.THROWS_LIST,
|
||||
JavaSyntaxElementType.PROVIDES_WITH_LIST,
|
||||
JavaSyntaxElementType.PERMITS_LIST,
|
||||
JavaSyntaxElementType.REFERENCE_PARAMETER_LIST,
|
||||
JavaSyntaxElementType.EMPTY_EXPRESSION,
|
||||
JavaSyntaxElementType.EXPRESSION_LIST,
|
||||
JavaSyntaxElementType.ANNOTATION_PARAMETER_LIST,
|
||||
|
||||
)
|
||||
|
||||
override fun isLeftBound(elementType: SyntaxElementType): Boolean =
|
||||
elementType in leftBoundTokens
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.java.syntax.element
|
||||
|
||||
import com.intellij.java.syntax.parser.JavaParser
|
||||
import com.intellij.platform.syntax.CancellationProvider
|
||||
import com.intellij.platform.syntax.LazyParser
|
||||
import com.intellij.platform.syntax.LazyParsingContext
|
||||
import com.intellij.platform.syntax.lexer.TokenList
|
||||
import com.intellij.platform.syntax.parser.ProductionResult
|
||||
import com.intellij.platform.syntax.parser.prepareProduction
|
||||
import com.intellij.platform.syntax.tree.SyntaxNode
|
||||
import com.intellij.platform.syntax.util.cancellation.cancellationProvider
|
||||
import com.intellij.platform.syntax.util.parser.SyntaxBuilderUtil
|
||||
import com.intellij.pom.java.LanguageLevel
|
||||
|
||||
internal class JavaCodeBlockParser : LazyParser {
|
||||
override fun parse(parsingContext: LazyParsingContext): ProductionResult {
|
||||
return doParse(
|
||||
node = parsingContext.node,
|
||||
cachedLexemes = parsingContext.tokenList,
|
||||
text = parsingContext.text,
|
||||
level = getLanguageLevel(parsingContext),
|
||||
cancellationProvider = cancellationProvider()
|
||||
)
|
||||
}
|
||||
|
||||
override fun tryReparse(parsingContext: LazyParsingContext): ProductionResult? {
|
||||
val cancellationProvider = cancellationProvider()
|
||||
|
||||
val level = getLanguageLevel(parsingContext)
|
||||
val tokens = cachedOrLex(
|
||||
cachedLexemes = parsingContext.tokenList,
|
||||
text = parsingContext.text,
|
||||
languageLevel = level,
|
||||
cancellationProvider = cancellationProvider
|
||||
)
|
||||
|
||||
val hasProperBraceBalance = SyntaxBuilderUtil.hasProperBraceBalance(
|
||||
tokenList = tokens,
|
||||
leftBrace = JavaSyntaxTokenType.LBRACE,
|
||||
rightBrace = JavaSyntaxTokenType.RBRACE,
|
||||
cancellationProvider = cancellationProvider
|
||||
)
|
||||
if (!hasProperBraceBalance) return null
|
||||
|
||||
return doParse(
|
||||
node = parsingContext.node,
|
||||
cachedLexemes = tokens,
|
||||
text = parsingContext.text,
|
||||
level = level,
|
||||
cancellationProvider = cancellationProvider
|
||||
)
|
||||
}
|
||||
|
||||
private fun doParse(
|
||||
node: SyntaxNode,
|
||||
cachedLexemes: TokenList? = null,
|
||||
text: CharSequence,
|
||||
level: LanguageLevel,
|
||||
cancellationProvider: CancellationProvider?,
|
||||
): ProductionResult {
|
||||
val builder = createSyntaxBuilder(node, text, level, cachedLexemes, cancellationProvider)
|
||||
JavaParser(level).statementParser.parseCodeBlockDeep(builder, true)
|
||||
return prepareProduction(builder)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.java.syntax.element
|
||||
|
||||
import com.intellij.platform.syntax.LazyParsingContext
|
||||
import com.intellij.platform.syntax.extensions.ExtensionPointKey
|
||||
import com.intellij.platform.syntax.extensions.ExtensionSupport
|
||||
import com.intellij.platform.syntax.tree.SyntaxTree
|
||||
import com.intellij.pom.java.LanguageLevel
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
/**
|
||||
* Implement this extension point to provide the custom language level for Java lazy parsers.
|
||||
*/
|
||||
@ApiStatus.OverrideOnly
|
||||
interface JavaLanguageLevelProvider {
|
||||
fun getLanguageLevel(syntaxTree: SyntaxTree): LanguageLevel
|
||||
}
|
||||
|
||||
internal fun getLanguageLevel(parsingContext: LazyParsingContext): LanguageLevel {
|
||||
val languageLevelProvider = ExtensionSupport().getExtensions(languageLevelExtensionPoint).firstOrNull()
|
||||
val languageLevel = languageLevelProvider?.getLanguageLevel(parsingContext.tree) ?: LanguageLevel.HIGHEST
|
||||
return languageLevel
|
||||
}
|
||||
|
||||
private val languageLevelExtensionPoint: ExtensionPointKey<JavaLanguageLevelProvider> = ExtensionPointKey("com.intellij.java.syntax.languageLevelProvider")
|
||||
@@ -119,7 +119,7 @@ object JavaSyntaxElementType {
|
||||
@JvmField val DEFAULT_CASE_LABEL_ELEMENT: SyntaxElementType = SyntaxElementType("DEFAULT_CASE_LABEL_ELEMENT")
|
||||
@JvmField val CASE_LABEL_ELEMENT_LIST: SyntaxElementType = SyntaxElementType("CASE_LABEL_ELEMENT_LIST")
|
||||
|
||||
@JvmField val CODE_BLOCK: SyntaxElementType = SyntaxElementType("CODE_BLOCK")
|
||||
@JvmField val CODE_BLOCK: SyntaxElementType = SyntaxElementType("CODE_BLOCK", lazyParser = JavaCodeBlockParser())
|
||||
|
||||
@JvmField val MEMBERS: SyntaxElementType = SyntaxElementType("MEMBERS")
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
@file:JvmName("JavaSyntaxUtil")
|
||||
|
||||
package com.intellij.java.syntax.element
|
||||
|
||||
import com.intellij.java.syntax.JavaSyntaxDefinition
|
||||
import com.intellij.java.syntax.JavaSyntaxLog
|
||||
import com.intellij.platform.syntax.CancellationProvider
|
||||
import com.intellij.platform.syntax.lexer.TokenList
|
||||
import com.intellij.platform.syntax.lexer.performLexing
|
||||
import com.intellij.platform.syntax.parser.SyntaxTreeBuilder
|
||||
import com.intellij.platform.syntax.parser.SyntaxTreeBuilderFactory
|
||||
import com.intellij.platform.syntax.tree.SyntaxNode
|
||||
import com.intellij.pom.java.LanguageLevel
|
||||
import kotlin.jvm.JvmName
|
||||
|
||||
internal fun createSyntaxBuilder(
|
||||
chameleon: SyntaxNode,
|
||||
text: CharSequence,
|
||||
languageLevel: LanguageLevel,
|
||||
cachedLexemes: TokenList? = null,
|
||||
cancellationProvider: CancellationProvider?,
|
||||
): SyntaxTreeBuilder {
|
||||
val lexemes = cachedOrLex(cachedLexemes, text, languageLevel, cancellationProvider)
|
||||
val builder = SyntaxTreeBuilderFactory.builder(
|
||||
text = text,
|
||||
whitespaces = JavaSyntaxDefinition.whitespaces,
|
||||
comments = JavaSyntaxDefinition.commentSet,
|
||||
tokenList = lexemes,
|
||||
)
|
||||
.withStartOffset(chameleon.startOffset)
|
||||
.withLanguage(JavaSyntaxDefinition.language.id)
|
||||
.withWhitespaceOrCommentBindingPolicy(JavaBindingPolicy)
|
||||
.withCancellationProvider(cancellationProvider)
|
||||
.withLogger(JavaSyntaxLog.log)
|
||||
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
internal fun cachedOrLex(
|
||||
cachedLexemes: TokenList?,
|
||||
text: CharSequence,
|
||||
languageLevel: LanguageLevel,
|
||||
cancellationProvider: CancellationProvider?,
|
||||
): TokenList {
|
||||
return cachedLexemes ?: run {
|
||||
val lexer = JavaSyntaxDefinition.createLexer(languageLevel)
|
||||
performLexing(text, lexer, cancellationProvider, JavaSyntaxLog.log)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,17 @@
|
||||
*:com.intellij.platform.syntax.CancellationProvider
|
||||
- a:checkCancelled():V
|
||||
*:com.intellij.platform.syntax.LazyParser
|
||||
- a:parse(com.intellij.platform.syntax.LazyParsingContext):com.intellij.platform.syntax.parser.ProductionResult
|
||||
- tryReparse(com.intellij.platform.syntax.LazyParsingContext):com.intellij.platform.syntax.parser.ProductionResult
|
||||
*f:com.intellij.platform.syntax.LazyParserKt
|
||||
- *sf:parseLazyNode(com.intellij.platform.syntax.LazyParsingContext):com.intellij.platform.syntax.parser.ProductionResult
|
||||
- *sf:tryReparseLazyNode(com.intellij.platform.syntax.LazyParsingContext):com.intellij.platform.syntax.parser.ProductionResult
|
||||
*f:com.intellij.platform.syntax.LazyParsingContext
|
||||
- <init>(com.intellij.platform.syntax.tree.SyntaxTree,com.intellij.platform.syntax.tree.SyntaxNode,java.lang.CharSequence,com.intellij.platform.syntax.lexer.TokenList):V
|
||||
- f:getNode():com.intellij.platform.syntax.tree.SyntaxNode
|
||||
- f:getText():java.lang.CharSequence
|
||||
- f:getTokenList():com.intellij.platform.syntax.lexer.TokenList
|
||||
- f:getTree():com.intellij.platform.syntax.tree.SyntaxTree
|
||||
*:com.intellij.platform.syntax.Logger
|
||||
- a:debug(java.lang.String,java.lang.Throwable):V
|
||||
- bs:debug$default(com.intellij.platform.syntax.Logger,java.lang.String,java.lang.Throwable,I,java.lang.Object):V
|
||||
@@ -20,8 +32,11 @@
|
||||
- equals(java.lang.Object):Z
|
||||
- f:getIndex():I
|
||||
- hashCode():I
|
||||
- f:isLazyParseable():Z
|
||||
*f:com.intellij.platform.syntax.SyntaxElementTypeKt
|
||||
- *sf:SyntaxElementType(java.lang.String):com.intellij.platform.syntax.SyntaxElementType
|
||||
- *sf:SyntaxElementType(java.lang.String,com.intellij.platform.syntax.LazyParser):com.intellij.platform.syntax.SyntaxElementType
|
||||
- *bs:SyntaxElementType$default(java.lang.String,com.intellij.platform.syntax.LazyParser,I,java.lang.Object):com.intellij.platform.syntax.SyntaxElementType
|
||||
*f:com.intellij.platform.syntax.SyntaxElementTypeSet
|
||||
- java.util.Set
|
||||
- kotlin.jvm.internal.markers.KMappedMarker
|
||||
@@ -189,3 +204,17 @@
|
||||
- f:defaultRightBinder():com.intellij.platform.syntax.parser.WhitespacesAndCommentsBinder
|
||||
- f:greedyLeftBinder():com.intellij.platform.syntax.parser.WhitespacesAndCommentsBinder
|
||||
- f:greedyRightBinder():com.intellij.platform.syntax.parser.WhitespacesAndCommentsBinder
|
||||
*:com.intellij.platform.syntax.tree.SyntaxNode
|
||||
- a:getEndOffset():I
|
||||
- a:getErrorMessage():java.lang.String
|
||||
- a:getFirstChild():com.intellij.platform.syntax.tree.SyntaxNode
|
||||
- a:getLastChild():com.intellij.platform.syntax.tree.SyntaxNode
|
||||
- a:getNextSibling():com.intellij.platform.syntax.tree.SyntaxNode
|
||||
- a:getParent():com.intellij.platform.syntax.tree.SyntaxNode
|
||||
- a:getPrevSibling():com.intellij.platform.syntax.tree.SyntaxNode
|
||||
- a:getStartOffset():I
|
||||
- a:getText():java.lang.CharSequence
|
||||
- a:getType():com.intellij.platform.syntax.SyntaxElementType
|
||||
*:com.intellij.platform.syntax.tree.SyntaxTree
|
||||
- a:getRoot():com.intellij.platform.syntax.tree.SyntaxNode
|
||||
- a:getText():java.lang.CharSequence
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
@file:ApiStatus.Experimental
|
||||
|
||||
package com.intellij.platform.syntax
|
||||
|
||||
import com.intellij.platform.syntax.lexer.TokenList
|
||||
import com.intellij.platform.syntax.parser.ProductionResult
|
||||
import com.intellij.platform.syntax.tree.SyntaxNode
|
||||
import com.intellij.platform.syntax.tree.SyntaxTree
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
/**
|
||||
* A parser that is attached to so-called chameleon nodes which allows to parse them lazily on demand.
|
||||
*
|
||||
* ### Implementation note:
|
||||
*
|
||||
* Provided [SyntaxTree] and [SyntaxNode] might be backed by different tree implementations,
|
||||
* depending on the syntax-lib client. So please don't make any assumptions on the actual types of the passed instances.
|
||||
*
|
||||
* If you want to add platform-specific code, introduce an extension point, see [com.intellij.platform.syntax.extensions.ExtensionSupport].
|
||||
*
|
||||
* @see parseLazyNode
|
||||
* @see tryReparseLazyNode
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
@ApiStatus.OverrideOnly
|
||||
interface LazyParser {
|
||||
/**
|
||||
* Called when the node is requested to be parsed.
|
||||
*
|
||||
* @return the result of the parsing operation
|
||||
*/
|
||||
fun parse(parsingContext: LazyParsingContext): ProductionResult
|
||||
|
||||
/**
|
||||
* Called when the node is requested to be reparsed.
|
||||
*
|
||||
* @return the result of the parsing operation or `null` if reparsing is not possible
|
||||
* (e.g., when braces got unbalanced in the next)
|
||||
*/
|
||||
fun tryReparse(parsingContext: LazyParsingContext): ProductionResult? = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given node and returns [ProductionResult].
|
||||
*
|
||||
* @see LazyParser.parse
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
fun parseLazyNode(parsingContext: LazyParsingContext): ProductionResult {
|
||||
return parsingContext.lazyParser.parse(parsingContext)
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to reparse the given node and returns [ProductionResult] if possible.
|
||||
*
|
||||
* @see LazyParser.tryReparse
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
fun tryReparseLazyNode(parsingContext: LazyParsingContext): ProductionResult? {
|
||||
return parsingContext.lazyParser.tryReparse(parsingContext)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tree the tree being parsed
|
||||
* @param node the node being parsed
|
||||
* @param text the text of the node being parsed
|
||||
* @param tokenList the token list being parsed. Might be missing if the parsing engine does not store this information.
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
class LazyParsingContext(
|
||||
val tree: SyntaxTree,
|
||||
val node: SyntaxNode,
|
||||
val text: CharSequence,
|
||||
val tokenList: TokenList?,
|
||||
) {
|
||||
internal val lazyParser: LazyParser
|
||||
get() = node.type.lazyParser ?: error("Node ${node} has non-lazy element type ${node.type}")
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import org.jetbrains.annotations.ApiStatus
|
||||
import kotlin.concurrent.atomics.AtomicInt
|
||||
import kotlin.concurrent.atomics.ExperimentalAtomicApi
|
||||
import kotlin.concurrent.atomics.fetchAndIncrement
|
||||
import kotlin.jvm.JvmOverloads
|
||||
|
||||
/**
|
||||
* A class defining a token or node type.
|
||||
@@ -17,10 +18,19 @@ import kotlin.concurrent.atomics.fetchAndIncrement
|
||||
@ApiStatus.Experimental
|
||||
class SyntaxElementType internal constructor(
|
||||
private val debugName: String,
|
||||
internal val lazyParser: LazyParser?,
|
||||
@Suppress("unused") unusedParam: Any?, // this parameter is necessary for disambiguation with the factory function
|
||||
) {
|
||||
val index: Int = counter.fetchAndIncrement()
|
||||
|
||||
/**
|
||||
* Checks if this element type is lazy-parseable.
|
||||
* For performing reparse, use [parseLazyNode] and [tryReparseLazyNode] functions.
|
||||
*
|
||||
* @return `true` if this element type is lazy-parseable.
|
||||
*/
|
||||
fun isLazyParseable(): Boolean = lazyParser != null
|
||||
|
||||
override fun toString(): String = debugName
|
||||
|
||||
override fun equals(other: Any?): Boolean = this === other
|
||||
@@ -29,9 +39,11 @@ class SyntaxElementType internal constructor(
|
||||
}
|
||||
|
||||
@ApiStatus.Experimental
|
||||
@JvmOverloads
|
||||
fun SyntaxElementType(
|
||||
debugName: String,
|
||||
lazyParser: LazyParser? = null,
|
||||
): SyntaxElementType =
|
||||
SyntaxElementType(debugName, null as Any?)
|
||||
SyntaxElementType(debugName, lazyParser, null as Any?)
|
||||
|
||||
private val counter = AtomicInt(0)
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.syntax.tree
|
||||
|
||||
import com.intellij.platform.syntax.SyntaxElementType
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
/**
|
||||
* API for a Syntax Tree.
|
||||
*
|
||||
* The tree is passed to [com.intellij.platform.syntax.LazyParser] as the context for parsing.
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
interface SyntaxTree {
|
||||
val text: CharSequence
|
||||
val root: SyntaxNode
|
||||
}
|
||||
|
||||
/**
|
||||
* API for a Syntax Node.
|
||||
*
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
interface SyntaxNode {
|
||||
val text: CharSequence
|
||||
|
||||
val type: SyntaxElementType
|
||||
|
||||
val startOffset: Int
|
||||
val endOffset: Int
|
||||
|
||||
val parent: SyntaxNode?
|
||||
|
||||
val prevSibling: SyntaxNode?
|
||||
val nextSibling: SyntaxNode?
|
||||
|
||||
val firstChild: SyntaxNode?
|
||||
val lastChild: SyntaxNode?
|
||||
|
||||
val errorMessage: String?
|
||||
}
|
||||
@@ -109,13 +109,14 @@
|
||||
- *sf:logger(java.lang.String):com.intellij.platform.syntax.Logger
|
||||
*f:com.intellij.platform.syntax.util.parser.SyntaxBuilderUtil
|
||||
- sf:INSTANCE:com.intellij.platform.syntax.util.parser.SyntaxBuilderUtil
|
||||
- f:advance(com.intellij.platform.syntax.parser.SyntaxTreeBuilder,I):V
|
||||
- f:drop(com.intellij.platform.syntax.parser.SyntaxTreeBuilder$Marker[]):V
|
||||
- f:expect(com.intellij.platform.syntax.parser.SyntaxTreeBuilder,com.intellij.platform.syntax.SyntaxElementType):Z
|
||||
- f:expect(com.intellij.platform.syntax.parser.SyntaxTreeBuilder,com.intellij.platform.syntax.SyntaxElementTypeSet):Z
|
||||
- sf:advance(com.intellij.platform.syntax.parser.SyntaxTreeBuilder,I):V
|
||||
- sf:drop(com.intellij.platform.syntax.parser.SyntaxTreeBuilder$Marker[]):V
|
||||
- sf:expect(com.intellij.platform.syntax.parser.SyntaxTreeBuilder,com.intellij.platform.syntax.SyntaxElementType):Z
|
||||
- sf:expect(com.intellij.platform.syntax.parser.SyntaxTreeBuilder,com.intellij.platform.syntax.SyntaxElementTypeSet):Z
|
||||
- sf:hasProperBraceBalance(com.intellij.platform.syntax.lexer.TokenList,com.intellij.platform.syntax.SyntaxElementType,com.intellij.platform.syntax.SyntaxElementType,com.intellij.platform.syntax.CancellationProvider):Z
|
||||
- sf:hasProperBraceBalance(java.lang.CharSequence,com.intellij.platform.syntax.lexer.Lexer,com.intellij.platform.syntax.SyntaxElementType,com.intellij.platform.syntax.SyntaxElementType,com.intellij.platform.syntax.CancellationProvider):Z
|
||||
- f:parseBlockLazy(com.intellij.platform.syntax.parser.SyntaxTreeBuilder,com.intellij.platform.syntax.SyntaxElementType,com.intellij.platform.syntax.SyntaxElementType,com.intellij.platform.syntax.SyntaxElementType):com.intellij.platform.syntax.parser.SyntaxTreeBuilder$Marker
|
||||
- f:rawTokenText(com.intellij.platform.syntax.parser.SyntaxTreeBuilder,I):java.lang.CharSequence
|
||||
- sf:parseBlockLazy(com.intellij.platform.syntax.parser.SyntaxTreeBuilder,com.intellij.platform.syntax.SyntaxElementType,com.intellij.platform.syntax.SyntaxElementType,com.intellij.platform.syntax.SyntaxElementType):com.intellij.platform.syntax.parser.SyntaxTreeBuilder$Marker
|
||||
- sf:rawTokenText(com.intellij.platform.syntax.parser.SyntaxTreeBuilder,I):java.lang.CharSequence
|
||||
*c:com.intellij.platform.syntax.util.parser.SyntaxTreeBuilderAdapter
|
||||
- com.intellij.platform.syntax.parser.SyntaxTreeBuilder
|
||||
- <init>(com.intellij.platform.syntax.parser.SyntaxTreeBuilder):V
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.intellij.platform.syntax.CancellationProvider
|
||||
import com.intellij.platform.syntax.SyntaxElementType
|
||||
import com.intellij.platform.syntax.SyntaxElementTypeSet
|
||||
import com.intellij.platform.syntax.lexer.Lexer
|
||||
import com.intellij.platform.syntax.lexer.TokenList
|
||||
import com.intellij.platform.syntax.parser.SyntaxTreeBuilder
|
||||
import com.intellij.platform.syntax.parser.WhitespacesBinders
|
||||
import com.intellij.util.text.CharSequenceSubSequence
|
||||
@@ -19,6 +20,7 @@ object SyntaxBuilderUtil {
|
||||
* @param this PSI builder to operate on.
|
||||
* @param count number of tokens to skip.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun SyntaxTreeBuilder.advance(count: Int) {
|
||||
repeat(count) {
|
||||
if (eof()) return
|
||||
@@ -34,6 +36,7 @@ object SyntaxBuilderUtil {
|
||||
* @param expectedType expected token.
|
||||
* @return true if token matches, false otherwise.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun SyntaxTreeBuilder.expect(expectedType: SyntaxElementType?): Boolean {
|
||||
if (tokenType === expectedType) {
|
||||
advanceLexer()
|
||||
@@ -49,6 +52,7 @@ object SyntaxBuilderUtil {
|
||||
* @param expectedTypes expected token types.
|
||||
* @return true if token matches, false otherwise.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun SyntaxTreeBuilder.expect(expectedTypes: SyntaxElementTypeSet): Boolean {
|
||||
if (tokenType in expectedTypes) {
|
||||
advanceLexer()
|
||||
@@ -62,12 +66,14 @@ object SyntaxBuilderUtil {
|
||||
*
|
||||
* @param markers markers to drop.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun drop(vararg markers: SyntaxTreeBuilder.Marker?) {
|
||||
for (marker in markers) {
|
||||
marker?.drop()
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun SyntaxTreeBuilder.rawTokenText(index: Int): CharSequence {
|
||||
return CharSequenceSubSequence(
|
||||
baseSequence = text,
|
||||
@@ -80,6 +86,7 @@ object SyntaxBuilderUtil {
|
||||
* tries to parse a code block with corresponding left and right braces.
|
||||
* @return collapsed marker of the block or `null` if there is no code block at all.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun SyntaxTreeBuilder.parseBlockLazy(
|
||||
leftBrace: SyntaxElementType,
|
||||
rightBrace: SyntaxElementType,
|
||||
@@ -114,8 +121,8 @@ object SyntaxBuilderUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if `text` looks like a proper block.
|
||||
* In particular it
|
||||
* Checks if [text] looks like a proper block.
|
||||
* In particular, it
|
||||
* (1) checks brace balance
|
||||
* (2) verifies that the block's closing brace is the last token
|
||||
*
|
||||
@@ -135,15 +142,59 @@ object SyntaxBuilderUtil {
|
||||
cancellationProvider: CancellationProvider?,
|
||||
): Boolean {
|
||||
lexer.start(text)
|
||||
return checkBraceBalance(
|
||||
leftBrace = leftBrace,
|
||||
rightBrace = rightBrace,
|
||||
cancellationProvider = cancellationProvider,
|
||||
advance = lexer::advance,
|
||||
curType = lexer::getTokenType
|
||||
)
|
||||
}
|
||||
|
||||
if (lexer.getTokenType() !== leftBrace) return false
|
||||
/**
|
||||
* Checks if [tokenList] looks like a proper block.
|
||||
* In particular, it
|
||||
* (1) checks brace balance
|
||||
* (2) verifies that the block's closing brace is the last token
|
||||
*
|
||||
* @param tokenList - tokens to check
|
||||
* @param leftBrace - left brace element type
|
||||
* @param rightBrace - right brace element type
|
||||
* @param cancellationProvider - a hook to stop operation if it's not necessary anymore
|
||||
* @return true if `text` passes the checks
|
||||
*/
|
||||
@JvmStatic
|
||||
fun hasProperBraceBalance(
|
||||
tokenList: TokenList,
|
||||
leftBrace: SyntaxElementType,
|
||||
rightBrace: SyntaxElementType,
|
||||
cancellationProvider: CancellationProvider?,
|
||||
): Boolean {
|
||||
var i = 0
|
||||
return checkBraceBalance(
|
||||
leftBrace = leftBrace,
|
||||
rightBrace = rightBrace,
|
||||
cancellationProvider = cancellationProvider,
|
||||
advance = { i++ },
|
||||
curType = { tokenList.getTokenType(i) }
|
||||
)
|
||||
}
|
||||
|
||||
private inline fun checkBraceBalance(
|
||||
leftBrace: SyntaxElementType,
|
||||
rightBrace: SyntaxElementType,
|
||||
cancellationProvider: CancellationProvider?,
|
||||
advance: () -> Unit,
|
||||
curType: () -> SyntaxElementType?,
|
||||
): Boolean {
|
||||
if (curType() !== leftBrace) return false
|
||||
advance()
|
||||
|
||||
lexer.advance()
|
||||
var balance = 1
|
||||
|
||||
while (true) {
|
||||
cancellationProvider?.checkCancelled()
|
||||
val type = lexer.getTokenType()
|
||||
val type = curType()
|
||||
|
||||
if (type == null) {
|
||||
//eof: checking balance
|
||||
@@ -162,7 +213,7 @@ object SyntaxBuilderUtil {
|
||||
balance--
|
||||
}
|
||||
|
||||
lexer.advance()
|
||||
advance()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user