Skip to content

Commit a1cd636

Browse files
committed
feat(completion/resolving/documentation): add support for fromCell and fromSlice methods on structs and messages
Fixes #86 #187
1 parent 527de9b commit a1cd636

File tree

9 files changed

+209
-3
lines changed

9 files changed

+209
-3
lines changed

server/src/TypeInferer.ts

+17-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,12 @@ export class TypeInferer {
4444
if (node.node.type === "type_identifier") {
4545
const resolved = Reference.resolve(new NamedNode(node.node, node.file))
4646
if (resolved === null) {
47-
if (node.node.text === "K" || node.node.text === "V") {
47+
if (
48+
node.node.text === "K" ||
49+
node.node.text === "V" ||
50+
node.node.text === "S" ||
51+
node.node.text === "M"
52+
) {
4853
return new PlaceholderTy(node.node.text, new NamedNode(node.node, node.file))
4954
}
5055
return null
@@ -224,12 +229,23 @@ export class TypeInferer {
224229
const resolved = Reference.resolve(element)
225230
if (resolved === null) return null
226231
if (!(resolved instanceof Fun)) return null
232+
const calledName = resolved.name()
227233

228234
const returnType = resolved.returnType()
229235
if (returnType === null) return null
230236
const type = this.inferTypeMaybeOption(returnType.node, returnType)
231237
if (!type) return null
232238

239+
const qualifier = node.node.childForFieldName("object")
240+
241+
// Handle `Struct.fromCell()` calls
242+
if (
243+
qualifier &&
244+
(calledName.startsWith("AnyStruct_") || calledName.startsWith("AnyMessage_"))
245+
) {
246+
return new Expression(qualifier, node.file).type()
247+
}
248+
233249
// following code is completely garbage, we need to replace it with actual generics
234250
if (
235251
type instanceof PlaceholderTy ||

server/src/completion/ReferenceCompletionProcessor.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
} from "@server/completion/WeightedCompletionItem"
1212
import {StructTy} from "@server/types/BaseTy"
1313
import {tactCodeBlock} from "@server/documentation/documentation"
14+
import {trimPrefix} from "@server/utils/strings"
1415

1516
export class ReferenceCompletionProcessor implements ScopeProcessor {
1617
public constructor(private readonly ctx: CompletionContext) {}
@@ -58,7 +59,7 @@ export class ReferenceCompletionProcessor implements ScopeProcessor {
5859
if (!(node instanceof NamedNode)) return true
5960

6061
const prefix = state.get("prefix") ?? ""
61-
const name = node.name()
62+
const name = trimPrefix(trimPrefix(node.name(), "AnyMessage_"), "AnyStruct_")
6263
if (name.endsWith("DummyIdentifier") || name === "AnyStruct" || name === "AnyMessage") {
6364
return true
6465
}

server/src/documentation/documentation.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,10 @@ export function generateDocFor(node: NamedNode): string | null {
4444
const func = new Fun(astNode, node.file)
4545
const doc = extractCommentsDoc(node)
4646

47+
const name = trimPrefix(trimPrefix(node.name(), "AnyMessage_"), "AnyStruct_")
48+
4749
return defaultResult(
48-
`${func.modifiers()}fun ${node.name()}${func.signaturePresentation()}`,
50+
`${func.modifiers()}fun ${name}${func.signaturePresentation()}`,
4951
doc,
5052
)
5153
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
========================================================================
2+
fromCell/fromSlice for Struct
3+
========================================================================
4+
primitive Int;
5+
6+
fun AnyStruct_fromCell(cell: Cell): S {}
7+
fun AnyStruct_fromSlice(slice: Slice): S {}
8+
9+
struct Foo {}
10+
11+
fun test() {
12+
Foo.<caret>;
13+
}
14+
------------------------------------------------------------------------
15+
2 fromCell(cell: Cell): S
16+
2 fromSlice(slice: Slice): S
17+
14 call Use as function argument
18+
14 do Create do-until loop
19+
14 if Create if statement
20+
14 let Create variable
21+
14 not Negate expression
22+
14 repeat Create repeat loop
23+
24+
========================================================================
25+
fromCell/fromSlice for message
26+
========================================================================
27+
primitive Int;
28+
29+
fun AnyMessage_fromCell(cell: Cell): M {}
30+
fun AnyMessage_fromSlice(slice: Slice): M {}
31+
32+
message Foo {}
33+
34+
fun test() {
35+
Foo.<caret>;
36+
}
37+
------------------------------------------------------------------------
38+
2 fromCell(cell: Cell): M
39+
2 fromSlice(slice: Slice): M
40+
14 call Use as function argument
41+
14 do Create do-until loop
42+
14 if Create if statement
43+
14 let Create variable
44+
14 not Negate expression
45+
14 repeat Create repeat loop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
========================================================================
2+
fromCell/fromSlice for Struct
3+
========================================================================
4+
primitive Int;
5+
6+
fun AnyStruct_fromCell(cell: Cell): S {}
7+
fun AnyStruct_fromSlice(slice: Slice): S {}
8+
9+
struct Foo {}
10+
11+
fun test() {
12+
Foo.<caret>fromCell();
13+
}
14+
------------------------------------------------------------------------
15+
```tact
16+
fun fromCell(cell: Cell): S
17+
```
18+
19+
========================================================================
20+
fromCell/fromSlice for message
21+
========================================================================
22+
primitive Int;
23+
24+
fun AnyMessage_fromCell(cell: Cell): M {}
25+
fun AnyMessage_fromSlice(slice: Slice): M {}
26+
27+
message Foo {}
28+
29+
fun test() {
30+
Foo.<caret>fromSlice();
31+
}
32+
------------------------------------------------------------------------
33+
```tact
34+
fun fromSlice(slice: Slice): M
35+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
========================================================================
2+
fromCell/fromSlice for Struct
3+
========================================================================
4+
primitive Int;
5+
6+
fun AnyStruct_fromCell(cell: Cell): S {}
7+
fun AnyStruct_fromSlice(slice: Slice): S {}
8+
9+
struct Foo {}
10+
11+
fun test() {
12+
Foo.<caret>fromCell();
13+
}
14+
------------------------------------------------------------------------
15+
8:8 -> 2:4 resolved
16+
17+
========================================================================
18+
fromCell/fromSlice for message
19+
========================================================================
20+
primitive Int;
21+
22+
fun AnyMessage_fromCell(cell: Cell): M {}
23+
fun AnyMessage_fromSlice(slice: Slice): M {}
24+
25+
message Foo {}
26+
27+
fun test() {
28+
Foo.<caret>fromSlice();
29+
}
30+
------------------------------------------------------------------------
31+
8:8 -> 3:4 resolved
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
========================================================================
2+
fromCell/fromSlice for Struct
3+
========================================================================
4+
primitive Int;
5+
6+
fun AnyStruct_fromCell(cell: Cell): S {}
7+
fun AnyStruct_fromSlice(slice: Slice): S {}
8+
9+
struct Foo {}
10+
11+
fun test() {
12+
let f = Foo.<caret>;
13+
// ^! Foo
14+
}
15+
------------------------------------------------------------------------
16+
ok
17+
18+
========================================================================
19+
fromCell/fromSlice for message
20+
========================================================================
21+
primitive Int;
22+
23+
fun AnyMessage_fromCell(cell: Cell): M {}
24+
fun AnyMessage_fromSlice(slice: Slice): M {}
25+
26+
message Foo {}
27+
28+
fun test() {
29+
let f = Foo.<caret>;
30+
// ^! Foo
31+
}
32+
------------------------------------------------------------------------
33+
ok

server/src/psi/Reference.ts

+37
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,36 @@ export class Reference {
179179
if (qualifierType === null) return true
180180

181181
if (qualifierType instanceof StructTy || qualifierType instanceof MessageTy) {
182+
if (qualifier.node.type === "identifier") {
183+
const resolved = Reference.resolve(new NamedNode(qualifier.node, qualifier.file))
184+
if (resolved instanceof Struct || resolved instanceof Message) {
185+
// found `Foo.fromCell` case
186+
const prefix = resolved instanceof Struct ? "AnyStruct_" : "AnyMessage_"
187+
188+
const fromCellName = prefix + "fromCell"
189+
const fromCell = index.elementByName(IndexKey.Funs, fromCellName)
190+
if (fromCell) {
191+
const newState = state.withValue(
192+
"search-name",
193+
prefix + this.element.name(),
194+
)
195+
if (!proc.execute(fromCell, newState)) return false
196+
}
197+
198+
const fromSliceName = prefix + "fromSlice"
199+
const fromSlice = index.elementByName(IndexKey.Funs, fromSliceName)
200+
if (fromSlice) {
201+
const newState = state.withValue(
202+
"search-name",
203+
prefix + this.element.name(),
204+
)
205+
if (!proc.execute(fromSlice, newState)) return false
206+
}
207+
208+
return true
209+
}
210+
}
211+
182212
const nodeStruct = index.elementByName(IndexKey.Primitives, "AnyStruct")
183213
if (nodeStruct) {
184214
const structPrimitiveTy = new PrimitiveTy("AnyStruct", nodeStruct, null)
@@ -418,6 +448,13 @@ export class Reference {
418448
public execute(node: Node, state: ResolveState): boolean {
419449
if (!(node instanceof Fun)) return true
420450
if (node.withSelf()) return true // don't add methods to unqualified completion
451+
if (
452+
node.name().startsWith("AnyStruct_") ||
453+
node.name().startsWith("AnyMessage_")
454+
) {
455+
// this functions in fact static methods
456+
return true
457+
}
421458
return proc.execute(node, state)
422459
}
423460
})()

stubs/stubs.tact

+6
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,12 @@ primitive AnyStruct;
131131
/// Pseudo-type that represents any Message.
132132
primitive AnyMessage;
133133

134+
fun AnyStruct_fromCell(cell: Cell): S {}
135+
fun AnyStruct_fromSlice(slice: Slice): S {}
136+
137+
fun AnyMessage_fromCell(cell: Cell): M {}
138+
fun AnyMessage_fromSlice(slice: Slice): M {}
139+
134140
/// Extension function for any Struct.
135141
///
136142
/// Converts the Struct to a `Cell` and returns it.

0 commit comments

Comments
 (0)