plugin improvements; attempt to make AI life easier (incomplete and weak)
This commit is contained in:
parent
474293cfe3
commit
d2632cb99e
11
.junie/guidelines.md
Normal file
11
.junie/guidelines.md
Normal file
@ -0,0 +1,11 @@
|
||||
# Lyng Project Guidelines
|
||||
|
||||
This project uses the Lyng scripting language for multiplatform scripting.
|
||||
|
||||
## Coding in Lyng
|
||||
When writing, refactoring, or analyzing Lyng code:
|
||||
- **Reference**: Always use `LYNG_AI_SPEC.md` in the project root as the primary source of truth for syntax and idioms.
|
||||
- **File Extensions**: Use `.lyng` for all script files.
|
||||
- **Implicit Coroutines**: Remember that all Lyng functions are implicitly coroutines; do not look for `async/await`.
|
||||
- **Everything is an Expression**: Leverage the fact that blocks, if-statements, and loops return values.
|
||||
- **Maps vs Blocks**: Be careful: `{}` is a block/lambda, use `Map()` for an empty map.
|
||||
89
LYNG_AI_SPEC.md
Normal file
89
LYNG_AI_SPEC.md
Normal file
@ -0,0 +1,89 @@
|
||||
# Lyng Language AI Specification (V1.2)
|
||||
|
||||
High-density specification for LLMs. Reference this for all Lyng code generation.
|
||||
|
||||
## 1. Core Philosophy & Syntax
|
||||
- **Everything is an Expression**: Blocks, `if`, `when`, `for`, `while` return their last expression (or `void`).
|
||||
- **Implicit Coroutines**: All functions are coroutines. No `async/await`. Use `launch { ... }` (returns `Deferred`) or `flow { ... }`.
|
||||
- **Variables**: `val` (read-only), `var` (mutable). Supports late-init `val` in classes (must be assigned in `init` or body).
|
||||
- **Null Safety**: `?` (nullable type), `?.` (safe access), `?( )` (safe invoke), `?{ }` (safe block invoke), `?[ ]` (safe index), `?:` or `??` (elvis), `?=` (assign-if-null).
|
||||
- **Equality**: `==` (equals), `!=` (not equals), `===` (ref identity), `!==` (ref not identity).
|
||||
- **Comparison**: `<`, `>`, `<=`, `>=`, `<=>` (shuttle/spaceship, returns -1, 0, 1).
|
||||
- **Destructuring**: `val [a, b, rest...] = list`. Supports nested `[a, [b, c]]` and splats.
|
||||
|
||||
## 2. Object-Oriented Programming (OOP)
|
||||
- **Multiple Inheritance**: Supported with **C3 MRO** (Python-style). Diamond-safe.
|
||||
- **Header Arguments**: `class Foo(a, b) : Base(a)` defines fields `a`, `b` and passes `a` to `Base`.
|
||||
- **Members**: `fun name(args) { ... }`, `val`, `var`, `static val`, `static fun`.
|
||||
- **Properties (Get/Set)**: Pure accessors, no auto-backing fields.
|
||||
```lyng
|
||||
var age
|
||||
get() = _age
|
||||
private set(v) { if(v >= 0) _age = v }
|
||||
// Laconic syntax:
|
||||
val area get = π * r * r
|
||||
```
|
||||
- **Mandatory `override`**: Required for all members existing in the ancestor chain.
|
||||
- **Visibility**: `public` (default), `protected` (subclasses), `private` (this class instance only). `private set` / `protected set` allowed on properties.
|
||||
- **Disambiguation**: `this@Base.member()` or `(obj as Base).member()`. `as` returns a qualified view.
|
||||
- **Abstract/Interface**: `interface` is a synonym for `abstract class`. Both support state and constructors.
|
||||
- **Extensions**: `fun Class.ext()` or `val Class.ext get = ...`. Scope-isolated.
|
||||
|
||||
## 3. Delegation (`by`)
|
||||
Unified model for `val`, `var`, and `fun`.
|
||||
```lyng
|
||||
val x by MyDelegate()
|
||||
var y by Map() // Uses "y" as key in map
|
||||
fn f(a, b) by RemoteProxy() // Calls Proxy.invoke(thisRef, "f", a, b)
|
||||
```
|
||||
Delegate Methods:
|
||||
- `getValue(thisRef, name)`: for `val`/`var`.
|
||||
- `setValue(thisRef, name, val)`: for `var`.
|
||||
- `invoke(thisRef, name, args...)`: for `fn` (called if `getValue` is absent).
|
||||
- `bind(name, access, thisRef)`: optional hook called at declaration/binding time. `access` is `DelegateAccess.Val`, `Var`, or `Callable`.
|
||||
|
||||
## 4. Standard Library & Functional Built-ins
|
||||
- **Scope Functions**:
|
||||
- `obj.let { it... }`: result of block. `it` is `obj`.
|
||||
- `obj.apply { this... }`: returns `obj`. `this` is `obj`.
|
||||
- `obj.also { it... }`: returns `obj`. `it` is `obj`.
|
||||
- `obj.run { this... }`: result of block. `this` is `obj`.
|
||||
- `with(obj, { ... })`: result of block. `this` is `obj`.
|
||||
- **Functional**: `forEach`, `map`, `filter`, `any`, `all`, `sum`, `count`, `sortedBy`, `flatten`, `flatMap`, `associateBy`.
|
||||
- **Lazy**: `val x = cached { expensive() }` (call as `x()`) or `val x by lazy { ... }`.
|
||||
- **Collections**: `List` ( `[a, b]` ), `Map` ( `Map(k => v)` ), `Set` ( `Set(a, b)` ). `MapEntry` ( `k => v` ).
|
||||
|
||||
## 5. Patterns & Shorthands
|
||||
- **Map Literals**: `{ key: value, identifier: }` (identifier shorthand `x:` is `x: x`). Empty map is `Map()`.
|
||||
- **Named Arguments**: `fun(y: 10, x: 5)`. Shorthand: `Point(x:, y:)`.
|
||||
- **Varargs & Splats**: `fun f(args...)`, `f(...otherList)`.
|
||||
- **Labels**: `loop@ for(x in list) { if(x == 0) break@loop }`.
|
||||
- **Dynamic**: `val d = dynamic { get { name -> ... } }` allows `d.anyName`.
|
||||
|
||||
## 6. Operators & Methods to Overload
|
||||
| Op | Method | Op | Method |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `+` | `plus` | `==` | `equals` |
|
||||
| `-` | `minus` | `<=>` | `compareTo` |
|
||||
| `*` | `mul` | `[]` | `getAt` / `putAt` |
|
||||
| `/` | `div` | `!` | `logicalNot` |
|
||||
| `%` | `mod` | `-` | `negate` (unary) |
|
||||
| `=~` | `operatorMatch` | `+=` | `plusAssign` |
|
||||
|
||||
## 7. Common Snippets
|
||||
```lyng
|
||||
// Multiple Inheritance and Properties
|
||||
class Warrior(id, hp) : Character(id), HealthPool(hp) {
|
||||
override fun toString() = "Warrior #%s (%s HP)"(id, hp)
|
||||
}
|
||||
|
||||
// Map entry and merging
|
||||
val m = Map("a" => 1) + ("b" => 2)
|
||||
m += "c" => 3
|
||||
|
||||
// Destructuring with splat
|
||||
val [first, middle..., last] = [1, 2, 3, 4, 5]
|
||||
|
||||
// Safe Navigation and Elvis
|
||||
val companyName = person?.job?.company?.name ?? "Freelancer"
|
||||
```
|
||||
@ -140,6 +140,12 @@ Tips:
|
||||
- After a dot, globals are intentionally suppressed (e.g., `lines().Path` is not valid), only the receiver’s members are suggested.
|
||||
- If completion seems sparse, make sure related modules are imported (e.g., `import lyng.io.fs` so that `Path` and its methods are known).
|
||||
|
||||
## AI Assistant Support
|
||||
|
||||
To help AI assistants (like Cursor, Windsurf, or GitHub Copilot) understand Lyng with minimal effort, we provide a high-density language specification:
|
||||
|
||||
- **[LYNG_AI_SPEC.md](LYNG_AI_SPEC.md)**: A concise guide for AI models to learn Lyng syntax, idioms, and core philosophy. We recommend pointing your AI tool to this file or including it in your project's custom instructions.
|
||||
|
||||
## Why?
|
||||
|
||||
Designed to add scripting to kotlin multiplatform application in easy and efficient way. This is attempt to achieve what Lua is for C/++.
|
||||
|
||||
@ -239,6 +239,9 @@ object CompletionEngineLight {
|
||||
fun addMembersOf(name: String, direct: Boolean) {
|
||||
val cls = classes[name] ?: return
|
||||
val target = if (direct) directMap else inheritedMap
|
||||
for (cf in cls.ctorFields + cls.classFields) {
|
||||
target.getOrPut(cf.name) { mutableListOf() }.add(DocLookupUtils.toMemberVal(cf))
|
||||
}
|
||||
for (m in cls.members) target.getOrPut(m.name) { mutableListOf() }.add(m)
|
||||
for (b in cls.bases) if (visited.add(b)) addMembersOf(b, false)
|
||||
}
|
||||
|
||||
@ -96,6 +96,19 @@ object DocLookupUtils {
|
||||
return null
|
||||
}
|
||||
|
||||
fun findEnclosingClass(mini: MiniScript, offset: Int): MiniClassDecl? {
|
||||
val src = mini.range.start.source
|
||||
return mini.declarations.filterIsInstance<MiniClassDecl>()
|
||||
.filter {
|
||||
val start = src.offsetOf(it.range.start)
|
||||
val end = src.offsetOf(it.range.end)
|
||||
offset in start..end
|
||||
}
|
||||
.minByOrNull {
|
||||
src.offsetOf(it.range.end) - src.offsetOf(it.range.start)
|
||||
}
|
||||
}
|
||||
|
||||
fun findTypeByRange(mini: MiniScript?, name: String, startOffset: Int, text: String? = null, imported: List<String>? = null): MiniTypeRef? {
|
||||
if (mini == null) return null
|
||||
val src = mini.range.start.source
|
||||
@ -233,10 +246,14 @@ object DocLookupUtils {
|
||||
val rep = list.first()
|
||||
val bases = LinkedHashSet<String>()
|
||||
val members = LinkedHashMap<String, MutableList<MiniMemberDecl>>()
|
||||
val ctorFields = LinkedHashMap<String, MiniCtorField>()
|
||||
val classFields = LinkedHashMap<String, MiniCtorField>()
|
||||
var doc: MiniDoc? = null
|
||||
for (c in list) {
|
||||
bases.addAll(c.bases)
|
||||
if (doc == null && c.doc != null && c.doc.raw.isNotBlank()) doc = c.doc
|
||||
for (cf in c.ctorFields) ctorFields[cf.name] = cf
|
||||
for (cf in c.classFields) classFields[cf.name] = cf
|
||||
for (m in c.members) {
|
||||
// Group by name to keep overloads together
|
||||
members.getOrPut(m.name) { mutableListOf() }.add(m)
|
||||
@ -249,8 +266,8 @@ object DocLookupUtils {
|
||||
name = rep.name,
|
||||
bases = bases.toList(),
|
||||
bodyRange = rep.bodyRange,
|
||||
ctorFields = rep.ctorFields,
|
||||
classFields = rep.classFields,
|
||||
ctorFields = ctorFields.values.toList(),
|
||||
classFields = classFields.values.toList(),
|
||||
doc = doc,
|
||||
nameStart = rep.nameStart,
|
||||
members = mergedMembers
|
||||
@ -268,6 +285,18 @@ object DocLookupUtils {
|
||||
return result
|
||||
}
|
||||
|
||||
internal fun toMemberVal(cf: MiniCtorField): MiniMemberValDecl = MiniMemberValDecl(
|
||||
range = MiniRange(cf.nameStart, cf.nameStart),
|
||||
name = cf.name,
|
||||
mutable = cf.mutable,
|
||||
type = cf.type,
|
||||
initRange = null,
|
||||
doc = null,
|
||||
nameStart = cf.nameStart,
|
||||
isStatic = false,
|
||||
isExtern = false
|
||||
)
|
||||
|
||||
fun resolveMemberWithInheritance(importedModules: List<String>, className: String, member: String, localMini: MiniScript? = null): Pair<String, MiniNamedDecl>? {
|
||||
val classes = aggregateClasses(importedModules, localMini)
|
||||
fun dfs(name: String, visited: MutableSet<String>): Pair<String, MiniNamedDecl>? {
|
||||
@ -275,6 +304,8 @@ object DocLookupUtils {
|
||||
val cls = classes[name]
|
||||
if (cls != null) {
|
||||
cls.members.firstOrNull { it.name == member }?.let { return name to it }
|
||||
cls.ctorFields.firstOrNull { it.name == member }?.let { return name to toMemberVal(it) }
|
||||
cls.classFields.firstOrNull { it.name == member }?.let { return name to toMemberVal(it) }
|
||||
for (baseName in cls.bases) {
|
||||
dfs(baseName, visited)?.let { return it }
|
||||
}
|
||||
@ -656,6 +687,12 @@ object DocLookupUtils {
|
||||
val identRange = wordRangeAt(text, i + 1)
|
||||
if (identRange != null) {
|
||||
val ident = text.substring(identRange.first, identRange.second)
|
||||
|
||||
// 3a) Handle plain "this"
|
||||
if (ident == "this" && mini != null) {
|
||||
findEnclosingClass(mini, identRange.first)?.let { return it.name }
|
||||
}
|
||||
|
||||
// if it's "as Type", we want Type
|
||||
var k = prevNonWs(text, identRange.first - 1)
|
||||
if (k >= 1 && text[k] == 's' && text[k - 1] == 'a' && (k - 1 == 0 || !text[k - 2].isLetterOrDigit())) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user