Compare commits
No commits in common. "2d2a74656c21d35556b020a17fb3d396af902e68" and "017111827d49295bd8ef365b8d4a137074fb0fbf" have entirely different histories.
2d2a74656c
...
017111827d
@ -21,12 +21,6 @@
|
|||||||
- MI Satisfaction: Abstract requirements are automatically satisfied by matching concrete members found later in the C3 MRO chain without requiring explicit proxy methods.
|
- MI Satisfaction: Abstract requirements are automatically satisfied by matching concrete members found later in the C3 MRO chain without requiring explicit proxy methods.
|
||||||
- Integration: Updated highlighters (lynglib, lyngweb, IDEA plugin), IDEA completion, and Grazie grammar checking.
|
- Integration: Updated highlighters (lynglib, lyngweb, IDEA plugin), IDEA completion, and Grazie grammar checking.
|
||||||
- Documentation: Updated `docs/OOP.md` with sections on "Abstract Classes and Members", "Interfaces", and "Overriding and Virtual Dispatch".
|
- Documentation: Updated `docs/OOP.md` with sections on "Abstract Classes and Members", "Interfaces", and "Overriding and Virtual Dispatch".
|
||||||
- IDEA plugin: Improved natural language support and spellchecking
|
|
||||||
- Disabled the limited built-in English and Technical dictionaries.
|
|
||||||
- Enforced usage of the platform's standard Natural Languages (Grazie) and Spellchecker components.
|
|
||||||
- Integrated `SpellCheckerManager` for word suggestions and validation, respecting users' personal and project dictionaries.
|
|
||||||
- Added project-specific "learned words" support via `Lyng Formatter` settings and quick-fixes.
|
|
||||||
- Enhanced fallback spellchecker for technical terms and Lyng-specific vocabulary.
|
|
||||||
|
|
||||||
- Language: Class properties with accessors
|
- Language: Class properties with accessors
|
||||||
- Support for `val` (read-only) and `var` (read-write) properties in classes.
|
- Support for `val` (read-only) and `var` (read-write) properties in classes.
|
||||||
|
|||||||
@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package net.sergeych.lyng.idea.grazie
|
||||||
|
|
||||||
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
|
import java.io.BufferedReader
|
||||||
|
import java.io.InputStreamReader
|
||||||
|
import java.util.zip.GZIPInputStream
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Very simple English dictionary loader for offline suggestions on IC-243.
|
||||||
|
* It loads a word list from classpath resources. Supports plain text (one word per line)
|
||||||
|
* and gzipped text if the resource ends with .gz.
|
||||||
|
*/
|
||||||
|
object EnglishDictionary {
|
||||||
|
private val log = Logger.getInstance(EnglishDictionary::class.java)
|
||||||
|
|
||||||
|
@Volatile private var loaded = false
|
||||||
|
@Volatile private var words: Set<String> = emptySet()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load dictionary from bundled resources (once).
|
||||||
|
* If multiple candidates exist, the first found is used.
|
||||||
|
*/
|
||||||
|
private fun ensureLoaded() {
|
||||||
|
if (loaded) return
|
||||||
|
synchronized(this) {
|
||||||
|
if (loaded) return
|
||||||
|
val candidates = listOf(
|
||||||
|
// preferred large bundles first (add en-basic.txt.gz ~3–5MB here)
|
||||||
|
"/dictionaries/en-basic.txt.gz",
|
||||||
|
"/dictionaries/en-large.txt.gz",
|
||||||
|
// plain text fallbacks
|
||||||
|
"/dictionaries/en-basic.txt",
|
||||||
|
"/dictionaries/en-large.txt",
|
||||||
|
)
|
||||||
|
val merged = HashSet<String>(128_000)
|
||||||
|
for (res in candidates) {
|
||||||
|
try {
|
||||||
|
val stream = javaClass.getResourceAsStream(res) ?: continue
|
||||||
|
val reader = if (res.endsWith(".gz"))
|
||||||
|
BufferedReader(InputStreamReader(GZIPInputStream(stream)))
|
||||||
|
else
|
||||||
|
BufferedReader(InputStreamReader(stream))
|
||||||
|
var loadedCount = 0
|
||||||
|
reader.useLines { seq -> seq.forEach { line ->
|
||||||
|
val w = line.trim()
|
||||||
|
if (w.isNotEmpty() && !w.startsWith("#")) { merged += w.lowercase(); loadedCount++ }
|
||||||
|
} }
|
||||||
|
log.info("EnglishDictionary: loaded $loadedCount words from $res (total=${merged.size})")
|
||||||
|
} catch (t: Throwable) {
|
||||||
|
log.info("EnglishDictionary: failed to load $res: ${t.javaClass.simpleName}: ${t.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (merged.isEmpty()) {
|
||||||
|
// Fallback minimal set
|
||||||
|
merged += setOf("comment","comments","error","errors","found","file","not","word","words","count","value","name","class","function","string")
|
||||||
|
log.info("EnglishDictionary: using minimal built-in set (${merged.size})")
|
||||||
|
}
|
||||||
|
words = merged
|
||||||
|
loaded = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun allWords(): Set<String> {
|
||||||
|
ensureLoaded()
|
||||||
|
return words
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -402,8 +402,6 @@ class LyngGrazieAnnotator : ExternalAnnotator<LyngGrazieAnnotator.Input, LyngGra
|
|||||||
var painted = 0
|
var painted = 0
|
||||||
val docText = file.viewProvider.document?.text ?: return 0
|
val docText = file.viewProvider.document?.text ?: return 0
|
||||||
val tokenRegex = Regex("[A-Za-z][A-Za-z0-9_']{2,}")
|
val tokenRegex = Regex("[A-Za-z][A-Za-z0-9_']{2,}")
|
||||||
val settings = LyngFormatterSettings.getInstance(file.project)
|
|
||||||
val learned = settings.learnedWords
|
|
||||||
for ((content, hostRange) in fragments) {
|
for ((content, hostRange) in fragments) {
|
||||||
val text = try { docText.substring(hostRange.startOffset, hostRange.endOffset) } catch (_: Throwable) { null } ?: continue
|
val text = try { docText.substring(hostRange.startOffset, hostRange.endOffset) } catch (_: Throwable) { null } ?: continue
|
||||||
var seen = 0
|
var seen = 0
|
||||||
@ -415,7 +413,7 @@ class LyngGrazieAnnotator : ExternalAnnotator<LyngGrazieAnnotator.Input, LyngGra
|
|||||||
val parts = splitIdentifier(token)
|
val parts = splitIdentifier(token)
|
||||||
for (part in parts) {
|
for (part in parts) {
|
||||||
if (part.length <= 2) continue
|
if (part.length <= 2) continue
|
||||||
if (isAllowedWord(part, learned)) continue
|
if (isAllowedWord(part)) continue
|
||||||
|
|
||||||
// Map part back to original token occurrence within this hostRange
|
// Map part back to original token occurrence within this hostRange
|
||||||
val localStart = m.range.first + token.indexOf(part)
|
val localStart = m.range.first + token.indexOf(part)
|
||||||
@ -453,8 +451,6 @@ class LyngGrazieAnnotator : ExternalAnnotator<LyngGrazieAnnotator.Input, LyngGra
|
|||||||
var painted = 0
|
var painted = 0
|
||||||
val docText = file.viewProvider.document?.text
|
val docText = file.viewProvider.document?.text
|
||||||
val tokenRegex = Regex("[A-Za-z][A-Za-z0-9_']{2,}")
|
val tokenRegex = Regex("[A-Za-z][A-Za-z0-9_']{2,}")
|
||||||
val settings = LyngFormatterSettings.getInstance(file.project)
|
|
||||||
val learned = settings.learnedWords
|
|
||||||
val baseWords = setOf(
|
val baseWords = setOf(
|
||||||
// small, common vocabulary to catch near-miss typos in typical code/comments
|
// small, common vocabulary to catch near-miss typos in typical code/comments
|
||||||
"comment","comments","error","errors","found","file","not","word","words","count","value","name","class","function","string"
|
"comment","comments","error","errors","found","file","not","word","words","count","value","name","class","function","string"
|
||||||
@ -473,7 +469,7 @@ class LyngGrazieAnnotator : ExternalAnnotator<LyngGrazieAnnotator.Input, LyngGra
|
|||||||
for (part in parts) {
|
for (part in parts) {
|
||||||
seen++
|
seen++
|
||||||
val lower = part.lowercase()
|
val lower = part.lowercase()
|
||||||
if (lower.length <= 2 || isAllowedWord(part, learned)) continue
|
if (lower.length <= 2 || isAllowedWord(part)) continue
|
||||||
|
|
||||||
val localStart = m.range.first + token.indexOf(part)
|
val localStart = m.range.first + token.indexOf(part)
|
||||||
val localEnd = localStart + part.length
|
val localEnd = localStart + part.length
|
||||||
@ -544,32 +540,29 @@ class LyngGrazieAnnotator : ExternalAnnotator<LyngGrazieAnnotator.Input, LyngGra
|
|||||||
private fun suggestReplacements(file: PsiFile, word: String): List<String> {
|
private fun suggestReplacements(file: PsiFile, word: String): List<String> {
|
||||||
val lower = word.lowercase()
|
val lower = word.lowercase()
|
||||||
val fromProject = collectProjectWords(file)
|
val fromProject = collectProjectWords(file)
|
||||||
|
val fromTech = TechDictionary.allWords()
|
||||||
val fromSpellChecker = try {
|
val fromEnglish = EnglishDictionary.allWords()
|
||||||
val mgrCls = Class.forName("com.intellij.spellchecker.SpellCheckerManager")
|
// Merge with priority: project (p=0), tech (p=1), english (p=2)
|
||||||
val getInstance = mgrCls.methods.firstOrNull { it.name == "getInstance" && it.parameterCount == 1 }
|
|
||||||
val getSuggestions = mgrCls.methods.firstOrNull { it.name == "getSuggestions" && it.parameterCount == 1 && it.parameterTypes[0] == String::class.java }
|
|
||||||
val mgr = getInstance?.invoke(null, file.project)
|
|
||||||
if (mgr != null && getSuggestions != null) {
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
getSuggestions.invoke(mgr, word) as? List<String>
|
|
||||||
} else null
|
|
||||||
} catch (_: Throwable) {
|
|
||||||
null
|
|
||||||
} ?: emptyList()
|
|
||||||
|
|
||||||
// Merge with priority: project (p=0), spellchecker (p=1)
|
|
||||||
val all = LinkedHashSet<String>()
|
val all = LinkedHashSet<String>()
|
||||||
// Add project words that are close enough
|
all.addAll(fromProject)
|
||||||
for (w in fromProject) {
|
all.addAll(fromTech)
|
||||||
|
all.addAll(fromEnglish)
|
||||||
|
data class Cand(val w: String, val d: Int, val p: Int)
|
||||||
|
val cands = ArrayList<Cand>(32)
|
||||||
|
for (w in all) {
|
||||||
if (w == lower) continue
|
if (w == lower) continue
|
||||||
if (kotlin.math.abs(w.length - lower.length) <= 2 && editDistance(lower, w) <= 2) {
|
if (kotlin.math.abs(w.length - lower.length) > 2) continue
|
||||||
all.add(w)
|
val d = editDistance(lower, w)
|
||||||
|
val p = when {
|
||||||
|
w in fromProject -> 0
|
||||||
|
w in fromTech -> 1
|
||||||
|
else -> 2
|
||||||
}
|
}
|
||||||
|
cands += Cand(w, d, p)
|
||||||
}
|
}
|
||||||
all.addAll(fromSpellChecker)
|
cands.sortWith(compareBy<Cand> { it.d }.thenBy { it.p }.thenBy { it.w })
|
||||||
|
// Return a larger pool so callers can choose desired display count
|
||||||
return all.take(16).toList()
|
return cands.take(16).map { it.w }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun collectProjectWords(file: PsiFile): Set<String> {
|
private fun collectProjectWords(file: PsiFile): Set<String> {
|
||||||
@ -596,19 +589,14 @@ class LyngGrazieAnnotator : ExternalAnnotator<LyngGrazieAnnotator.Input, LyngGra
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isAllowedWord(w: String, learnedWords: Set<String> = emptySet()): Boolean {
|
private fun isAllowedWord(w: String): Boolean {
|
||||||
val s = w.lowercase()
|
val s = w.lowercase()
|
||||||
if (s in learnedWords) return true
|
|
||||||
return s in setOf(
|
return s in setOf(
|
||||||
// common code words / language keywords to avoid noise
|
// common code words / language keywords to avoid noise
|
||||||
"val","var","fun","class","interface","enum","type","import","package","return","if","else","when","while","for","try","catch","finally","true","false","null",
|
"val","var","fun","class","interface","enum","type","import","package","return","if","else","when","while","for","try","catch","finally","true","false","null",
|
||||||
"abstract","closed","override",
|
"abstract","closed","override",
|
||||||
// very common English words
|
// very common English words
|
||||||
"the","and","or","not","with","from","into","this","that","file","found","count","name","value","object",
|
"the","and","or","not","with","from","into","this","that","file","found","count","name","value","object"
|
||||||
// Lyng technical/vocabulary words formerly in TechDictionary
|
|
||||||
"lyng","miniast","binder","printf","specifier","specifiers","regex","token","tokens",
|
|
||||||
"identifier","identifiers","keyword","keywords","comment","comments","string","strings",
|
|
||||||
"literal","literals","formatting","formatter","grazie","typo","typos","dictionary","dictionaries"
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package net.sergeych.lyng.idea.grazie
|
||||||
|
|
||||||
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
|
import java.io.BufferedReader
|
||||||
|
import java.io.InputStreamReader
|
||||||
|
import java.util.zip.GZIPInputStream
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lightweight technical/Lyng vocabulary dictionary.
|
||||||
|
* Loaded from classpath resources; supports .txt and .txt.gz. Merged with EnglishDictionary.
|
||||||
|
*/
|
||||||
|
object TechDictionary {
|
||||||
|
private val log = Logger.getInstance(TechDictionary::class.java)
|
||||||
|
@Volatile private var loaded = false
|
||||||
|
@Volatile private var words: Set<String> = emptySet()
|
||||||
|
|
||||||
|
private fun ensureLoaded() {
|
||||||
|
if (loaded) return
|
||||||
|
synchronized(this) {
|
||||||
|
if (loaded) return
|
||||||
|
val candidates = listOf(
|
||||||
|
"/dictionaries/tech-lyng.txt.gz",
|
||||||
|
"/dictionaries/tech-lyng.txt"
|
||||||
|
)
|
||||||
|
val merged = HashSet<String>(8_000)
|
||||||
|
for (res in candidates) {
|
||||||
|
try {
|
||||||
|
val stream = javaClass.getResourceAsStream(res) ?: continue
|
||||||
|
val reader = if (res.endsWith(".gz"))
|
||||||
|
BufferedReader(InputStreamReader(GZIPInputStream(stream)))
|
||||||
|
else
|
||||||
|
BufferedReader(InputStreamReader(stream))
|
||||||
|
var n = 0
|
||||||
|
reader.useLines { seq -> seq.forEach { line ->
|
||||||
|
val w = line.trim()
|
||||||
|
if (w.isNotEmpty() && !w.startsWith("#")) { merged += w.lowercase(); n++ }
|
||||||
|
} }
|
||||||
|
log.info("TechDictionary: loaded $n words from $res (total=${merged.size})")
|
||||||
|
} catch (t: Throwable) {
|
||||||
|
log.info("TechDictionary: failed to load $res: ${t.javaClass.simpleName}: ${t.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (merged.isEmpty()) {
|
||||||
|
merged += setOf(
|
||||||
|
// minimal Lyng/tech seeding to avoid empty dictionary
|
||||||
|
"lyng","miniast","binder","printf","specifier","specifiers","regex","token","tokens",
|
||||||
|
"identifier","identifiers","keyword","keywords","comment","comments","string","strings",
|
||||||
|
"literal","literals","formatting","formatter","grazie","typo","typos","dictionary","dictionaries"
|
||||||
|
)
|
||||||
|
log.info("TechDictionary: using minimal built-in set (${merged.size})")
|
||||||
|
}
|
||||||
|
words = merged
|
||||||
|
loaded = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun allWords(): Set<String> {
|
||||||
|
ensureLoaded()
|
||||||
|
return words
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -20,14 +20,13 @@ package net.sergeych.lyng.idea.navigation
|
|||||||
import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler
|
import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import net.sergeych.lyng.idea.LyngLanguage
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensures Ctrl+B (Go to Definition) works on Lyng identifiers by resolving through LyngPsiReference.
|
* Ensures Ctrl+B (Go to Definition) works on Lyng identifiers by resolving through LyngPsiReference.
|
||||||
*/
|
*/
|
||||||
class LyngGotoDeclarationHandler : GotoDeclarationHandler {
|
class LyngGotoDeclarationHandler : GotoDeclarationHandler {
|
||||||
override fun getGotoDeclarationTargets(sourceElement: PsiElement?, offset: Int, editor: Editor?): Array<PsiElement>? {
|
override fun getGotoDeclarationTargets(sourceElement: PsiElement?, offset: Int, editor: Editor?): Array<PsiElement>? {
|
||||||
if (sourceElement == null || sourceElement.language != LyngLanguage) return null
|
if (sourceElement == null) return null
|
||||||
|
|
||||||
val allTargets = mutableListOf<PsiElement>()
|
val allTargets = mutableListOf<PsiElement>()
|
||||||
|
|
||||||
|
|||||||
@ -38,18 +38,16 @@ class LyngPsiReference(element: PsiElement) : PsiPolyVariantReferenceBase<PsiEle
|
|||||||
|
|
||||||
val mini = LyngAstManager.getMiniAst(file) ?: return emptyArray()
|
val mini = LyngAstManager.getMiniAst(file) ?: return emptyArray()
|
||||||
val binding = LyngAstManager.getBinding(file)
|
val binding = LyngAstManager.getBinding(file)
|
||||||
val imported = DocLookupUtils.canonicalImportedModules(mini, text).toSet()
|
|
||||||
val currentPackage = getPackageName(file)
|
|
||||||
val allowedPackages = if (currentPackage != null) imported + currentPackage else imported
|
|
||||||
|
|
||||||
// 1. Member resolution (obj.member)
|
// 1. Member resolution (obj.member)
|
||||||
val dotPos = TextCtx.findDotLeft(text, offset)
|
val dotPos = TextCtx.findDotLeft(text, offset)
|
||||||
if (dotPos != null) {
|
if (dotPos != null) {
|
||||||
val receiverClass = DocLookupUtils.guessReceiverClassViaMini(mini, text, dotPos, imported.toList(), binding)
|
val imported = DocLookupUtils.canonicalImportedModules(mini, text)
|
||||||
?: DocLookupUtils.guessReceiverClass(text, dotPos, imported.toList(), mini)
|
val receiverClass = DocLookupUtils.guessReceiverClassViaMini(mini, text, dotPos, imported, binding)
|
||||||
|
?: DocLookupUtils.guessReceiverClass(text, dotPos, imported, mini)
|
||||||
|
|
||||||
if (receiverClass != null) {
|
if (receiverClass != null) {
|
||||||
val resolved = DocLookupUtils.resolveMemberWithInheritance(imported.toList(), receiverClass, name, mini)
|
val resolved = DocLookupUtils.resolveMemberWithInheritance(imported, receiverClass, name, mini)
|
||||||
if (resolved != null) {
|
if (resolved != null) {
|
||||||
val owner = resolved.first
|
val owner = resolved.first
|
||||||
val member = resolved.second
|
val member = resolved.second
|
||||||
@ -77,7 +75,7 @@ class LyngPsiReference(element: PsiElement) : PsiPolyVariantReferenceBase<PsiEle
|
|||||||
}
|
}
|
||||||
// If we couldn't resolve exactly, we might still want to search globally but ONLY for members
|
// If we couldn't resolve exactly, we might still want to search globally but ONLY for members
|
||||||
if (results.isEmpty()) {
|
if (results.isEmpty()) {
|
||||||
results.addAll(resolveGlobally(file.project, name, membersOnly = true, allowedPackages = allowedPackages))
|
results.addAll(resolveGlobally(file.project, name, membersOnly = true))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 2. Local resolution via Binder
|
// 2. Local resolution via Binder
|
||||||
@ -96,7 +94,7 @@ class LyngPsiReference(element: PsiElement) : PsiPolyVariantReferenceBase<PsiEle
|
|||||||
// 3. Global project scan
|
// 3. Global project scan
|
||||||
// Only search globally if we haven't found a strong local match
|
// Only search globally if we haven't found a strong local match
|
||||||
if (results.isEmpty()) {
|
if (results.isEmpty()) {
|
||||||
results.addAll(resolveGlobally(file.project, name, allowedPackages = allowedPackages))
|
results.addAll(resolveGlobally(file.project, name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,16 +141,6 @@ class LyngPsiReference(element: PsiElement) : PsiPolyVariantReferenceBase<PsiEle
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getPackageName(file: PsiFile): String? {
|
|
||||||
val mini = LyngAstManager.getMiniAst(file) ?: return null
|
|
||||||
return try {
|
|
||||||
val pkg = mini.range.start.source.extractPackageName()
|
|
||||||
if (pkg.startsWith("lyng.")) pkg else "lyng.$pkg"
|
|
||||||
} catch (e: Exception) {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun resolve(): PsiElement? {
|
override fun resolve(): PsiElement? {
|
||||||
val results = multiResolve(false)
|
val results = multiResolve(false)
|
||||||
if (results.isEmpty()) return null
|
if (results.isEmpty()) return null
|
||||||
@ -166,20 +154,13 @@ class LyngPsiReference(element: PsiElement) : PsiPolyVariantReferenceBase<PsiEle
|
|||||||
return target
|
return target
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun resolveGlobally(project: Project, name: String, membersOnly: Boolean = false, allowedPackages: Set<String>? = null): List<ResolveResult> {
|
private fun resolveGlobally(project: Project, name: String, membersOnly: Boolean = false): List<ResolveResult> {
|
||||||
val results = mutableListOf<ResolveResult>()
|
val results = mutableListOf<ResolveResult>()
|
||||||
val files = FilenameIndex.getAllFilesByExt(project, "lyng", GlobalSearchScope.projectScope(project))
|
val files = FilenameIndex.getAllFilesByExt(project, "lyng", GlobalSearchScope.projectScope(project))
|
||||||
val psiManager = PsiManager.getInstance(project)
|
val psiManager = PsiManager.getInstance(project)
|
||||||
|
|
||||||
for (vFile in files) {
|
for (vFile in files) {
|
||||||
val file = psiManager.findFile(vFile) ?: continue
|
val file = psiManager.findFile(vFile) ?: continue
|
||||||
|
|
||||||
// Filter by package if requested
|
|
||||||
if (allowedPackages != null) {
|
|
||||||
val pkg = getPackageName(file)
|
|
||||||
if (pkg == null || pkg !in allowedPackages) continue
|
|
||||||
}
|
|
||||||
|
|
||||||
val mini = LyngAstManager.getMiniAst(file) ?: continue
|
val mini = LyngAstManager.getMiniAst(file) ?: continue
|
||||||
val src = mini.range.start.source
|
val src = mini.range.start.source
|
||||||
|
|
||||||
|
|||||||
466
lyng-idea/src/main/resources/dictionaries/en-basic.txt
Normal file
466
lyng-idea/src/main/resources/dictionaries/en-basic.txt
Normal file
@ -0,0 +1,466 @@
|
|||||||
|
the
|
||||||
|
be
|
||||||
|
to
|
||||||
|
of
|
||||||
|
and
|
||||||
|
a
|
||||||
|
in
|
||||||
|
that
|
||||||
|
have
|
||||||
|
I
|
||||||
|
it
|
||||||
|
for
|
||||||
|
not
|
||||||
|
on
|
||||||
|
with
|
||||||
|
he
|
||||||
|
as
|
||||||
|
you
|
||||||
|
do
|
||||||
|
at
|
||||||
|
this
|
||||||
|
but
|
||||||
|
his
|
||||||
|
by
|
||||||
|
from
|
||||||
|
they
|
||||||
|
we
|
||||||
|
say
|
||||||
|
her
|
||||||
|
she
|
||||||
|
or
|
||||||
|
an
|
||||||
|
will
|
||||||
|
my
|
||||||
|
one
|
||||||
|
all
|
||||||
|
would
|
||||||
|
there
|
||||||
|
their
|
||||||
|
what
|
||||||
|
so
|
||||||
|
up
|
||||||
|
out
|
||||||
|
if
|
||||||
|
about
|
||||||
|
who
|
||||||
|
get
|
||||||
|
which
|
||||||
|
go
|
||||||
|
me
|
||||||
|
when
|
||||||
|
make
|
||||||
|
can
|
||||||
|
like
|
||||||
|
time
|
||||||
|
no
|
||||||
|
just
|
||||||
|
him
|
||||||
|
know
|
||||||
|
take
|
||||||
|
people
|
||||||
|
into
|
||||||
|
year
|
||||||
|
your
|
||||||
|
good
|
||||||
|
some
|
||||||
|
could
|
||||||
|
them
|
||||||
|
see
|
||||||
|
other
|
||||||
|
than
|
||||||
|
then
|
||||||
|
now
|
||||||
|
look
|
||||||
|
only
|
||||||
|
come
|
||||||
|
its
|
||||||
|
over
|
||||||
|
think
|
||||||
|
also
|
||||||
|
back
|
||||||
|
after
|
||||||
|
use
|
||||||
|
two
|
||||||
|
how
|
||||||
|
our
|
||||||
|
work
|
||||||
|
first
|
||||||
|
well
|
||||||
|
way
|
||||||
|
even
|
||||||
|
new
|
||||||
|
want
|
||||||
|
because
|
||||||
|
any
|
||||||
|
these
|
||||||
|
give
|
||||||
|
day
|
||||||
|
most
|
||||||
|
us
|
||||||
|
is
|
||||||
|
are
|
||||||
|
was
|
||||||
|
were
|
||||||
|
been
|
||||||
|
being
|
||||||
|
does
|
||||||
|
did
|
||||||
|
done
|
||||||
|
has
|
||||||
|
had
|
||||||
|
having
|
||||||
|
may
|
||||||
|
might
|
||||||
|
must
|
||||||
|
shall
|
||||||
|
should
|
||||||
|
ought
|
||||||
|
need
|
||||||
|
used
|
||||||
|
here
|
||||||
|
therefore
|
||||||
|
where
|
||||||
|
why
|
||||||
|
while
|
||||||
|
until
|
||||||
|
since
|
||||||
|
before
|
||||||
|
afterward
|
||||||
|
between
|
||||||
|
among
|
||||||
|
without
|
||||||
|
within
|
||||||
|
through
|
||||||
|
across
|
||||||
|
against
|
||||||
|
toward
|
||||||
|
upon
|
||||||
|
above
|
||||||
|
below
|
||||||
|
under
|
||||||
|
around
|
||||||
|
near
|
||||||
|
far
|
||||||
|
early
|
||||||
|
late
|
||||||
|
often
|
||||||
|
always
|
||||||
|
never
|
||||||
|
seldom
|
||||||
|
sometimes
|
||||||
|
usually
|
||||||
|
really
|
||||||
|
very
|
||||||
|
quite
|
||||||
|
rather
|
||||||
|
almost
|
||||||
|
already
|
||||||
|
again
|
||||||
|
still
|
||||||
|
yet
|
||||||
|
soon
|
||||||
|
today
|
||||||
|
tomorrow
|
||||||
|
yesterday
|
||||||
|
number
|
||||||
|
string
|
||||||
|
boolean
|
||||||
|
true
|
||||||
|
false
|
||||||
|
null
|
||||||
|
none
|
||||||
|
file
|
||||||
|
files
|
||||||
|
path
|
||||||
|
paths
|
||||||
|
line
|
||||||
|
lines
|
||||||
|
word
|
||||||
|
words
|
||||||
|
count
|
||||||
|
value
|
||||||
|
values
|
||||||
|
name
|
||||||
|
names
|
||||||
|
title
|
||||||
|
text
|
||||||
|
message
|
||||||
|
error
|
||||||
|
errors
|
||||||
|
warning
|
||||||
|
warnings
|
||||||
|
info
|
||||||
|
information
|
||||||
|
debug
|
||||||
|
trace
|
||||||
|
format
|
||||||
|
printf
|
||||||
|
specifier
|
||||||
|
specifiers
|
||||||
|
pattern
|
||||||
|
patterns
|
||||||
|
match
|
||||||
|
matches
|
||||||
|
regex
|
||||||
|
version
|
||||||
|
versions
|
||||||
|
module
|
||||||
|
modules
|
||||||
|
package
|
||||||
|
packages
|
||||||
|
import
|
||||||
|
imports
|
||||||
|
export
|
||||||
|
exports
|
||||||
|
class
|
||||||
|
classes
|
||||||
|
object
|
||||||
|
objects
|
||||||
|
function
|
||||||
|
functions
|
||||||
|
method
|
||||||
|
methods
|
||||||
|
parameter
|
||||||
|
parameters
|
||||||
|
argument
|
||||||
|
arguments
|
||||||
|
variable
|
||||||
|
variables
|
||||||
|
constant
|
||||||
|
constants
|
||||||
|
type
|
||||||
|
types
|
||||||
|
generic
|
||||||
|
generics
|
||||||
|
map
|
||||||
|
maps
|
||||||
|
list
|
||||||
|
lists
|
||||||
|
array
|
||||||
|
arrays
|
||||||
|
set
|
||||||
|
sets
|
||||||
|
queue
|
||||||
|
stack
|
||||||
|
graph
|
||||||
|
tree
|
||||||
|
node
|
||||||
|
nodes
|
||||||
|
edge
|
||||||
|
edges
|
||||||
|
pair
|
||||||
|
pairs
|
||||||
|
key
|
||||||
|
keys
|
||||||
|
value
|
||||||
|
values
|
||||||
|
index
|
||||||
|
indices
|
||||||
|
length
|
||||||
|
size
|
||||||
|
empty
|
||||||
|
contains
|
||||||
|
equals
|
||||||
|
compare
|
||||||
|
greater
|
||||||
|
less
|
||||||
|
minimum
|
||||||
|
maximum
|
||||||
|
average
|
||||||
|
sum
|
||||||
|
total
|
||||||
|
random
|
||||||
|
round
|
||||||
|
floor
|
||||||
|
ceil
|
||||||
|
sin
|
||||||
|
cos
|
||||||
|
tan
|
||||||
|
sqrt
|
||||||
|
abs
|
||||||
|
min
|
||||||
|
max
|
||||||
|
read
|
||||||
|
write
|
||||||
|
open
|
||||||
|
close
|
||||||
|
append
|
||||||
|
create
|
||||||
|
delete
|
||||||
|
remove
|
||||||
|
update
|
||||||
|
save
|
||||||
|
load
|
||||||
|
start
|
||||||
|
stop
|
||||||
|
run
|
||||||
|
execute
|
||||||
|
return
|
||||||
|
break
|
||||||
|
continue
|
||||||
|
try
|
||||||
|
catch
|
||||||
|
finally
|
||||||
|
throw
|
||||||
|
throws
|
||||||
|
if
|
||||||
|
else
|
||||||
|
when
|
||||||
|
while
|
||||||
|
for
|
||||||
|
loop
|
||||||
|
range
|
||||||
|
case
|
||||||
|
switch
|
||||||
|
default
|
||||||
|
optional
|
||||||
|
required
|
||||||
|
enable
|
||||||
|
disable
|
||||||
|
enabled
|
||||||
|
disabled
|
||||||
|
visible
|
||||||
|
hidden
|
||||||
|
public
|
||||||
|
private
|
||||||
|
protected
|
||||||
|
internal
|
||||||
|
external
|
||||||
|
inline
|
||||||
|
override
|
||||||
|
abstract
|
||||||
|
sealed
|
||||||
|
open
|
||||||
|
final
|
||||||
|
static
|
||||||
|
const
|
||||||
|
lazy
|
||||||
|
late
|
||||||
|
init
|
||||||
|
initialize
|
||||||
|
configuration
|
||||||
|
settings
|
||||||
|
option
|
||||||
|
options
|
||||||
|
preference
|
||||||
|
preferences
|
||||||
|
project
|
||||||
|
projects
|
||||||
|
module
|
||||||
|
modules
|
||||||
|
build
|
||||||
|
builds
|
||||||
|
compile
|
||||||
|
compiles
|
||||||
|
compiler
|
||||||
|
test
|
||||||
|
tests
|
||||||
|
testing
|
||||||
|
assert
|
||||||
|
assertion
|
||||||
|
result
|
||||||
|
results
|
||||||
|
success
|
||||||
|
failure
|
||||||
|
status
|
||||||
|
state
|
||||||
|
context
|
||||||
|
scope
|
||||||
|
scopes
|
||||||
|
token
|
||||||
|
tokens
|
||||||
|
identifier
|
||||||
|
identifiers
|
||||||
|
keyword
|
||||||
|
keywords
|
||||||
|
comment
|
||||||
|
comments
|
||||||
|
string
|
||||||
|
strings
|
||||||
|
literal
|
||||||
|
literals
|
||||||
|
formatting
|
||||||
|
formatter
|
||||||
|
spell
|
||||||
|
spelling
|
||||||
|
dictionary
|
||||||
|
dictionaries
|
||||||
|
language
|
||||||
|
languages
|
||||||
|
natural
|
||||||
|
grazie
|
||||||
|
typo
|
||||||
|
typos
|
||||||
|
suggest
|
||||||
|
suggestion
|
||||||
|
suggestions
|
||||||
|
replace
|
||||||
|
replacement
|
||||||
|
replacements
|
||||||
|
learn
|
||||||
|
learned
|
||||||
|
learns
|
||||||
|
filter
|
||||||
|
filters
|
||||||
|
exclude
|
||||||
|
excludes
|
||||||
|
include
|
||||||
|
includes
|
||||||
|
bundle
|
||||||
|
bundled
|
||||||
|
resource
|
||||||
|
resources
|
||||||
|
gzipped
|
||||||
|
plain
|
||||||
|
text
|
||||||
|
editor
|
||||||
|
editors
|
||||||
|
inspection
|
||||||
|
inspections
|
||||||
|
highlight
|
||||||
|
highlighting
|
||||||
|
underline
|
||||||
|
underlines
|
||||||
|
style
|
||||||
|
styles
|
||||||
|
range
|
||||||
|
ranges
|
||||||
|
offset
|
||||||
|
offsets
|
||||||
|
position
|
||||||
|
positions
|
||||||
|
apply
|
||||||
|
applies
|
||||||
|
provides
|
||||||
|
present
|
||||||
|
absent
|
||||||
|
available
|
||||||
|
unavailable
|
||||||
|
version
|
||||||
|
build
|
||||||
|
platform
|
||||||
|
ide
|
||||||
|
intellij
|
||||||
|
plugin
|
||||||
|
plugins
|
||||||
|
sandbox
|
||||||
|
gradle
|
||||||
|
kotlin
|
||||||
|
java
|
||||||
|
linux
|
||||||
|
macos
|
||||||
|
windows
|
||||||
|
unix
|
||||||
|
system
|
||||||
|
systems
|
||||||
|
support
|
||||||
|
supports
|
||||||
|
compatible
|
||||||
|
compatibility
|
||||||
|
fallback
|
||||||
|
native
|
||||||
|
automatic
|
||||||
|
autoswitch
|
||||||
|
switch
|
||||||
|
switches
|
||||||
282
lyng-idea/src/main/resources/dictionaries/tech-lyng.txt
Normal file
282
lyng-idea/src/main/resources/dictionaries/tech-lyng.txt
Normal file
@ -0,0 +1,282 @@
|
|||||||
|
# Lyng/tech vocabulary – one word per line, lowercase
|
||||||
|
lyng
|
||||||
|
miniast
|
||||||
|
binder
|
||||||
|
printf
|
||||||
|
specifier
|
||||||
|
specifiers
|
||||||
|
regex
|
||||||
|
regexp
|
||||||
|
token
|
||||||
|
tokens
|
||||||
|
lexer
|
||||||
|
parser
|
||||||
|
syntax
|
||||||
|
semantic
|
||||||
|
highlight
|
||||||
|
highlighting
|
||||||
|
underline
|
||||||
|
typo
|
||||||
|
typos
|
||||||
|
dictionary
|
||||||
|
dictionaries
|
||||||
|
grazie
|
||||||
|
natural
|
||||||
|
languages
|
||||||
|
inspection
|
||||||
|
inspections
|
||||||
|
annotation
|
||||||
|
annotator
|
||||||
|
annotations
|
||||||
|
quickfix
|
||||||
|
quickfixes
|
||||||
|
intention
|
||||||
|
intentions
|
||||||
|
replacement
|
||||||
|
replacements
|
||||||
|
identifier
|
||||||
|
identifiers
|
||||||
|
keyword
|
||||||
|
keywords
|
||||||
|
comment
|
||||||
|
comments
|
||||||
|
string
|
||||||
|
strings
|
||||||
|
literal
|
||||||
|
literals
|
||||||
|
formatting
|
||||||
|
formatter
|
||||||
|
splitter
|
||||||
|
camelcase
|
||||||
|
snakecase
|
||||||
|
pascalcase
|
||||||
|
uppercase
|
||||||
|
lowercase
|
||||||
|
titlecase
|
||||||
|
case
|
||||||
|
cases
|
||||||
|
project
|
||||||
|
module
|
||||||
|
modules
|
||||||
|
resource
|
||||||
|
resources
|
||||||
|
bundle
|
||||||
|
bundled
|
||||||
|
gzipped
|
||||||
|
plaintext
|
||||||
|
text
|
||||||
|
range
|
||||||
|
ranges
|
||||||
|
offset
|
||||||
|
offsets
|
||||||
|
position
|
||||||
|
positions
|
||||||
|
apply
|
||||||
|
applies
|
||||||
|
runtime
|
||||||
|
compile
|
||||||
|
build
|
||||||
|
artifact
|
||||||
|
artifacts
|
||||||
|
plugin
|
||||||
|
plugins
|
||||||
|
intellij
|
||||||
|
idea
|
||||||
|
sandbox
|
||||||
|
gradle
|
||||||
|
kotlin
|
||||||
|
java
|
||||||
|
jvm
|
||||||
|
coroutines
|
||||||
|
suspend
|
||||||
|
scope
|
||||||
|
scopes
|
||||||
|
context
|
||||||
|
contexts
|
||||||
|
tokenizer
|
||||||
|
tokenizers
|
||||||
|
spell
|
||||||
|
spelling
|
||||||
|
spellcheck
|
||||||
|
spellchecker
|
||||||
|
fallback
|
||||||
|
native
|
||||||
|
autoswitch
|
||||||
|
switch
|
||||||
|
switching
|
||||||
|
enable
|
||||||
|
disable
|
||||||
|
enabled
|
||||||
|
disabled
|
||||||
|
setting
|
||||||
|
settings
|
||||||
|
preference
|
||||||
|
preferences
|
||||||
|
editor
|
||||||
|
filetype
|
||||||
|
filetypes
|
||||||
|
language
|
||||||
|
languages
|
||||||
|
psi
|
||||||
|
psielement
|
||||||
|
psifile
|
||||||
|
textcontent
|
||||||
|
textdomain
|
||||||
|
stealth
|
||||||
|
stealthy
|
||||||
|
printfspec
|
||||||
|
format
|
||||||
|
formats
|
||||||
|
pattern
|
||||||
|
patterns
|
||||||
|
match
|
||||||
|
matches
|
||||||
|
group
|
||||||
|
groups
|
||||||
|
node
|
||||||
|
nodes
|
||||||
|
tree
|
||||||
|
graph
|
||||||
|
edge
|
||||||
|
edges
|
||||||
|
pair
|
||||||
|
pairs
|
||||||
|
map
|
||||||
|
maps
|
||||||
|
list
|
||||||
|
lists
|
||||||
|
array
|
||||||
|
arrays
|
||||||
|
set
|
||||||
|
sets
|
||||||
|
queue
|
||||||
|
stack
|
||||||
|
index
|
||||||
|
indices
|
||||||
|
length
|
||||||
|
size
|
||||||
|
empty
|
||||||
|
contains
|
||||||
|
equals
|
||||||
|
compare
|
||||||
|
greater
|
||||||
|
less
|
||||||
|
minimum
|
||||||
|
maximum
|
||||||
|
average
|
||||||
|
sum
|
||||||
|
total
|
||||||
|
random
|
||||||
|
round
|
||||||
|
floor
|
||||||
|
ceil
|
||||||
|
sin
|
||||||
|
cos
|
||||||
|
tan
|
||||||
|
sqrt
|
||||||
|
abs
|
||||||
|
min
|
||||||
|
max
|
||||||
|
read
|
||||||
|
write
|
||||||
|
open
|
||||||
|
close
|
||||||
|
append
|
||||||
|
create
|
||||||
|
delete
|
||||||
|
remove
|
||||||
|
update
|
||||||
|
save
|
||||||
|
load
|
||||||
|
start
|
||||||
|
stop
|
||||||
|
run
|
||||||
|
execute
|
||||||
|
return
|
||||||
|
break
|
||||||
|
continue
|
||||||
|
try
|
||||||
|
catch
|
||||||
|
finally
|
||||||
|
throw
|
||||||
|
throws
|
||||||
|
if
|
||||||
|
else
|
||||||
|
when
|
||||||
|
while
|
||||||
|
for
|
||||||
|
loop
|
||||||
|
rangeop
|
||||||
|
caseop
|
||||||
|
switchop
|
||||||
|
default
|
||||||
|
optional
|
||||||
|
required
|
||||||
|
public
|
||||||
|
private
|
||||||
|
protected
|
||||||
|
internal
|
||||||
|
external
|
||||||
|
inline
|
||||||
|
override
|
||||||
|
abstract
|
||||||
|
sealed
|
||||||
|
open
|
||||||
|
final
|
||||||
|
static
|
||||||
|
const
|
||||||
|
lazy
|
||||||
|
late
|
||||||
|
init
|
||||||
|
initialize
|
||||||
|
configuration
|
||||||
|
option
|
||||||
|
options
|
||||||
|
projectwide
|
||||||
|
workspace
|
||||||
|
crossplatform
|
||||||
|
multiplatform
|
||||||
|
commonmain
|
||||||
|
jsmain
|
||||||
|
native
|
||||||
|
platform
|
||||||
|
api
|
||||||
|
implementation
|
||||||
|
dependency
|
||||||
|
dependencies
|
||||||
|
classpath
|
||||||
|
source
|
||||||
|
sources
|
||||||
|
document
|
||||||
|
documents
|
||||||
|
logging
|
||||||
|
logger
|
||||||
|
info
|
||||||
|
debug
|
||||||
|
trace
|
||||||
|
warning
|
||||||
|
error
|
||||||
|
severity
|
||||||
|
severitylevel
|
||||||
|
intentionaction
|
||||||
|
daemon
|
||||||
|
daemoncodeanalyzer
|
||||||
|
restart
|
||||||
|
textattributes
|
||||||
|
textattributeskey
|
||||||
|
typostyle
|
||||||
|
learned
|
||||||
|
learn
|
||||||
|
tech
|
||||||
|
vocabulary
|
||||||
|
domain
|
||||||
|
term
|
||||||
|
terms
|
||||||
|
us
|
||||||
|
uk
|
||||||
|
american
|
||||||
|
british
|
||||||
|
colour
|
||||||
|
color
|
||||||
|
organisation
|
||||||
|
organization
|
||||||
@ -1048,12 +1048,7 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Nullable suffix after base or generic
|
// Nullable suffix after base or generic
|
||||||
val isNullable = if (cc.skipTokenOfType(Token.Type.QUESTION, isOptional = true)) {
|
val isNullable = cc.skipTokenOfType(Token.Type.QUESTION, isOptional = true)
|
||||||
true
|
|
||||||
} else if (cc.skipTokenOfType(Token.Type.IFNULLASSIGN, isOptional = true)) {
|
|
||||||
cc.pushPendingAssign()
|
|
||||||
true
|
|
||||||
} else false
|
|
||||||
val endPos = cc.currentPos()
|
val endPos = cc.currentPos()
|
||||||
|
|
||||||
val miniRef = buildBaseRef(if (miniArgs != null) endPos else lastEnd, miniArgs, isNullable)
|
val miniRef = buildBaseRef(if (miniArgs != null) endPos else lastEnd, miniArgs, isNullable)
|
||||||
|
|||||||
@ -35,9 +35,8 @@ class CompilerContext(val tokens: List<Token>) {
|
|||||||
|
|
||||||
var currentIndex = 0
|
var currentIndex = 0
|
||||||
private var pendingGT = 0
|
private var pendingGT = 0
|
||||||
private var pendingAssign = false
|
|
||||||
|
|
||||||
fun hasNext() = currentIndex < tokens.size || pendingGT > 0 || pendingAssign
|
fun hasNext() = currentIndex < tokens.size || pendingGT > 0
|
||||||
fun hasPrevious() = currentIndex > 0
|
fun hasPrevious() = currentIndex > 0
|
||||||
fun next(): Token {
|
fun next(): Token {
|
||||||
if (pendingGT > 0) {
|
if (pendingGT > 0) {
|
||||||
@ -45,11 +44,6 @@ class CompilerContext(val tokens: List<Token>) {
|
|||||||
val last = tokens[currentIndex - 1]
|
val last = tokens[currentIndex - 1]
|
||||||
return Token(">", last.pos.copy(column = last.pos.column + 1), Token.Type.GT)
|
return Token(">", last.pos.copy(column = last.pos.column + 1), Token.Type.GT)
|
||||||
}
|
}
|
||||||
if (pendingAssign) {
|
|
||||||
pendingAssign = false
|
|
||||||
val last = tokens[currentIndex - 1]
|
|
||||||
return Token("=", last.pos.copy(column = last.pos.column + 1), Token.Type.ASSIGN)
|
|
||||||
}
|
|
||||||
return if (currentIndex < tokens.size) tokens[currentIndex++]
|
return if (currentIndex < tokens.size) tokens[currentIndex++]
|
||||||
else Token("", tokens.last().pos, Token.Type.EOF)
|
else Token("", tokens.last().pos, Token.Type.EOF)
|
||||||
}
|
}
|
||||||
@ -58,19 +52,16 @@ class CompilerContext(val tokens: List<Token>) {
|
|||||||
pendingGT++
|
pendingGT++
|
||||||
}
|
}
|
||||||
|
|
||||||
fun pushPendingAssign() {
|
fun previous() = if (pendingGT > 0) {
|
||||||
pendingAssign = true
|
pendingGT-- // This is wrong, previous should go back.
|
||||||
}
|
// But we don't really use previous() in generics parser after splitting.
|
||||||
|
throw IllegalStateException("previous() not supported after pushPendingGT")
|
||||||
fun previous() = if (pendingGT > 0 || pendingAssign) {
|
|
||||||
throw IllegalStateException("previous() not supported after pushPending tokens")
|
|
||||||
} else if (!hasPrevious()) throw IllegalStateException("No previous token") else tokens[--currentIndex]
|
} else if (!hasPrevious()) throw IllegalStateException("No previous token") else tokens[--currentIndex]
|
||||||
|
|
||||||
fun savePos() = (currentIndex shl 3) or (pendingGT and 3) or (if (pendingAssign) 4 else 0)
|
fun savePos() = (currentIndex shl 2) or (pendingGT and 3)
|
||||||
fun restorePos(pos: Int) {
|
fun restorePos(pos: Int) {
|
||||||
currentIndex = pos shr 3
|
currentIndex = pos shr 2
|
||||||
pendingGT = pos and 3
|
pendingGT = pos and 3
|
||||||
pendingAssign = (pos and 4) != 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun ensureLabelIsValid(pos: Pos, label: String) {
|
fun ensureLabelIsValid(pos: Pos, label: String) {
|
||||||
|
|||||||
@ -100,16 +100,11 @@ object LyngFormatter {
|
|||||||
fun isControlHeaderNoBrace(s: String): Boolean {
|
fun isControlHeaderNoBrace(s: String): Boolean {
|
||||||
val t = s.trim()
|
val t = s.trim()
|
||||||
if (t.isEmpty()) return false
|
if (t.isEmpty()) return false
|
||||||
// match: if (...) | else if (...) | else | catch (...) | finally
|
// match: if (...) | else if (...) | else
|
||||||
if (Regex("^if\\s*\\(.*\\)$").matches(t)) return true
|
val isIf = Regex("^if\\s*\\(.*\\)\\s*$").matches(t)
|
||||||
if (Regex("^else\\s+if\\s*\\(.*\\)$").matches(t)) return true
|
val isElseIf = Regex("^else\\s+if\\s*\\(.*\\)\\s*$").matches(t)
|
||||||
if (t == "else") return true
|
val isElse = t == "else"
|
||||||
if (Regex("^catch\\s*\\(.*\\)$").matches(t)) return true
|
if (isIf || isElseIf || isElse) return true
|
||||||
if (t == "finally") return true
|
|
||||||
|
|
||||||
// Short definition form: fun x() = or val x =
|
|
||||||
if (Regex("^(override\\s+)?(fun|fn)\\b.*=\\s*$").matches(t)) return true
|
|
||||||
if (Regex("^(private\\s+|protected\\s+|public\\s+|override\\s+)?(val|var)\\b.*=\\s*$").matches(t)) return true
|
|
||||||
|
|
||||||
// property accessors ending with ) or =
|
// property accessors ending with ) or =
|
||||||
if (isAccessorRelated(t)) {
|
if (isAccessorRelated(t)) {
|
||||||
@ -118,23 +113,11 @@ object LyngFormatter {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isDoubleIndentHeader(s: String): Boolean {
|
|
||||||
val t = s.trim()
|
|
||||||
if (!t.endsWith("=")) return false
|
|
||||||
// Is it a function or property definition?
|
|
||||||
if (Regex("\\b(fun|fn|val|var)\\b").containsMatchIn(t)) return true
|
|
||||||
// Is it an accessor?
|
|
||||||
if (isPropertyAccessor(t)) return true
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for ((i, rawLine) in lines.withIndex()) {
|
for ((i, rawLine) in lines.withIndex()) {
|
||||||
val (parts, nextInBlockComment) = splitIntoParts(rawLine, inBlockComment)
|
val (parts, nextInBlockComment) = splitIntoParts(rawLine, inBlockComment)
|
||||||
val code = parts.filter { it.type == PartType.Code }.joinToString("") { it.text }
|
val code = parts.filter { it.type == PartType.Code }.joinToString("") { it.text }
|
||||||
val codeAndStrings = parts.filter { it.type != PartType.BlockComment && it.type != PartType.LineComment }.joinToString("") { it.text }
|
|
||||||
val trimmedStart = code.dropWhile { it == ' ' || it == '\t' }
|
val trimmedStart = code.dropWhile { it == ' ' || it == '\t' }
|
||||||
val trimmedLine = rawLine.trim()
|
val trimmedLine = rawLine.trim()
|
||||||
val trimmedCodeAndStrings = codeAndStrings.trim()
|
|
||||||
|
|
||||||
// Compute effective indent level for this line
|
// Compute effective indent level for this line
|
||||||
val currentExtraIndent = extraIndents.sum()
|
val currentExtraIndent = extraIndents.sum()
|
||||||
@ -155,15 +138,9 @@ object LyngFormatter {
|
|||||||
|
|
||||||
// Single-line control header (if/else/else if) without braces: indent the next
|
// Single-line control header (if/else/else if) without braces: indent the next
|
||||||
// non-empty, non-'}', non-'else' line by one extra level
|
// non-empty, non-'}', non-'else' line by one extra level
|
||||||
val isContinuation = trimmedStart.startsWith("else") || trimmedStart.startsWith("catch") || trimmedStart.startsWith("finally")
|
|
||||||
val applyAwaiting = awaitingExtraIndent > 0 && trimmedStart.isNotEmpty() &&
|
val applyAwaiting = awaitingExtraIndent > 0 && trimmedStart.isNotEmpty() &&
|
||||||
!trimmedStart.startsWith("}")
|
!trimmedStart.startsWith("else") && !trimmedStart.startsWith("}")
|
||||||
var actualAwaiting = 0
|
if (applyAwaiting) effectiveLevel += awaitingExtraIndent
|
||||||
if (applyAwaiting) {
|
|
||||||
actualAwaiting = awaitingExtraIndent
|
|
||||||
if (isContinuation) actualAwaiting = (actualAwaiting - 1).coerceAtLeast(0)
|
|
||||||
effectiveLevel += actualAwaiting
|
|
||||||
}
|
|
||||||
|
|
||||||
val firstChar = trimmedStart.firstOrNull()
|
val firstChar = trimmedStart.firstOrNull()
|
||||||
// While inside parentheses, continuation applies scaled by nesting level
|
// While inside parentheses, continuation applies scaled by nesting level
|
||||||
@ -217,7 +194,7 @@ object LyngFormatter {
|
|||||||
val newBlockLevel = blockLevel
|
val newBlockLevel = blockLevel
|
||||||
if (newBlockLevel > oldBlockLevel) {
|
if (newBlockLevel > oldBlockLevel) {
|
||||||
val isAccessorRelatedLine = isAccessor || (!inBlockComment && isAccessorRelated(code))
|
val isAccessorRelatedLine = isAccessor || (!inBlockComment && isAccessorRelated(code))
|
||||||
val addedThisLine = actualAwaiting + (if (isAccessorRelatedLine) 1 else 0)
|
val addedThisLine = (if (applyAwaiting) awaitingExtraIndent else 0) + (if (isAccessorRelatedLine) 1 else 0)
|
||||||
repeat(newBlockLevel - oldBlockLevel) {
|
repeat(newBlockLevel - oldBlockLevel) {
|
||||||
extraIndents.add(addedThisLine)
|
extraIndents.add(addedThisLine)
|
||||||
}
|
}
|
||||||
@ -230,29 +207,23 @@ object LyngFormatter {
|
|||||||
inBlockComment = nextInBlockComment
|
inBlockComment = nextInBlockComment
|
||||||
|
|
||||||
// Update awaitingExtraIndent based on current line
|
// Update awaitingExtraIndent based on current line
|
||||||
val nextLineTrimmed = lines.getOrNull(i + 1)?.trim() ?: ""
|
|
||||||
val nextIsContinuation = nextLineTrimmed.startsWith("else") ||
|
|
||||||
nextLineTrimmed.startsWith("catch") ||
|
|
||||||
nextLineTrimmed.startsWith("finally")
|
|
||||||
|
|
||||||
if (applyAwaiting && trimmedStart.isNotEmpty()) {
|
if (applyAwaiting && trimmedStart.isNotEmpty()) {
|
||||||
// we have just applied it.
|
// we have just applied it.
|
||||||
val endsWithBrace = code.trimEnd().endsWith("{")
|
val endsWithBrace = code.trimEnd().endsWith("{")
|
||||||
if (!endsWithBrace && isControlHeaderNoBrace(trimmedCodeAndStrings)) {
|
if (!endsWithBrace && isControlHeaderNoBrace(code)) {
|
||||||
// It's another header, increment
|
// It's another header, increment
|
||||||
val isAccessorOrDouble = isAccessor || (!inBlockComment && isAccessorRelated(code)) || isDoubleIndentHeader(trimmedCodeAndStrings)
|
val isAccessorRelatedLine = isAccessor || (!inBlockComment && isAccessorRelated(code))
|
||||||
val increment = if (isAccessorOrDouble) 2 else 1
|
awaitingExtraIndent += if (isAccessorRelatedLine) 2 else 1
|
||||||
awaitingExtraIndent = actualAwaiting + increment
|
} else {
|
||||||
} else if (!nextIsContinuation) {
|
// It's the body, reset
|
||||||
// It's the body, and no continuation follows, so reset
|
|
||||||
awaitingExtraIndent = 0
|
awaitingExtraIndent = 0
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// start awaiting if current line is a control header without '{'
|
// start awaiting if current line is a control header without '{'
|
||||||
val endsWithBrace = code.trimEnd().endsWith("{")
|
val endsWithBrace = code.trimEnd().endsWith("{")
|
||||||
if (!endsWithBrace && isControlHeaderNoBrace(trimmedCodeAndStrings)) {
|
if (!endsWithBrace && isControlHeaderNoBrace(code)) {
|
||||||
val isAccessorOrDouble = isAccessor || (!inBlockComment && isAccessorRelated(code)) || isDoubleIndentHeader(trimmedCodeAndStrings)
|
val isAccessorRelatedLine = isAccessor || (!inBlockComment && isAccessorRelated(code))
|
||||||
awaitingExtraIndent = if (isAccessorOrDouble) 2 else 1
|
awaitingExtraIndent = if (isAccessorRelatedLine) 2 else 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4803,7 +4803,7 @@ class ScriptTest {
|
|||||||
fun f(a: String = "foo") = a + "!"
|
fun f(a: String = "foo") = a + "!"
|
||||||
fun g(a: String? = null) = a ?: "!!"
|
fun g(a: String? = null) = a ?: "!!"
|
||||||
assertEquals(f(), "foo!")
|
assertEquals(f(), "foo!")
|
||||||
assertEquals(g(), "!!")
|
assertEquals(f(), "!!")
|
||||||
assertEquals(f("bar"), "bar!")
|
assertEquals(f("bar"), "bar!")
|
||||||
class T(b: Int=42,c: String?=null)
|
class T(b: Int=42,c: String?=null)
|
||||||
assertEquals(42, T().b)
|
assertEquals(42, T().b)
|
||||||
@ -4811,18 +4811,5 @@ class ScriptTest {
|
|||||||
""".trimIndent())
|
""".trimIndent())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testArgsPriorityWithSplash() = runTest {
|
|
||||||
eval("""
|
|
||||||
class A {
|
|
||||||
val tags get() = ["foo"]
|
|
||||||
|
|
||||||
fun f1(tags...) = tags
|
|
||||||
|
|
||||||
fun f2(tags...) = f1(...tags)
|
|
||||||
}
|
|
||||||
assertEquals(["bar"], A().f2("bar"))
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user