Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changeset/effect-v4-completions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"@effect/language-service": minor
---

Add Effect v4 completions support

- Detect installed Effect version (v3 or v4) and conditionally enable version-specific completions
- Add `Schema.ErrorClass` and `Schema.RequestClass` completions for Effect v4
- Disable v3-only completions (`Effect.Service`, `Effect.Tag`, `Schema.TaggedError`, `Schema.TaggedClass`, `Schema.TaggedRequest`, `Context.Tag` self, `Rpc.make` classes, `Schema.brand`, `Model.Class`) when Effect v4 is detected
- Support lowercase `taggedEnum` in addition to `TaggedEnum` for v4 API compatibility
232 changes: 232 additions & 0 deletions packages/harness-effect-v4/__snapshots__/completions.test.ts.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,145 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`Completion contextSelfInClasses > contextSelfInClasses.ts at 4:40 1`] = `
[
{
"insertText": "Context.Tag("@effect/harness-effect-v4/MyService")<MyService, \${0}>(){}",
"isSnippet": true,
"kind": "const",
"name": "Tag("MyService")",
"replacementSpan": {
"length": 8,
"start": 144,
},
"sortText": "11",
},
]
`;

exports[`Completion contextSelfInClasses > contextSelfInClasses_directImportTag.ts at 4:35 1`] = `[]`;

exports[`Completion contextSelfInClasses > contextSelfInClasses_identifierKey.ts at 5:40 1`] = `
[
{
"insertText": "Context.Tag("@effect/harness-effect-v4/MyService")<MyService, \${0}>(){}",
"isSnippet": true,
"kind": "const",
"name": "Tag("MyService")",
"replacementSpan": {
"length": 8,
"start": 240,
},
"sortText": "11",
},
]
`;

exports[`Completion durationInput > durationInput.ts at 4:38 1`] = `
[
{
"insertText": "\${0} nanos",
"isSnippet": true,
"kind": "string",
"name": "nanos",
"sortText": "11",
},
{
"insertText": "\${0} micros",
"isSnippet": true,
"kind": "string",
"name": "micros",
"sortText": "11",
},
{
"insertText": "\${0} millis",
"isSnippet": true,
"kind": "string",
"name": "millis",
"sortText": "11",
},
{
"insertText": "\${0} seconds",
"isSnippet": true,
"kind": "string",
"name": "seconds",
"sortText": "11",
},
{
"insertText": "\${0} minutes",
"isSnippet": true,
"kind": "string",
"name": "minutes",
"sortText": "11",
},
{
"insertText": "\${0} hours",
"isSnippet": true,
"kind": "string",
"name": "hours",
"sortText": "11",
},
{
"insertText": "\${0} days",
"isSnippet": true,
"kind": "string",
"name": "days",
"sortText": "11",
},
{
"insertText": "\${0} weeks",
"isSnippet": true,
"kind": "string",
"name": "weeks",
"sortText": "11",
},
]
`;

exports[`Completion durationInput > durationInput_props.ts at 5:14 1`] = `[]`;

exports[`Completion effectCodegensComment > effectCodegensComment.ts at 2:5 1`] = `
[
{
"insertText": "@effect-codegens \${1|accessors,annotate,typeToSchema|} $0",
"isSnippet": true,
"kind": "string",
"name": "@effect-codegens",
"replacementSpan": {
"length": 1,
"start": 73,
},
"sortText": "11",
},
]
`;

exports[`Completion effectDataClasses > effectDataClasses.ts at 4:35 1`] = `
[
{
"insertText": "Data.TaggedError("MyError")<{\${0}}>{}",
"isSnippet": true,
"kind": "const",
"name": "TaggedError("MyError")",
"replacementSpan": {
"length": 5,
"start": 133,
},
"sortText": "11",
},
{
"insertText": "Data.TaggedClass("MyError")<{\${0}}>{}",
"isSnippet": true,
"kind": "const",
"name": "TaggedClass("MyError")",
"replacementSpan": {
"length": 5,
"start": 133,
},
"sortText": "11",
},
]
`;

exports[`Completion effectDiagnosticsComment > effectDiagnosticsComment.ts at 2:5 1`] = `
[
{
Expand Down Expand Up @@ -43,6 +183,98 @@ exports[`Completion effectJsdocComment > effectJsdocComment.ts at 2:6 1`] = `
]
`;

exports[`Completion effectSchemaSelfInClasses > effectSchemaSelfInClasses_directImportClass.ts at 4:35 1`] = `
[
{
"insertText": "Class<MyClass>("MyClass")({\${0}}){}",
"isSnippet": true,
"kind": "const",
"name": "Class<MyClass>",
"replacementSpan": {
"length": 5,
"start": 143,
},
"sortText": "11",
},
]
`;

exports[`Completion effectSchemaSelfInClasses > effectSchemaSelfInClasses_dotToken.ts at 4:22 1`] = `
[
{
"insertText": "S.Class<Test>("Test")({\${0}}){}",
"isSnippet": true,
"kind": "const",
"name": "Class<Test>",
"replacementSpan": {
"length": 2,
"start": 130,
},
"sortText": "11",
},
{
"insertText": "S.ErrorClass<Test>()({\${0}}){}",
"isSnippet": true,
"kind": "const",
"name": "ErrorClass<Test>",
"replacementSpan": {
"length": 2,
"start": 130,
},
"sortText": "11",
},
{
"insertText": "S.RequestClass<Test>("Test")({\${0}}){}",
"isSnippet": true,
"kind": "const",
"name": "RequestClass<Test>",
"replacementSpan": {
"length": 2,
"start": 130,
},
"sortText": "11",
},
]
`;

exports[`Completion effectSchemaSelfInClasses > effectSchemaSelfInClasses_tagg.ts at 4:25 1`] = `
[
{
"insertText": "S.Class<Test>("Test")({\${0}}){}",
"isSnippet": true,
"kind": "const",
"name": "Class<Test>",
"replacementSpan": {
"length": 5,
"start": 130,
},
"sortText": "11",
},
{
"insertText": "S.ErrorClass<Test>()({\${0}}){}",
"isSnippet": true,
"kind": "const",
"name": "ErrorClass<Test>",
"replacementSpan": {
"length": 5,
"start": 130,
},
"sortText": "11",
},
{
"insertText": "S.RequestClass<Test>("Test")({\${0}}){}",
"isSnippet": true,
"kind": "const",
"name": "RequestClass<Test>",
"replacementSpan": {
"length": 5,
"start": 130,
},
"sortText": "11",
},
]
`;

exports[`Completion fnFunctionStar > fnFunctionStar.ts at 4:24 1`] = `
[
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// 4:40
import * as Context from "effect/Context"

export class MyService extends Context.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// 4:35
import { Tag } from "effect/Context"

export class MyService extends Tag
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// 5:40
// @test-config { "keyPatterns": [ { "pattern": "package-identifier", "target": "service" } ] }
import * as Context from "effect/Context"

export class MyService extends Context.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// 4:38
import * as Effect from "effect/Effect"

export const program = Effect.sleep("")
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// 5:14
import * as Effect from "effect/Effect"

export const program = Effect.timeoutFail(Effect.never, {
duration: ""
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// 2:5
// @
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// 2:5
// @
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// 4:35
import * as Data from "effect/Data"

export class MyError extends Data.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// 4:35
import { Class } from "effect/Schema"

export class MyClass extends Class
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// 4:22
import * as S from "effect/Schema"

class Test extends S.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// 4:25
import * as S from "effect/Schema"

class Test extends S.tag
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const contextSelfInClasses = LSP.createCompletion({
const ts = yield* Nano.service(TypeScriptApi.TypeScriptApi)
const tsUtils = yield* Nano.service(TypeScriptUtils.TypeScriptUtils)
const typeParser = yield* Nano.service(TypeParser.TypeParser)
if (typeParser.supportedEffect() === "v4") return []

const maybeInfos = tsUtils.parseDataForExtendsClassCompletion(sourceFile, position)
if (!maybeInfos) return []
Expand Down
Loading
Loading