idea plugin 0.0.2-SNAPSHOT, improced, added reformat code. Formatting tools improved in lynglib. Site information added
This commit is contained in:
1 parent
06e8e1579d
commit
ec49bbbf52
19 files changed
+530
-54
No files matched your search
@@ -21,7 +21,7 @@ plugins {
|
||||
}
|
||||
|
||||
group = "net.sergeych.lyng"
|
||||
version = "0.0.1-SNAPSHOT"
|
||||
version = "0.0.2-SNAPSHOT"
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(17)
|
||||
@@ -41,8 +41,10 @@ dependencies {
|
||||
|
||||
intellij {
|
||||
type.set("IC")
|
||||
// Run sandbox on IntelliJ IDEA 2024.3.x
|
||||
// Build against a modern baseline. Install range is controlled by since/until below.
|
||||
version.set("2024.3.1")
|
||||
// We manage <idea-version> ourselves in plugin.xml to keep it open-ended (no upper cap)
|
||||
updateSinceUntilBuild.set(false)
|
||||
// Include only available bundled plugins for this IDE build
|
||||
plugins.set(listOf(
|
||||
"com.intellij.java"
|
||||
@@ -51,8 +53,24 @@ intellij {
|
||||
|
||||
tasks {
|
||||
patchPluginXml {
|
||||
// Compatible with 2024.3+
|
||||
sinceBuild.set("243")
|
||||
untilBuild.set(null as String?)
|
||||
// Keep version and other metadata patched by Gradle, but since/until are controlled in plugin.xml.
|
||||
// (intellij.updateSinceUntilBuild=false prevents Gradle from injecting an until-build cap)
|
||||
}
|
||||
|
||||
// Build an installable plugin zip and copy it to $PROJECT_ROOT/distributables
|
||||
// Usage: ./gradlew :lyng-idea:buildInstallablePlugin
|
||||
// It depends on buildPlugin and overwrites any existing file with the same name
|
||||
register<Copy>("buildInstallablePlugin") {
|
||||
dependsOn("buildPlugin")
|
||||
|
||||
// The Gradle IntelliJ Plugin produces: build/distributions/<project.name>-<version>.zip
|
||||
val zipName = "${project.name}-${project.version}.zip"
|
||||
val sourceZip = layout.buildDirectory.file("distributions/$zipName")
|
||||
|
||||
from(sourceZip)
|
||||
into(rootProject.layout.projectDirectory.dir("distributables"))
|
||||
|
||||
// Overwrite if a file with the same name exists
|
||||
duplicatesStrategy = DuplicatesStrategy.INCLUDE
|
||||
}
|
||||
}
|
||||
+88
-3
@@ -28,6 +28,10 @@ import com.intellij.psi.PsiFile
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import net.sergeych.lyng.Compiler
|
||||
import net.sergeych.lyng.Source
|
||||
import net.sergeych.lyng.binding.Binder
|
||||
import net.sergeych.lyng.binding.SymbolKind
|
||||
import net.sergeych.lyng.highlight.HighlightKind
|
||||
import net.sergeych.lyng.highlight.SimpleLyngHighlighter
|
||||
import net.sergeych.lyng.highlight.offsetOf
|
||||
import net.sergeych.lyng.idea.highlight.LyngHighlighterColors
|
||||
import net.sergeych.lyng.idea.util.IdeLenientImportProvider
|
||||
@@ -59,7 +63,8 @@ class LyngExternalAnnotator : ExternalAnnotator<LyngExternalAnnotator.Input, Lyn
|
||||
val src = Source("<ide>", text)
|
||||
val provider = IdeLenientImportProvider.create()
|
||||
runBlocking { Compiler.compileWithMini(src, provider, sink) }
|
||||
} catch (_: Throwable) {
|
||||
} catch (e: Throwable) {
|
||||
if (e is com.intellij.openapi.progress.ProcessCanceledException) throw e
|
||||
// Fail softly: no semantic layer this pass
|
||||
return Result(collectedInfo.modStamp, emptyList())
|
||||
}
|
||||
@@ -67,7 +72,7 @@ class LyngExternalAnnotator : ExternalAnnotator<LyngExternalAnnotator.Input, Lyn
|
||||
val mini = sink.build() ?: return Result(collectedInfo.modStamp, emptyList())
|
||||
val source = Source("<ide>", text)
|
||||
|
||||
val out = ArrayList<Span>(64)
|
||||
val out = ArrayList<Span>(256)
|
||||
|
||||
fun putRange(start: Int, end: Int, key: com.intellij.openapi.editor.colors.TextAttributesKey) {
|
||||
if (start in 0..end && end <= text.length && start < end) out += Span(start, end, key)
|
||||
@@ -85,7 +90,7 @@ class LyngExternalAnnotator : ExternalAnnotator<LyngExternalAnnotator.Input, Lyn
|
||||
// Declarations
|
||||
for (d in mini.declarations) {
|
||||
when (d) {
|
||||
is MiniFunDecl -> putName(d.nameStart, d.name, LyngHighlighterColors.FUNCTION)
|
||||
is MiniFunDecl -> putName(d.nameStart, d.name, LyngHighlighterColors.FUNCTION_DECLARATION)
|
||||
is MiniClassDecl -> putName(d.nameStart, d.name, LyngHighlighterColors.TYPE)
|
||||
is MiniValDecl -> putName(
|
||||
d.nameStart,
|
||||
@@ -142,6 +147,86 @@ class LyngExternalAnnotator : ExternalAnnotator<LyngExternalAnnotator.Input, Lyn
|
||||
}
|
||||
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
// Semantic usages via Binder (best-effort)
|
||||
try {
|
||||
val binding = Binder.bind(text, mini)
|
||||
|
||||
// Map declaration ranges to avoid duplicating them as usages
|
||||
val declKeys = HashSet<Pair<Int, Int>>(binding.symbols.size * 2)
|
||||
for (sym in binding.symbols) declKeys += (sym.declStart to sym.declEnd)
|
||||
|
||||
fun keyForKind(k: SymbolKind) = when (k) {
|
||||
SymbolKind.Function -> LyngHighlighterColors.FUNCTION
|
||||
SymbolKind.Class, SymbolKind.Enum -> LyngHighlighterColors.TYPE
|
||||
SymbolKind.Param -> LyngHighlighterColors.PARAMETER
|
||||
SymbolKind.Val -> LyngHighlighterColors.VALUE
|
||||
SymbolKind.Var -> LyngHighlighterColors.VARIABLE
|
||||
}
|
||||
|
||||
// Track covered ranges to not override later heuristics
|
||||
val covered = HashSet<Pair<Int, Int>>()
|
||||
|
||||
for (ref in binding.references) {
|
||||
val key = ref.start to ref.end
|
||||
if (declKeys.contains(key)) continue
|
||||
val sym = binding.symbols.firstOrNull { it.id == ref.symbolId } ?: continue
|
||||
val color = keyForKind(sym.kind)
|
||||
putRange(ref.start, ref.end, color)
|
||||
covered += key
|
||||
}
|
||||
|
||||
// Heuristics on top of binder: function call-sites and simple name-based roles
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
val tokens = try { SimpleLyngHighlighter().highlight(text) } catch (_: Throwable) { emptyList() }
|
||||
|
||||
fun isFollowedByParenOrBlock(rangeEnd: Int): Boolean {
|
||||
var i = rangeEnd
|
||||
while (i < text.length) {
|
||||
val ch = text[i]
|
||||
if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') { i++; continue }
|
||||
return ch == '(' || ch == '{'
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Build simple name -> role map for top-level vals/vars and parameters
|
||||
val nameRole = HashMap<String, com.intellij.openapi.editor.colors.TextAttributesKey>(8)
|
||||
for (d in mini.declarations) when (d) {
|
||||
is MiniValDecl -> nameRole[d.name] = if (d.mutable) LyngHighlighterColors.VARIABLE else LyngHighlighterColors.VALUE
|
||||
is MiniFunDecl -> d.params.forEach { p -> nameRole[p.name] = LyngHighlighterColors.PARAMETER }
|
||||
else -> {}
|
||||
}
|
||||
|
||||
for (s in tokens) if (s.kind == HighlightKind.Identifier) {
|
||||
val start = s.range.start
|
||||
val end = s.range.endExclusive
|
||||
val key = start to end
|
||||
if (key in covered || key in declKeys) continue
|
||||
|
||||
// Call-site detection first so it wins over var/param role
|
||||
if (isFollowedByParenOrBlock(end)) {
|
||||
putRange(start, end, LyngHighlighterColors.FUNCTION)
|
||||
covered += key
|
||||
continue
|
||||
}
|
||||
|
||||
// Simple role by known names
|
||||
val ident = try { text.substring(start, end) } catch (_: Throwable) { null }
|
||||
if (ident != null) {
|
||||
val roleKey = nameRole[ident]
|
||||
if (roleKey != null) {
|
||||
putRange(start, end, roleKey)
|
||||
covered += key
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
// Must rethrow cancellation; otherwise ignore binder failures (best-effort)
|
||||
if (e is com.intellij.openapi.progress.ProcessCanceledException) throw e
|
||||
}
|
||||
|
||||
return Result(collectedInfo.modStamp, out)
|
||||
}
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@ class LyngColorSettingsPage : ColorSettingsPage {
|
||||
AttributesDescriptor("Variable (semantic)", LyngHighlighterColors.VARIABLE),
|
||||
AttributesDescriptor("Value (semantic)", LyngHighlighterColors.VALUE),
|
||||
AttributesDescriptor("Function (semantic)", LyngHighlighterColors.FUNCTION),
|
||||
AttributesDescriptor("Function declaration (semantic)", LyngHighlighterColors.FUNCTION_DECLARATION),
|
||||
AttributesDescriptor("Type (semantic)", LyngHighlighterColors.TYPE),
|
||||
AttributesDescriptor("Namespace (semantic)", LyngHighlighterColors.NAMESPACE),
|
||||
AttributesDescriptor("Parameter (semantic)", LyngHighlighterColors.PARAMETER),
|
||||
|
||||
+10
-3
@@ -46,15 +46,22 @@ object LyngHighlighterColors {
|
||||
"LYNG_PUNCT", DefaultLanguageHighlighterColors.DOT
|
||||
)
|
||||
|
||||
// Semantic layer keys (placeholders for now)
|
||||
// Semantic layer keys
|
||||
val VARIABLE: TextAttributesKey = TextAttributesKey.createTextAttributesKey(
|
||||
"LYNG_VARIABLE", DefaultLanguageHighlighterColors.LOCAL_VARIABLE
|
||||
// Use a distinctive default to ensure visibility across common themes.
|
||||
// Users can still customize it separately from VALUE.
|
||||
"LYNG_VARIABLE", DefaultLanguageHighlighterColors.INSTANCE_FIELD
|
||||
)
|
||||
val VALUE: TextAttributesKey = TextAttributesKey.createTextAttributesKey(
|
||||
"LYNG_VALUE", DefaultLanguageHighlighterColors.INSTANCE_FIELD
|
||||
)
|
||||
val FUNCTION: TextAttributesKey = TextAttributesKey.createTextAttributesKey(
|
||||
"LYNG_FUNCTION", DefaultLanguageHighlighterColors.FUNCTION_CALL
|
||||
// Primary approach: make function calls as visible as declarations by default
|
||||
// (users can still customize separately in the color scheme UI).
|
||||
"LYNG_FUNCTION", DefaultLanguageHighlighterColors.FUNCTION_DECLARATION
|
||||
)
|
||||
val FUNCTION_DECLARATION: TextAttributesKey = TextAttributesKey.createTextAttributesKey(
|
||||
"LYNG_FUNCTION_DECLARATION", DefaultLanguageHighlighterColors.FUNCTION_DECLARATION
|
||||
)
|
||||
val TYPE: TextAttributesKey = TextAttributesKey.createTextAttributesKey(
|
||||
"LYNG_TYPE", DefaultLanguageHighlighterColors.CLASS_REFERENCE
|
||||
|
||||
@@ -16,13 +16,17 @@
|
||||
-->
|
||||
|
||||
<idea-plugin>
|
||||
<!-- Open-ended compatibility: 2024.3+ (build 243 and newer) -->
|
||||
<idea-version since-build="243"/>
|
||||
<id>net.sergeych.lyng.idea</id>
|
||||
<name>Lyng Language Support</name>
|
||||
<vendor email="real.sergeych@gmail.com">Sergeych Works</vendor>
|
||||
<vendor email="real.sergeych@gmail.com">Sergey Chernov</vendor>
|
||||
|
||||
<description>
|
||||
<![CDATA[
|
||||
Basic Lyng language support: file type, syntax highlighting scaffold, and quick docs/highlighter stubs.
|
||||
Basic Lyng language support: file type, syntax highlighting,
|
||||
editing assistance (on Enter indent), reformatting code (indents and spaces),
|
||||
and quick docs.
|
||||
]]>
|
||||
</description>
|
||||
|
||||
|
||||
@@ -15,16 +15,17 @@
|
||||
- limitations under the License.
|
||||
-
|
||||
-->
|
||||
|
||||
<svg width="40" height="40" viewBox="0 0 40 40" xmlns="http://www.w3.org/2000/svg" aria-labelledby="title" role="img">
|
||||
<title>Lyng Plugin Icon (temporary λ)</title>
|
||||
<defs>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" role="img" aria-label="Lyng favicon">
|
||||
<style>
|
||||
.glyph { fill: none; stroke: currentColor; stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; }
|
||||
:root { color-scheme: light dark; }
|
||||
.mark { fill: currentColor; }
|
||||
.math { font-family: 'STIX Two Math', 'Cambria Math', 'Times New Roman', serif; }
|
||||
</style>
|
||||
</defs>
|
||||
<g transform="translate(2,2)">
|
||||
<path class="glyph" d="M12 6 L18 22 C19.2 25.5 22.2 28 26 28 L32 28"/>
|
||||
<path class="glyph" d="M18 22 L8 34"/>
|
||||
</g>
|
||||
<g class="mark math">
|
||||
<!-- Keep favicon legible: lambda with superscript y only -->
|
||||
<text x="3" y="17" font-size="19" font-weight="700"
|
||||
color="#009000" stroke="#002000" stroke-width="0.3">λ</text>
|
||||
<text x="11.2" y="4" font-size="16" color="#009000" stroke="#002000"
|
||||
stroke-width="0.1">y</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.3 KiB |
@@ -15,16 +15,17 @@
|
||||
- limitations under the License.
|
||||
-
|
||||
-->
|
||||
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" aria-labelledby="title" role="img">
|
||||
<title>Lyng File Icon (temporary λ)</title>
|
||||
<defs>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" role="img" aria-label="Lyng favicon">
|
||||
<style>
|
||||
.g { fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
|
||||
:root { color-scheme: light dark; }
|
||||
.mark { fill: currentColor; }
|
||||
.math { font-family: 'STIX Two Math', 'Cambria Math', 'Times New Roman', serif; }
|
||||
</style>
|
||||
</defs>
|
||||
<g transform="translate(0.5,0.5)">
|
||||
<path class="g" d="M4.5 2.5 L7 9.5 C7.6 11.2 9.0 12.5 11.0 12.5 L14.5 12.5"/>
|
||||
<path class="g" d="M7 9.5 L2.5 14.5"/>
|
||||
</g>
|
||||
<g class="mark math">
|
||||
<!-- Keep favicon legible: lambda with superscript y only -->
|
||||
<text x="3" y="17" font-size="19" font-weight="700"
|
||||
color="#009000" stroke="#002000" stroke-width="0.3">λ</text>
|
||||
<text x="11.2" y="4" font-size="16" color="#009000" stroke="#002000"
|
||||
stroke-width="0.1">y</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.3 KiB |
Reference in new issue
Block a user