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
5 changes: 5 additions & 0 deletions .changeset/tidy-dragons-smile.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@effect/language-service": patch
---

Update Effect v4 compatibility to 4.0.0-beta.104, including the renamed `Schema.Error` and `Schema.TaggedError` constructors.
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,10 @@
"typescript": "^5.9.3",
"vite": "^7.3.1",
"vitest": "^4.0.17"
},
"pnpm": {
"overrides": {
"@effect/platform-node-shared": "4.0.0-beta.104"
}
}
}
48 changes: 40 additions & 8 deletions packages/harness-effect-v4/__snapshots__/completions.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,38 @@ exports[`Completion effectSchemaSelfInClasses > effectSchemaSelfInClasses_direct
]
`;

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

exports[`Completion effectSchemaSelfInClasses > effectSchemaSelfInClasses_directImportTaggedError.ts at 4:40 1`] = `
[
{
"insertText": "TaggedError<MyError>()("MyError", {\${0}}){}",
"isSnippet": true,
"kind": "const",
"name": "TaggedError<MyError>",
"replacementSpan": {
"length": 11,
"start": 149,
},
"sortText": "11",
},
]
`;

exports[`Completion effectSchemaSelfInClasses > effectSchemaSelfInClasses_dotToken.ts at 4:22 1`] = `
[
{
Expand All @@ -179,10 +211,10 @@ exports[`Completion effectSchemaSelfInClasses > effectSchemaSelfInClasses_dotTok
"sortText": "11",
},
{
"insertText": "S.TaggedErrorClass<Test>()("Test", {\${0}}){}",
"insertText": "S.TaggedError<Test>()("Test", {\${0}}){}",
"isSnippet": true,
"kind": "const",
"name": "TaggedErrorClass<Test>",
"name": "TaggedError<Test>",
"replacementSpan": {
"length": 2,
"start": 130,
Expand All @@ -201,10 +233,10 @@ exports[`Completion effectSchemaSelfInClasses > effectSchemaSelfInClasses_dotTok
"sortText": "11",
},
{
"insertText": "S.ErrorClass<Test>("Test")({\${0}}){}",
"insertText": "S.Error<Test>("Test")({\${0}}){}",
"isSnippet": true,
"kind": "const",
"name": "ErrorClass<Test>",
"name": "Error<Test>",
"replacementSpan": {
"length": 2,
"start": 130,
Expand Down Expand Up @@ -244,10 +276,10 @@ exports[`Completion effectSchemaSelfInClasses > effectSchemaSelfInClasses_tagg.t
"sortText": "11",
},
{
"insertText": "S.TaggedErrorClass<Test>()("Test", {\${0}}){}",
"insertText": "S.TaggedError<Test>()("Test", {\${0}}){}",
"isSnippet": true,
"kind": "const",
"name": "TaggedErrorClass<Test>",
"name": "TaggedError<Test>",
"replacementSpan": {
"length": 5,
"start": 130,
Expand All @@ -266,10 +298,10 @@ exports[`Completion effectSchemaSelfInClasses > effectSchemaSelfInClasses_tagg.t
"sortText": "11",
},
{
"insertText": "S.ErrorClass<Test>("Test")({\${0}}){}",
"insertText": "S.Error<Test>("Test")({\${0}}){}",
"isSnippet": true,
"kind": "const",
"name": "ErrorClass<Test>",
"name": "Error<Test>",
"replacementSpan": {
"length": 5,
"start": 130,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
unnecessaryFailYieldableError_fix from 668 to 709
unnecessaryFailYieldableError_skipNextLine from 668 to 709
unnecessaryFailYieldableError_skipFile from 668 to 709
unnecessaryFailYieldableError_fix from 712 to 751
unnecessaryFailYieldableError_skipNextLine from 712 to 751
unnecessaryFailYieldableError_skipFile from 712 to 751
unnecessaryFailYieldableError_fix from 663 to 704
unnecessaryFailYieldableError_skipNextLine from 663 to 704
unnecessaryFailYieldableError_skipFile from 663 to 704
unnecessaryFailYieldableError_fix from 707 to 746
unnecessaryFailYieldableError_skipNextLine from 707 to 746
unnecessaryFailYieldableError_skipFile from 707 to 746
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// code fix unnecessaryFailYieldableError_fix output for range 668 - 709
// code fix unnecessaryFailYieldableError_fix output for range 663 - 704
/** @effect-diagnostics missingReturnYieldStar:skip-file */
import * as Data from "effect/Data"
import * as Effect from "effect/Effect"
Expand All @@ -10,7 +10,7 @@ class NonYieldable extends Error {

class DataTaggedError extends Data.TaggedError("DataTaggedError")<{}> {}

class SchemaError extends Schema.ErrorClass<SchemaError>("SchemaError")({
class SchemaError extends Schema.Error<SchemaError>("SchemaError")({
_tag: Schema.tag("SchemaError")
}) {}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// code fix unnecessaryFailYieldableError_fix output for range 712 - 751
// code fix unnecessaryFailYieldableError_fix output for range 707 - 746
/** @effect-diagnostics missingReturnYieldStar:skip-file */
import * as Data from "effect/Data"
import * as Effect from "effect/Effect"
Expand All @@ -10,7 +10,7 @@ class NonYieldable extends Error {

class DataTaggedError extends Data.TaggedError("DataTaggedError")<{}> {}

class SchemaError extends Schema.ErrorClass<SchemaError>("SchemaError")({
class SchemaError extends Schema.Error<SchemaError>("SchemaError")({
_tag: Schema.tag("SchemaError")
}) {}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ class Person extends Schema.Class<Person>("Person")({
name: Schema.NonEmptyString,
age: Schema.Int
}) {
static decode: (input: Schema.Struct.ReadonlySide<{ readonly name: Schema.NonEmptyString; readonly age: Schema.Int }, "Encoded">, options?: ParseOption.ParseOptions) => Effect.Effect<Person, Schema.SchemaError> = Schema.decodeEffect(Person)
static decode: (input: { readonly name: string; readonly age: number }, options?: ParseOption.ParseOptions) => Effect.Effect<Person, Schema.SchemaError> = Schema.decodeEffect(Person)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Result of running refactor toggleTypeAnnotation at position 4:15
import * as Schema from "effect/Schema"

export const debug: (input: Schema.Struct.ReadonlyMakeIn<{ readonly id: Schema.Option<Schema.Number> }>, options?: Schema.MakeOptions) => Schema.Struct.ReadonlySide<{ readonly id: Schema.Option<Schema.Number> }, "Type"> = Schema.Struct({
export const debug: (input: { readonly id: import("effect/Option", { with: { "resolution-mode": "import" } }).Option<number> }, options?: Schema.MakeOptions) => { readonly id: import("effect/Option", { with: { "resolution-mode": "import" } }).Option<number> } = Schema.Struct({
id: Schema.Option(Schema.Number)
}).make
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// 4:35
import { Error } from "effect/Schema"

export class MyError extends Error
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// 4:40
import { TaggedError } from "effect/Schema"

export class MyError extends TaggedError
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class NonYieldable extends Error {

class DataTaggedError extends Data.TaggedError("DataTaggedError")<{}> {}

class SchemaError extends Schema.ErrorClass<SchemaError>("SchemaError")({
class SchemaError extends Schema.Error<SchemaError>("SchemaError")({
_tag: Schema.tag("SchemaError")
}) {}

Expand Down
2 changes: 1 addition & 1 deletion packages/harness-effect-v4/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
},
"dependencies": {
"@standard-schema/spec": "^1.1.0",
"effect": "^4.0.0-beta.94"
"effect": "4.0.0-beta.104"
},
"devDependencies": {
"@types/node": "^25.0.6"
Expand Down
4 changes: 2 additions & 2 deletions packages/language-service/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@
"perf": "tsx test/perf.ts"
},
"devDependencies": {
"@effect/platform-node": "^4.0.0-beta.94",
"@effect/platform-node": "4.0.0-beta.104",
"@types/pako": "^2.0.4",
"@typescript-eslint/project-service": "^8.52.0",
"effect": "^4.0.0-beta.94",
"effect": "4.0.0-beta.104",
"pako": "^2.1.0",
"ts-patch": "^3.3.0"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,43 +56,22 @@ export const effectSchemaSelfInClasses = LSP.createCompletion({
}

// Check for Schema.TaggedError or direct import TaggedError
if (typeParser.supportedEffect() === "v3") {
const hasTaggedErrorCompletion = isFullyQualified || Option.isSome(
yield* pipe(
typeParser.isNodeReferenceToEffectSchemaModuleApi("TaggedError")(accessedObject),
Nano.option
)
)
if (hasTaggedErrorCompletion) {
completions.push({
name: `TaggedError<${name}>`,
kind: ts.ScriptElementKind.constElement,
insertText: isFullyQualified
? `${schemaIdentifier}.TaggedError<${name}>()("${errorTagKey}", {${"${0}"}}){}`
: `TaggedError<${name}>()("${errorTagKey}", {${"${0}"}}){}`,
replacementSpan,
isSnippet: true
})
}
}
if (typeParser.supportedEffect() === "v4") {
const hasTaggedErrorCompletion = isFullyQualified || Option.isSome(
yield* pipe(
typeParser.isNodeReferenceToEffectSchemaModuleApi("TaggedErrorClass")(accessedObject),
Nano.option
)
const hasTaggedErrorCompletion = isFullyQualified || Option.isSome(
yield* pipe(
typeParser.isNodeReferenceToEffectSchemaModuleApi("TaggedError")(accessedObject),
Nano.option
)
if (hasTaggedErrorCompletion) {
completions.push({
name: `TaggedErrorClass<${name}>`,
kind: ts.ScriptElementKind.constElement,
insertText: isFullyQualified
? `${schemaIdentifier}.TaggedErrorClass<${name}>()("${errorTagKey}", {${"${0}"}}){}`
: `TaggedErrorClass<${name}>()("${errorTagKey}", {${"${0}"}}){}`,
replacementSpan,
isSnippet: true
})
}
)
if (hasTaggedErrorCompletion) {
completions.push({
name: `TaggedError<${name}>`,
kind: ts.ScriptElementKind.constElement,
insertText: isFullyQualified
? `${schemaIdentifier}.TaggedError<${name}>()("${errorTagKey}", {${"${0}"}}){}`
: `TaggedError<${name}>()("${errorTagKey}", {${"${0}"}}){}`,
replacementSpan,
isSnippet: true
})
}

// Check for Schema.TaggedClass or direct import TaggedClass
Expand Down Expand Up @@ -136,19 +115,19 @@ export const effectSchemaSelfInClasses = LSP.createCompletion({
}

if (typeParser.supportedEffect() === "v4") {
const hasErrorClassCompletion = isFullyQualified || Option.isSome(
const hasErrorCompletion = isFullyQualified || Option.isSome(
yield* pipe(
typeParser.isNodeReferenceToEffectSchemaModuleApi("ErrorClass")(accessedObject),
typeParser.isNodeReferenceToEffectSchemaModuleApi("Error")(accessedObject),
Nano.option
)
)
if (hasErrorClassCompletion) {
if (hasErrorCompletion) {
completions.push({
name: `ErrorClass<${name}>`,
name: `Error<${name}>`,
kind: ts.ScriptElementKind.constElement,
insertText: isFullyQualified
? `${schemaIdentifier}.ErrorClass<${name}>("${name}")({${"${0}"}}){}`
: `ErrorClass<${name}>()({${"${0}"}}){}`,
? `${schemaIdentifier}.Error<${name}>("${name}")({${"${0}"}}){}`
: `Error<${name}>("${name}")({${"${0}"}}){}`,
replacementSpan,
isSnippet: true
})
Expand Down
29 changes: 23 additions & 6 deletions packages/language-service/src/core/LayerGraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -396,8 +396,8 @@ export const formatNestedLayerGraph = Nano.fn("formatNestedLayerGraph")(

// and then the edges
for (const edgeInfo of Graph.values(Graph.edges(layerGraph))) {
const sourceData = layerGraph.nodes.get(edgeInfo.source)!
const targetData = layerGraph.nodes.get(edgeInfo.target)!
const sourceData = Option.getOrUndefined(Graph.getNode(layerGraph, edgeInfo.source))!
const targetData = Option.getOrUndefined(Graph.getNode(layerGraph, edgeInfo.target))!
let connected: boolean = false
for (const kind of ["requires", "provides"] as const) {
for (let i = 0; i < sourceData[kind].length; i++) {
Expand Down Expand Up @@ -540,13 +540,27 @@ export interface LayerMagicResult {
missingOutputTypes: Set<ts.Type>
}

const traversalNeighbors = <N, E>(
graph: Graph.Graph<N, E, "directed"> | Graph.MutableGraph<N, E, "directed">,
node: Graph.NodeIndex,
direction: Graph.TraversalDirection
) =>
direction === "undirected"
? [...new Set([...Graph.successors(graph, node), ...Graph.predecessors(graph, node)])]
: direction === "outgoing"
? Graph.successors(graph, node)
: Graph.predecessors(graph, node)

export const dfsPostOrderWithOrder = <N, E>(
graph: Graph.Graph<N, E, "directed"> | Graph.MutableGraph<N, E, "directed">,
config: Graph.SearchConfig & { order: Order.Order<N> }
): Graph.NodeWalker<N> => {
const start = config.start ?? []
const direction = config.direction ?? "outgoing"
const orderByIndex = Order.mapInput(config.order, (_: Graph.NodeIndex) => graph.nodes.get(_)!)
const orderByIndex = Order.mapInput(
config.order,
(_: Graph.NodeIndex) => Option.getOrUndefined(Graph.getNode(graph, _))!
)

return new Graph.Walker((f) => ({
[Symbol.iterator]: () => {
Expand All @@ -572,7 +586,7 @@ export const dfsPostOrderWithOrder = <N, E>(

if (!current.visitedChildren) {
current.visitedChildren = true
const neighbors = Graph.neighborsDirected(graph, current.node, direction)
const neighbors = traversalNeighbors(graph, current.node, direction)
const sortedNeighbors = Array.sort(neighbors, orderByIndex)
for (let i = sortedNeighbors.length - 1; i >= 0; i--) {
const neighbor = sortedNeighbors[i]
Expand Down Expand Up @@ -626,7 +640,10 @@ export const convertOutlineGraphToLayerMagic = Nano.fn("convertOutlineGraphToLay
const layerOrder = Order.combine(orderByProvidedCount, orderByRequiredCount)

// no need to filter because the outline graph is already deduplicated and only keeping childs
const reversedGraph = Graph.mutate(outlineGraph, Graph.reverse)
const reversedGraph = Graph.mutate(outlineGraph, (mutable) => {
Graph.reverse(mutable)
return undefined
})
const rootIndexes = Array.fromIterable(Graph.indices(Graph.externals(reversedGraph, { direction: "incoming" })))
const allNodes = Array.fromIterable(
Graph.values(dfsPostOrderWithOrder(reversedGraph, { start: rootIndexes, order: layerOrder }))
Expand Down Expand Up @@ -674,7 +691,7 @@ export const walkLeavesMatching = <N, E>(
if (discovered.has(current)) continue
discovered.add(current)

const neighbors = Graph.neighborsDirected(graph, current, direction)
const neighbors = traversalNeighbors(graph, current, direction)
const neighborsMatching: Array<Graph.NodeIndex> = []
for (const neighbor of neighbors) {
const neighborNode = Graph.getNode(graph, neighbor)
Expand Down
8 changes: 4 additions & 4 deletions packages/language-service/src/diagnostics/outdatedApi.db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ export const effectModuleMigrationDb: ModuleMigrationDb = {
"race": asUnchanged,
"raceAll": asUnchanged,
"raceFirst": asUnchanged,
"reduce": asUnchanged,
"repeat": asUnchanged,
"repeatOrElse": asUnchanged,
"replicate": asUnchanged,
Expand Down Expand Up @@ -178,7 +179,6 @@ export const effectModuleMigrationDb: ModuleMigrationDb = {
"void": asUnchanged,
"when": asUnchanged,
"whileLoop": asUnchanged,
"withConcurrency": asUnchanged,
"withExecutionPlan": asUnchanged,
"withLogSpan": asUnchanged,
"withParentSpan": asUnchanged,
Expand Down Expand Up @@ -495,9 +495,6 @@ export const effectModuleMigrationDb: ModuleMigrationDb = {
"randomWith": asRemoved(
"Random access has been reworked in Effect v4."
),
"reduce": asRemoved(
"Use Effect.forEach with a mutable accumulator or Effect.gen instead."
),
"reduceEffect": asRemoved(
"Use Effect.forEach with a mutable accumulator or Effect.gen instead."
),
Expand Down Expand Up @@ -649,6 +646,9 @@ export const effectModuleMigrationDb: ModuleMigrationDb = {
"withConsoleScoped": asRemoved(
"Console configuration has been reworked in Effect v4."
),
"withConcurrency": asRemoved(
"Pass concurrency through operation options, such as Effect.all or Effect.forEach."
),
"withEarlyRelease": asRemoved(
"Use Effect.scoped with manual resource management instead."
),
Expand Down
Loading
Loading