mirror of
https://github.com/fluencelabs/aqua.git
synced 2024-12-04 14:40:17 +00:00
feat(language-server): Add types for types in LSP API (#1078)
This commit is contained in:
parent
9423ffc509
commit
3cd31c5827
29
integration-tests/lsp-aqua/types.aqua
Normal file
29
integration-tests/lsp-aqua/types.aqua
Normal file
@ -0,0 +1,29 @@
|
||||
aqua Types
|
||||
|
||||
alias Top: ⊤
|
||||
alias Bottom: ⊥
|
||||
|
||||
alias Number: u32
|
||||
alias String: string
|
||||
alias Array: []string
|
||||
alias Stream: *string
|
||||
alias Option: ?string
|
||||
|
||||
data Struct:
|
||||
a: Number
|
||||
b: String
|
||||
c: Array
|
||||
d: Option
|
||||
|
||||
service Srv("srv"):
|
||||
noop(srvArg: string)
|
||||
|
||||
ability Ability:
|
||||
a: Number
|
||||
b: String
|
||||
|
||||
func nilArrow():
|
||||
Srv.noop("")
|
||||
|
||||
func fullArrow(a: string, b: u32) -> string, u32:
|
||||
<- a, b
|
@ -18,16 +18,14 @@
|
||||
"scripts": {
|
||||
"build": "tsc & npm run compile-aqua",
|
||||
"test": "NODE_OPTIONS=--experimental-vm-modules jest --detectOpenHandles",
|
||||
"test:lsp": "NODE_OPTIONS=--experimental-vm-modules jest src/__test__/lsp-types.spec.ts --detectOpenHandles",
|
||||
"examples": "jest",
|
||||
"pubsub": "node -r ts-node/register src/pubsub.ts",
|
||||
"exec": "npm run compile-aqua && npm run prettify-compiled && node -r ts-node/register src/index.ts",
|
||||
"run": "node -r ts-node/register src/index.ts",
|
||||
"compile-aqua": "node --loader ts-node/esm ./src/compile.ts",
|
||||
"compile-aqua:air": "aqua -i ./aqua/ -o ./compiled-air -a",
|
||||
"prettify-compiled": "prettier --write src/compiled",
|
||||
"prettify": "prettier --write src",
|
||||
"aqua": "aqua",
|
||||
"do": "aqua dist deploy --addr /dns4/kras-04.fluence.dev/tcp/19001/wss/p2p/12D3KooWFEwNWcHqi9rtsmDhsYcDbRUCDXH84RC4FW6UfsFWaoHi --config-path deploy.json --service tsOracle"
|
||||
"prettify": "prettier --write src"
|
||||
},
|
||||
"prettier": {},
|
||||
"devDependencies": {
|
||||
|
103
integration-tests/src/__test__/lsp-types.spec.ts
Normal file
103
integration-tests/src/__test__/lsp-types.spec.ts
Normal file
@ -0,0 +1,103 @@
|
||||
describe("Testing LSP types", () => {
|
||||
it("dummy", async () => {
|
||||
expect(true).toBeTruthy()
|
||||
})
|
||||
})
|
||||
|
||||
// FIXME: these tests work only with LSP ESM build (ModuleKind.ESModule in build.sbt)
|
||||
/*
|
||||
import {
|
||||
AbilityType,
|
||||
AquaLSP, ArrayType, BottomType,
|
||||
OptionType,
|
||||
ScalarType, StreamType, StructType,
|
||||
TopType,
|
||||
Type, ServiceType, ArrowType
|
||||
} from '@fluencelabs/aqua-language-server-api/aqua-lsp-api';
|
||||
|
||||
describe("Testing LSP types", () => {
|
||||
it("check types in aqua file", async () => {
|
||||
const compiled = await AquaLSP.compile("lsp-aqua/types.aqua", {})
|
||||
const types = compiled.tokens.map(ti => ti.type)
|
||||
|
||||
const isTop = (type: Type): type is TopType => type.tag === "top";
|
||||
const tops = types.filter(isTop)
|
||||
expect(tops).toHaveLength(1)
|
||||
|
||||
const isBottom = (type: Type): type is BottomType => type.tag === "bottom";
|
||||
const bottom = types.filter(isBottom)
|
||||
expect(bottom).toHaveLength(1)
|
||||
|
||||
const isScalar = (type: Type): type is ScalarType => type.tag === "scalar";
|
||||
const scalars = types.filter(isScalar)
|
||||
expect(scalars).toHaveLength(8)
|
||||
scalars.forEach(sc => expect(sc.name).toBeDefined())
|
||||
|
||||
const isArray = (type: Type): type is ArrayType => type.tag === "array";
|
||||
const arrays = types.filter(isArray)
|
||||
expect(arrays).toHaveLength(2)
|
||||
arrays.forEach(sc => expect(sc.element).toBeDefined())
|
||||
|
||||
const isOption = (type: Type): type is OptionType => type.tag === "option";
|
||||
const options = types.filter(isOption)
|
||||
expect(options).toHaveLength(2)
|
||||
options.forEach(sc => expect(sc.element).toBeDefined())
|
||||
|
||||
const isStream = (type: Type): type is StreamType => type.tag === "stream";
|
||||
const streams = types.filter(isStream)
|
||||
expect(streams).toHaveLength(1)
|
||||
streams.forEach(sc => expect(sc.element).toBeDefined())
|
||||
|
||||
const isAbility = (type: Type): type is AbilityType => type.tag === "ability";
|
||||
const abilities = types.filter(isAbility)
|
||||
expect(abilities).toHaveLength(1)
|
||||
abilities.forEach((sc) => {
|
||||
expect(sc.name).toBeDefined()
|
||||
expect(sc.fields).toBeDefined()
|
||||
expect(Object.entries(sc.fields)).toHaveLength(2)
|
||||
})
|
||||
|
||||
const isStruct = (type: Type): type is StructType => type.tag === "struct";
|
||||
const structs = types.filter(isStruct)
|
||||
expect(structs).toHaveLength(1)
|
||||
structs.forEach((sc) => {
|
||||
expect(sc.name).toBeDefined()
|
||||
expect(sc.fields).toBeDefined()
|
||||
expect(Object.entries(sc.fields)).toHaveLength(4)
|
||||
})
|
||||
|
||||
const isService = (type: Type): type is ServiceType => type.tag === "service";
|
||||
const services = types.filter(isService)
|
||||
expect(services).toHaveLength(1)
|
||||
services.forEach((sc) => {
|
||||
expect(sc.name).toBeDefined()
|
||||
expect(sc.fields).toBeDefined()
|
||||
expect(Object.entries(sc.fields)).toHaveLength(1)
|
||||
})
|
||||
|
||||
const isArrow = (type: Type): type is ArrowType => type.tag === "arrow";
|
||||
const arrows = types.filter(isArrow)
|
||||
expect(arrows).toHaveLength(3)
|
||||
|
||||
const fullArrow = arrows[0]
|
||||
const argA = fullArrow.domain.args["a"]
|
||||
expect(argA).toEqual({name: "string", tag: "scalar"})
|
||||
|
||||
const argB = fullArrow.domain.args["b"]
|
||||
expect(argB).toEqual({name: "u32", tag: "scalar"})
|
||||
|
||||
const codomain = fullArrow.codomain.types
|
||||
expect(codomain[0]).toEqual({name: "string", tag: "scalar"})
|
||||
expect(codomain[1]).toEqual({name: "u32", tag: "scalar"})
|
||||
|
||||
const nilArrow = arrows[1]
|
||||
expect(nilArrow.domain.args).toEqual({})
|
||||
expect(nilArrow.codomain.types).toEqual([])
|
||||
|
||||
const srvNoopArrow = arrows[2]
|
||||
expect(srvNoopArrow.domain.args).toEqual({srvArg: {name: "string", tag: "scalar"}})
|
||||
expect(srvNoopArrow.codomain.types).toEqual([])
|
||||
|
||||
})
|
||||
|
||||
})*/
|
@ -4,7 +4,7 @@ import aqua.parser.lift.FileSpan
|
||||
|
||||
import scala.scalajs.js
|
||||
import scala.scalajs.js.annotation.JSExportAll
|
||||
import scala.scalajs.js.{UndefOr, undefined}
|
||||
import scala.scalajs.js.{undefined, UndefOr}
|
||||
|
||||
@JSExportAll
|
||||
case class CompilationResult(
|
||||
@ -16,7 +16,7 @@ case class CompilationResult(
|
||||
)
|
||||
|
||||
@JSExportAll
|
||||
case class ExprInfoJs(location: TokenLocation, `type`: String)
|
||||
case class ExprInfoJs(location: TokenLocation, `type`: TypeJs)
|
||||
|
||||
@JSExportAll
|
||||
case class TokenLocation(name: String, startLine: Int, startCol: Int, endLine: Int, endCol: Int)
|
||||
|
@ -84,8 +84,8 @@ object ResultHelper extends Logging {
|
||||
private def tokensToJs(tokens: List[DefinitionInfo[FileSpan.F]]): js.Array[ExprInfoJs] =
|
||||
tokens.flatMap { ti =>
|
||||
TokenLocation.fromSpan(ti.token.unit._1).map { tl =>
|
||||
val typeName = ti.`type`.show
|
||||
ExprInfoJs(tl, typeName)
|
||||
val typeDef = TypeJs.fromType(ti.`type`)
|
||||
ExprInfoJs(tl, typeDef)
|
||||
}
|
||||
}.toJSArray
|
||||
|
||||
|
@ -0,0 +1,105 @@
|
||||
package aqua.lsp
|
||||
|
||||
import aqua.types.*
|
||||
|
||||
import scala.scalajs.js.Dictionary
|
||||
import scala.scalajs.js.JSConverters.*
|
||||
import scalajs.js
|
||||
|
||||
sealed trait TypeJs extends js.Object {
|
||||
val tag: String
|
||||
}
|
||||
|
||||
class ScalarTypeJs(val name: String) extends TypeJs {
|
||||
val tag: String = "scalar"
|
||||
}
|
||||
|
||||
class ArrayTypeJs(val element: TypeJs) extends TypeJs {
|
||||
val tag: String = "array"
|
||||
}
|
||||
|
||||
class OptionTypeJs(val element: TypeJs) extends TypeJs {
|
||||
val tag: String = "option"
|
||||
}
|
||||
|
||||
class StreamTypeJs(val element: TypeJs) extends TypeJs {
|
||||
val tag: String = "stream"
|
||||
}
|
||||
|
||||
class StreamMapTypeJs(val element: TypeJs) extends TypeJs {
|
||||
val tag: String = "streammap"
|
||||
}
|
||||
|
||||
class CanonStreamTypeJs(val element: TypeJs) extends TypeJs {
|
||||
val tag: String = "canon"
|
||||
}
|
||||
|
||||
class AbilityTypeJs(val name: String, val fields: js.Dictionary[TypeJs]) extends TypeJs {
|
||||
val tag: String = "ability"
|
||||
}
|
||||
|
||||
class StructTypeJs(val name: String, val fields: js.Dictionary[TypeJs]) extends TypeJs {
|
||||
val tag: String = "struct"
|
||||
}
|
||||
|
||||
class ServiceTypeJs(val name: String, val fields: js.Dictionary[TypeJs]) extends TypeJs {
|
||||
val tag: String = "service"
|
||||
}
|
||||
|
||||
trait ProductType extends TypeJs
|
||||
|
||||
class LabeledConsTypeJs(val args: js.Dictionary[TypeJs]) extends TypeJs {
|
||||
val tag: String = "labeled"
|
||||
}
|
||||
|
||||
class UnlabeledConsTypeJs(val types: js.Array[TypeJs]) extends TypeJs {
|
||||
val tag: String = "unlabeled"
|
||||
}
|
||||
|
||||
class ArrowTypeJs(val domain: LabeledConsTypeJs, val codomain: UnlabeledConsTypeJs) extends TypeJs {
|
||||
val tag: String = "arrow"
|
||||
}
|
||||
|
||||
class NilTypeJs extends TypeJs {
|
||||
val tag: String = "nil"
|
||||
}
|
||||
|
||||
class BottomTypeJs extends TypeJs {
|
||||
val tag: String = "bottom"
|
||||
}
|
||||
|
||||
class TopTypeJs extends TypeJs {
|
||||
val tag: String = "top"
|
||||
}
|
||||
|
||||
object TypeJs {
|
||||
|
||||
def typeList(types: Iterable[(String, Type)]): Dictionary[TypeJs] =
|
||||
js.Dictionary(types.map { case (n, t) =>
|
||||
(n, TypeJs.fromType(t))
|
||||
}.toSeq: _*)
|
||||
|
||||
def fromType(t: Type): TypeJs = {
|
||||
t match
|
||||
case ScalarType(name) => new ScalarTypeJs(name)
|
||||
case LiteralType(_, name) => new ScalarTypeJs(name)
|
||||
case ArrayType(el) => new ArrayTypeJs(fromType(el))
|
||||
case OptionType(el) => new OptionTypeJs(fromType(el))
|
||||
case StreamType(el) => new StreamTypeJs(fromType(el))
|
||||
case StreamMapType(el) => new StreamMapTypeJs(fromType(el))
|
||||
case CanonStreamType(el) => new CanonStreamTypeJs(fromType(el))
|
||||
case StructType(name, fields) => new StructTypeJs(name, typeList(fields.toSortedMap))
|
||||
case AbilityType(name, fields) => new AbilityTypeJs(name, typeList(fields.toSortedMap))
|
||||
case ServiceType(name, fields) => new ServiceTypeJs(name, typeList(fields.toSortedMap))
|
||||
case lct: LabeledConsType => new LabeledConsTypeJs(typeList(lct.toLabelledList()))
|
||||
case uct: UnlabeledConsType => new UnlabeledConsTypeJs(uct.toList.map(fromType).toJSArray)
|
||||
case ArrowType(domain, codomain) =>
|
||||
ArrowTypeJs(
|
||||
new LabeledConsTypeJs(typeList(domain.toLabelledList())),
|
||||
new UnlabeledConsTypeJs(codomain.toList.map(fromType).toJSArray)
|
||||
)
|
||||
case TopType => new TopTypeJs()
|
||||
case BottomType => new BottomTypeJs()
|
||||
case NilType => new NilTypeJs()
|
||||
}
|
||||
}
|
@ -1,48 +1,141 @@
|
||||
export interface ScalarType {
|
||||
name: string,
|
||||
tag: "scalar"
|
||||
}
|
||||
|
||||
export interface ArrayType {
|
||||
element: Type,
|
||||
tag: "array"
|
||||
}
|
||||
|
||||
export interface OptionType {
|
||||
element: Type,
|
||||
tag: "option"
|
||||
}
|
||||
|
||||
export interface StreamType {
|
||||
element: Type,
|
||||
tag: "stream"
|
||||
}
|
||||
|
||||
export interface StreamMapType {
|
||||
element: Type,
|
||||
tag: "streammap"
|
||||
}
|
||||
|
||||
export interface CanonStreamType {
|
||||
element: Type,
|
||||
tag: "canon"
|
||||
}
|
||||
|
||||
export interface AbilityType {
|
||||
name: string,
|
||||
fields: Record<string, Type>,
|
||||
tag: "ability"
|
||||
}
|
||||
|
||||
export interface StructType {
|
||||
name: string,
|
||||
fields: Record<string, Type>,
|
||||
tag: "struct"
|
||||
}
|
||||
|
||||
export interface ServiceType {
|
||||
name: string,
|
||||
fields: Record<string, Type>,
|
||||
tag: "service"
|
||||
}
|
||||
|
||||
export interface LabeledConsType {
|
||||
args: Record<string, Type>,
|
||||
tag: "labeled"
|
||||
}
|
||||
|
||||
export interface UnlabeledConsType {
|
||||
types: Type[],
|
||||
tag: "unlabeled"
|
||||
}
|
||||
|
||||
export interface ArrowType {
|
||||
domain: LabeledConsType
|
||||
codomain: UnlabeledConsType,
|
||||
tag: "arrow"
|
||||
}
|
||||
|
||||
export interface NilType {
|
||||
tag: "nil"
|
||||
}
|
||||
|
||||
export interface BottomType {
|
||||
tag: "bottom"
|
||||
}
|
||||
|
||||
export interface TopType {
|
||||
tag: "top"
|
||||
}
|
||||
|
||||
export type Type =
|
||||
ScalarType
|
||||
| ArrayType
|
||||
| OptionType
|
||||
| StreamType
|
||||
| StreamMapType
|
||||
| CanonStreamType
|
||||
| AbilityType
|
||||
| StructType
|
||||
| ServiceType
|
||||
| LabeledConsType
|
||||
| UnlabeledConsType
|
||||
| ArrowType
|
||||
| NilType
|
||||
| TopType
|
||||
| BottomType
|
||||
|
||||
export interface TokenLocation {
|
||||
name: string;
|
||||
startLine: number;
|
||||
startCol: number;
|
||||
endLine: number;
|
||||
endCol: number;
|
||||
name: string;
|
||||
startLine: number;
|
||||
startCol: number;
|
||||
endLine: number;
|
||||
endCol: number;
|
||||
}
|
||||
|
||||
export interface TokenInfo {
|
||||
location: TokenLocation;
|
||||
type: string;
|
||||
location: TokenLocation;
|
||||
type: Type;
|
||||
}
|
||||
|
||||
export interface TokenLink {
|
||||
current: TokenLocation;
|
||||
definition: TokenLocation;
|
||||
current: TokenLocation;
|
||||
definition: TokenLocation;
|
||||
}
|
||||
|
||||
export interface TokenImport {
|
||||
current: TokenLocation;
|
||||
path: string;
|
||||
current: TokenLocation;
|
||||
path: string;
|
||||
}
|
||||
|
||||
export interface ErrorInfo {
|
||||
infoType: "error";
|
||||
start: number;
|
||||
end: number;
|
||||
message: string;
|
||||
location: string | null;
|
||||
infoType: "error";
|
||||
start: number;
|
||||
end: number;
|
||||
message: string;
|
||||
location: string | null;
|
||||
}
|
||||
|
||||
export interface WarningInfo {
|
||||
infoType: "warning";
|
||||
start: number;
|
||||
end: number;
|
||||
message: string;
|
||||
location: string | null;
|
||||
infoType: "warning";
|
||||
start: number;
|
||||
end: number;
|
||||
message: string;
|
||||
location: string | null;
|
||||
}
|
||||
|
||||
export interface CompilationResult {
|
||||
errors: ErrorInfo[];
|
||||
warnings: WarningInfo[];
|
||||
locations: TokenLink[];
|
||||
importLocations: TokenImport[];
|
||||
tokens: TokenInfo[];
|
||||
errors: ErrorInfo[];
|
||||
warnings: WarningInfo[];
|
||||
locations: TokenLink[];
|
||||
importLocations: TokenImport[];
|
||||
tokens: TokenInfo[];
|
||||
}
|
||||
|
||||
/*
|
||||
@ -59,7 +152,7 @@ export interface CompilationResult {
|
||||
export type Imports = Record<string, Record<string, string[]>>;
|
||||
|
||||
export class Compiler {
|
||||
compile(path: string, imports: Imports): Promise<CompilationResult>;
|
||||
compile(path: string, imports: Imports): Promise<CompilationResult>;
|
||||
}
|
||||
|
||||
export var AquaLSP: Compiler;
|
||||
|
Loading…
Reference in New Issue
Block a user