Skip to content

Commit 81cf5c6

Browse files
committed
wip: init server
1 parent d9e3241 commit 81cf5c6

File tree

20 files changed

+1544
-691
lines changed

20 files changed

+1544
-691
lines changed

inspect-extension-host/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"version": "0.0.1",
44
"description": "debug for mpx vscode plugin",
55
"devDependencies": {
6+
"@mpxjs/core": "catalog:",
67
"typescript": "catalog:"
78
}
89
}

packages/language-core/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export * from './types'
77
// export * from './utils/parseSfc'
88
// export * from './utils/shared'
99
// export * from './utils/ts'
10-
// export * from './virtualFile/vueFile'
10+
export * from './virtualFile/mpxFile'
1111

1212
// export { tsCodegen } from './lib/plugins/mpx-tsx'
1313

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import type { VirtualCode } from '@volar/language-core'
2+
import { computed, signal } from 'alien-signals'
3+
import type * as ts from 'typescript'
4+
import { allCodeFeatures } from '../plugins'
5+
import type { VueCompilerOptions, VueLanguagePluginReturn } from '../types'
6+
import { computedEmbeddedCodes } from './computedEmbeddedCodes'
7+
import { computedSfc } from './computedSfc'
8+
import { computedVueSfc } from './computedVueSfc'
9+
10+
export class MpxVirtualCode implements VirtualCode {
11+
// sources
12+
13+
id = 'main'
14+
15+
private _snapshot = signal<ts.IScriptSnapshot>(undefined!)
16+
17+
// computeds
18+
19+
private _vueSfc = computedVueSfc(
20+
this.plugins,
21+
this.fileName,
22+
this.languageId,
23+
this._snapshot,
24+
)
25+
private _sfc = computedSfc(
26+
this.ts,
27+
this.plugins,
28+
this.fileName,
29+
this._snapshot,
30+
this._vueSfc,
31+
)
32+
private _embeddedCodes = computedEmbeddedCodes(
33+
this.plugins,
34+
this.fileName,
35+
this._sfc,
36+
)
37+
private _mappings = computed(() => {
38+
const snapshot = this._snapshot()
39+
return [
40+
{
41+
sourceOffsets: [0],
42+
generatedOffsets: [0],
43+
lengths: [snapshot.getLength()],
44+
data: allCodeFeatures,
45+
},
46+
]
47+
})
48+
49+
// others
50+
51+
get snapshot() {
52+
return this._snapshot()
53+
}
54+
get vueSfc() {
55+
return this._vueSfc()
56+
}
57+
get sfc() {
58+
return this._sfc
59+
}
60+
get embeddedCodes() {
61+
return this._embeddedCodes()
62+
}
63+
get mappings() {
64+
return this._mappings()
65+
}
66+
67+
constructor(
68+
public fileName: string,
69+
public languageId: string,
70+
public initSnapshot: ts.IScriptSnapshot,
71+
public vueCompilerOptions: VueCompilerOptions,
72+
public plugins: VueLanguagePluginReturn[],
73+
public ts: typeof import('typescript'),
74+
) {
75+
this._snapshot(initSnapshot)
76+
}
77+
78+
update(newSnapshot: ts.IScriptSnapshot) {
79+
this._snapshot(newSnapshot)
80+
}
81+
}

packages/language-server/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@
1717
"directory": "packages/language-server"
1818
},
1919
"dependencies": {
20+
"@mpxjs/language-core": "workspace:*",
2021
"@mpxjs/language-service": "workspace:*",
2122
"@volar/language-core": "catalog:",
22-
"@volar/language-server": "catalog:"
23+
"@volar/language-server": "catalog:",
24+
"vscode-uri": "catalog:"
2325
},
2426
"devDependencies": {
2527
"@types/node": "catalog:"
Lines changed: 227 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,227 @@
1-
export const a = 1
1+
import { URI } from 'vscode-uri'
2+
import type * as ts from 'typescript'
3+
4+
import {
5+
createConnection,
6+
createServer,
7+
loadTsdkByPath,
8+
} from '@volar/language-server/node'
9+
import type { LanguageServer } from '@volar/language-server'
10+
import { createLanguageServiceEnvironment } from '@volar/language-server/lib/project/simpleProject'
11+
import {
12+
createLanguage,
13+
// createParsedCommandLine,
14+
// createVueLanguagePlugin,
15+
// getDefaultCompilerOptions,
16+
} from '@mpxjs/language-core'
17+
import {
18+
createLanguageService,
19+
createUriMap,
20+
getHybridModeLanguageServicePlugins,
21+
LanguageService,
22+
} from '@mpxjs/language-service'
23+
24+
import type { MpxInitializationOptions } from './types'
25+
26+
const connection = createConnection()
27+
const server = createServer(connection)
28+
29+
connection.listen()
30+
31+
connection.onInitialize(params => {
32+
const options: MpxInitializationOptions = params.initializationOptions
33+
34+
if (!options.typescript?.tsdk) {
35+
throw new Error('typescript.tsdk is required')
36+
}
37+
if (!options.typescript?.tsserverRequestCommand) {
38+
connection.console.warn(
39+
'typescript.tsserverRequestCommand is required since >= 3.0 for complete TS features',
40+
)
41+
}
42+
43+
const { typescript: ts } = loadTsdkByPath(
44+
options.typescript.tsdk,
45+
params.locale,
46+
)
47+
const tsconfigProjects = createUriMap<LanguageService>()
48+
const file2ProjectInfo = new Map<
49+
string,
50+
Promise<ts.server.protocol.ProjectInfo | null>
51+
>()
52+
53+
server.fileWatcher.onDidChangeWatchedFiles(({ changes }) => {
54+
for (const change of changes) {
55+
const changeUri = URI.parse(change.uri)
56+
if (tsconfigProjects.has(changeUri)) {
57+
tsconfigProjects.get(changeUri)!.dispose()
58+
tsconfigProjects.delete(changeUri)
59+
file2ProjectInfo.clear()
60+
}
61+
}
62+
})
63+
64+
let simpleLs: LanguageService | undefined
65+
66+
return server.initialize(
67+
params,
68+
{
69+
setup() {},
70+
async getLanguageService(uri) {
71+
if (
72+
uri.scheme === 'file' &&
73+
options.typescript.tsserverRequestCommand
74+
) {
75+
const fileName = uri.fsPath.replace(/\\/g, '/')
76+
let projectInfoPromise = file2ProjectInfo.get(fileName)
77+
if (!projectInfoPromise) {
78+
projectInfoPromise = sendTsRequest<ts.server.protocol.ProjectInfo>(
79+
ts.server.protocol.CommandTypes.ProjectInfo,
80+
{
81+
file: fileName,
82+
needFileNameList: false,
83+
} satisfies ts.server.protocol.ProjectInfoRequestArgs,
84+
)
85+
file2ProjectInfo.set(fileName, projectInfoPromise)
86+
}
87+
const projectInfo = await projectInfoPromise
88+
if (projectInfo) {
89+
const { configFileName } = projectInfo
90+
let ls = tsconfigProjects.get(URI.file(configFileName))
91+
if (!ls) {
92+
ls = createLs(server, configFileName)
93+
tsconfigProjects.set(URI.file(configFileName), ls)
94+
}
95+
return ls
96+
}
97+
}
98+
return (simpleLs ??= createLs(server, undefined))
99+
},
100+
getExistingLanguageServices() {
101+
return Promise.all(
102+
[...tsconfigProjects.values(), simpleLs].filter(promise => !!promise),
103+
)
104+
},
105+
reload() {
106+
for (const ls of [...tsconfigProjects.values(), simpleLs]) {
107+
ls?.dispose()
108+
}
109+
tsconfigProjects.clear()
110+
simpleLs = undefined
111+
},
112+
},
113+
getHybridModeLanguageServicePlugins(
114+
ts,
115+
options.typescript.tsserverRequestCommand
116+
? {
117+
collectExtractProps(...args: any[]) {
118+
return sendTsRequest('mpx:collectExtractProps', args)
119+
},
120+
// getComponentDirectives(...args: any[]) {
121+
// return sendTsRequest('mpx:getComponentDirectives', args)
122+
// },
123+
// getComponentEvents(...args: any[]) {
124+
// return sendTsRequest('mpx:getComponentEvents', args)
125+
// },
126+
// getComponentNames(...args: any[]) {
127+
// return sendTsRequest('mpx:getComponentNames', args)
128+
// },
129+
// getComponentProps(...args: any[]) {
130+
// return sendTsRequest('mpx:getComponentProps', args)
131+
// },
132+
// getElementAttrs(...args: any[]) {
133+
// return sendTsRequest('mpx:getElementAttrs', args)
134+
// },
135+
// getElementNames(...args: any[]) {
136+
// return sendTsRequest('mpx:getElementNames', args)
137+
// },
138+
// getImportPathForFile(...args: any[]) {
139+
// return sendTsRequest('mpx:getImportPathForFile', args)
140+
// },
141+
// getPropertiesAtLocation(...args: any[]) {
142+
// return sendTsRequest('mpx:getPropertiesAtLocation', args)
143+
// },
144+
getDocumentHighlights(fileName: string, position: any) {
145+
return sendTsRequest(
146+
'documentHighlights-full', // internal command
147+
{
148+
file: fileName,
149+
...({ position } as unknown as {
150+
line: number
151+
offset: number
152+
}),
153+
filesToSearch: [fileName],
154+
} satisfies ts.server.protocol.DocumentHighlightsRequestArgs,
155+
)
156+
},
157+
async getQuickInfoAtPosition(
158+
fileName: any,
159+
{ line, character }: any,
160+
) {
161+
const result = await sendTsRequest<ts.QuickInfo>(
162+
ts.server.protocol.CommandTypes.Quickinfo,
163+
{
164+
file: fileName,
165+
line: line + 1,
166+
offset: character + 1,
167+
} satisfies ts.server.protocol.FileLocationRequestArgs,
168+
)
169+
return ts.displayPartsToString(result?.displayParts ?? [])
170+
},
171+
}
172+
: undefined,
173+
),
174+
)
175+
176+
/**
177+
* Send request to client
178+
*/
179+
180+
function sendTsRequest<T>(command: string, args: any): Promise<T | null> {
181+
return connection.sendRequest<T>(
182+
options.typescript.tsserverRequestCommand!,
183+
[command, args],
184+
)
185+
}
186+
187+
function createLs(server: LanguageServer, tsconfig: string | undefined) {
188+
const commonLine = {
189+
options: ts.getDefaultCompilerOptions(),
190+
// mpxOptions: getDefaultCompilerOptions(),
191+
}
192+
const language = createLanguage<URI>(
193+
[
194+
{
195+
getLanguageId: uri => server.documents.get(uri)?.languageId,
196+
},
197+
// createVueLanguagePlugin(
198+
// ts,
199+
// commonLine.options,
200+
// commonLine.vueOptions,
201+
// (uri: { fsPath: string }) => uri.fsPath.replace(/\\/g, '/'),
202+
// ),
203+
],
204+
createUriMap(),
205+
uri => {
206+
const document = server.documents.get(uri)
207+
if (document) {
208+
language.scripts.set(uri, document.getSnapshot(), document.languageId)
209+
} else {
210+
language.scripts.delete(uri)
211+
}
212+
},
213+
)
214+
return createLanguageService(
215+
language,
216+
server.languageServicePlugins,
217+
createLanguageServiceEnvironment(server, [
218+
...server.workspaceFolders.all,
219+
]),
220+
{ mpx: { compilerOptions: commonLine.mpxOptions } },
221+
)
222+
}
223+
})
224+
225+
connection.onInitialized(server.initialized)
226+
227+
connection.onShutdown(server.shutdown)

packages/language-server/src/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
export type MpxInitializationOptions = {
22
typescript: {
33
tsdk: string
4-
tsserverRequestCommand?: string
4+
tsserverRequestCommand?: 'tsserverRequest'
55
}
66
}
77

packages/language-service/package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,22 @@
77
"**/*.d.ts"
88
],
99
"sideEffects": false,
10-
"types": "./out/types.d.ts",
10+
"types": "./out/index.d.ts",
1111
"repository": {
1212
"type": "git",
1313
"url": "https://github.com/mpx-ecology/language-tools.git",
1414
"directory": "packages/language-service"
1515
},
1616
"dependencies": {
1717
"@mpxjs/language-core": "workspace:*",
18+
"@mpxjs/typescript-plugin": "workspace:*",
1819
"@volar/language-core": "catalog:",
1920
"@volar/language-service": "catalog:",
2021
"@volar/typescript": "catalog:",
21-
"@vue/compiler-dom": "catalog:"
22+
"@vue/compiler-dom": "catalog:",
23+
"volar-service-emmet": "^0.0.64",
24+
"volar-service-typescript": "^0.0.64",
25+
"vscode-uri": "catalog:"
2226
},
2327
"devDependencies": {
2428
"@types/node": "catalog:"

0 commit comments

Comments
 (0)