Skip to content

Commit 73193a9

Browse files
committed
♻️ extract lifecycle into its own module
1 parent 0553e66 commit 73193a9

File tree

2 files changed

+106
-94
lines changed

2 files changed

+106
-94
lines changed

lib/lifecycle.ts

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import type { Operation, Stream } from "effection";
2+
import { all, call, createChannel } from "effection";
3+
import type { LSPAgent, RPCEndpoint } from "./types.ts";
4+
import { ErrorCodes } from "vscode-jsonrpc";
5+
import type { InitializeResult } from "vscode-languageserver-protocol";
6+
import { responseError } from "./json-rpc-connection.ts";
7+
import * as merge from "./merge.ts";
8+
9+
export interface State {
10+
notify: RPCEndpoint["notify"];
11+
request: RPCEndpoint["request"];
12+
}
13+
14+
export function lifecycle(
15+
servers: RPCEndpoint[],
16+
): [State, Stream<State, void>] {
17+
let states = createChannel<State, void>();
18+
return [uninitializedState(servers, states.send), states];
19+
}
20+
21+
function uninitializedState(
22+
servers: RPCEndpoint[],
23+
transition: (state: State) => Operation<void>,
24+
): State {
25+
return {
26+
*notify() {},
27+
*request(params) {
28+
let [method] = params;
29+
if (method !== "initialize") {
30+
yield* responseError(
31+
ErrorCodes.ServerNotInitialized,
32+
`server not initialized`,
33+
);
34+
}
35+
let agents = yield* all(servers.map((server) =>
36+
call(function* () {
37+
let initialization = yield* server.request<InitializeResult>(
38+
params,
39+
);
40+
let { capabilities } = initialization;
41+
return {
42+
...server,
43+
initialization,
44+
capabilities,
45+
} as LSPAgent;
46+
})
47+
));
48+
49+
yield* transition(initializedState(agents, transition));
50+
51+
return cast(merge.capabilities(agents));
52+
},
53+
};
54+
}
55+
56+
function initializedState(
57+
agents: LSPAgent[],
58+
transition: (state: State) => Operation<void>,
59+
): State {
60+
return {
61+
*notify(params) {
62+
// TODO: only forward notifications to interested agents
63+
for (let agent of agents) {
64+
yield* agent.notify(params);
65+
}
66+
},
67+
*request(params) {
68+
let [first] = agents;
69+
let [method] = params;
70+
if (method === "initialize") {
71+
yield* responseError(
72+
ErrorCodes.InvalidRequest,
73+
`initialize invoked twice`,
74+
);
75+
} else if (method === "shutdown") {
76+
yield* transition(shutdownState);
77+
for (let agent of agents) {
78+
yield* agent.request(params);
79+
}
80+
return cast(null);
81+
} else if (!first) {
82+
throw yield* responseError(
83+
ErrorCodes.InternalError,
84+
`no lsps to make requests`,
85+
);
86+
}
87+
88+
return yield* first.request(params);
89+
},
90+
};
91+
}
92+
93+
const shutdownState: State = {
94+
*notify() {},
95+
*request() {
96+
return yield* responseError(
97+
ErrorCodes.InvalidRequest,
98+
`server is shut down`,
99+
);
100+
},
101+
};
102+
103+
const cast = <T>(value: unknown) => value as T;

lib/multiplexer.ts

Lines changed: 3 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,11 @@
11
import type { Operation } from "effection";
2-
import { all, call, createChannel, each, resource, spawn } from "effection";
3-
2+
import { createChannel, each, resource, spawn } from "effection";
43
import type {
5-
LSPAgent,
64
LSPServerRequest,
75
NotificationParams,
8-
RequestParams,
96
RPCEndpoint,
107
} from "./types.ts";
11-
import { ErrorCodes } from "vscode-jsonrpc";
12-
import type { InitializeResult } from "vscode-languageserver-protocol";
13-
import { responseError } from "./json-rpc-connection.ts";
14-
import * as merge from "./merge.ts";
8+
import { lifecycle } from "./lifecycle.ts";
159

1610
export interface MultiplexerOptions {
1711
servers: RPCEndpoint[];
@@ -44,8 +38,7 @@ export function useMultiplexer(
4438
}
4539

4640
// delegate notifications and requests from client -> server to current state
47-
let states = createChannel<State, never>();
48-
let state = uninitialized(servers, states.send);
41+
let [state, states] = lifecycle(servers);
4942

5043
yield* spawn(function* () {
5144
for (state of yield* each(states)) {
@@ -68,87 +61,3 @@ export interface State {
6861
notify: RPCEndpoint["notify"];
6962
request: RPCEndpoint["request"];
7063
}
71-
72-
function uninitialized(
73-
servers: RPCEndpoint[],
74-
transition: (state: State) => Operation<void>,
75-
): State {
76-
return {
77-
*notify() {},
78-
*request<T>(params: RequestParams): Operation<T> {
79-
let [method] = params;
80-
if (method !== "initialize") {
81-
yield* responseError(
82-
ErrorCodes.ServerNotInitialized,
83-
`server not initialized`,
84-
);
85-
}
86-
let agents = yield* all(servers.map((server) =>
87-
call(function* () {
88-
let initialization = yield* server.request<InitializeResult>(
89-
params,
90-
);
91-
let { capabilities } = initialization;
92-
return {
93-
...server,
94-
initialization,
95-
capabilities,
96-
} as LSPAgent;
97-
})
98-
));
99-
100-
yield* transition(initialized(agents, transition));
101-
102-
return merge.capabilities(agents) as T;
103-
},
104-
};
105-
}
106-
107-
function initialized(
108-
agents: LSPAgent[],
109-
transition: (state: State) => Operation<void>,
110-
): State {
111-
return {
112-
*notify(params) {
113-
// TODO: only forward notifications to interested agents
114-
for (let agent of agents) {
115-
yield* agent.notify(params);
116-
}
117-
},
118-
*request(params) {
119-
let [first] = agents;
120-
let [method] = params;
121-
if (method === "initialize") {
122-
yield* responseError(
123-
ErrorCodes.InvalidRequest,
124-
`initialize invoked twice`,
125-
);
126-
} else if (method === "shutdown") {
127-
yield* transition(shutdown);
128-
for (let agent of agents) {
129-
yield* agent.request(params);
130-
}
131-
return cast(null);
132-
} else if (!first) {
133-
throw yield* responseError(
134-
ErrorCodes.InternalError,
135-
`no lsps to make requests`,
136-
);
137-
}
138-
139-
return yield* first.request(params);
140-
},
141-
};
142-
}
143-
144-
const shutdown: State = {
145-
*notify() {},
146-
*request() {
147-
return yield* responseError(
148-
ErrorCodes.InvalidRequest,
149-
`server is shut down`,
150-
);
151-
},
152-
};
153-
154-
const cast = <T>(value: unknown) => value as T;

0 commit comments

Comments
 (0)