Skip to content

Commit 1fdfcd3

Browse files
feat: 修复TS插件构建问题;使 patch Annotation 语义高亮更精确;添加 OpenHarmony SDK 的 defaultLibrary 语义高亮 (#213)
* chore: 修复TS插件构建问题 * feat: 使 patch Annotation 语义高亮更精确;添加 `OpenHarmony SDK` 的 `defaultLibrary` 语义高亮
1 parent e957acc commit 1fdfcd3

10 files changed

Lines changed: 195 additions & 206 deletions

File tree

.changeset/better-mugs-tap.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"vscode-naily-ets": patch
3+
---
4+
5+
chore: 修复 TS 插件构建问题

.changeset/fifty-beers-obey.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@arkts/language-plugin": patch
3+
"@arkts/language-server": patch
4+
"vscode-naily-ets": patch
5+
---
6+
7+
feat: 使 patch Annotation 语义高亮更精确;添加 `OpenHarmony SDK``defaultLibrary` 语义高亮

packages/language-plugin/src/ets-code.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export function createVirtualCode(snapshot: ts.IScriptSnapshot, languageId: stri
1717
}
1818
}
1919

20-
export function createEmptyVirtualCode(snapshot: ts.IScriptSnapshot, languageId: string, data: CodeInformation): VirtualCode {
20+
export function createEmptyVirtualCode<T extends Record<string, any>>(snapshot: ts.IScriptSnapshot, languageId: string, data: CodeInformation, options?: T): VirtualCode & T {
2121
return {
2222
id: 'root',
2323
languageId,
@@ -32,10 +32,18 @@ export function createEmptyVirtualCode(snapshot: ts.IScriptSnapshot, languageId:
3232
lengths: [snapshot.getLength()],
3333
data,
3434
}],
35-
}
35+
...options,
36+
} as unknown as VirtualCode & T
3637
}
3738

38-
export class ETSVirtualCode extends TsmVirtualCode {}
39+
export class ETSVirtualCode extends TsmVirtualCode {
40+
readonly filePath: string
41+
42+
constructor(filePath: string, sourceFile: ts.SourceFile, languageId: string, plugins: TsmLanguagePlugin[]) {
43+
super(filePath, sourceFile, languageId, plugins)
44+
this.filePath = filePath
45+
}
46+
}
3947

4048
export type ETSMacroPlugin = Omit<TsmLanguagePlugin, 'resolveVirtualCode'> & {
4149
resolveVirtualCode?(virtualCode: ETSVirtualCode): void
Lines changed: 113 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { LanguagePlugin, VirtualCode } from '@volar/language-core'
2+
import type { TypeScriptServiceScript } from '@volar/typescript'
23
import type * as ets from 'ohos-typescript'
34
import type * as ts from 'typescript'
45
import type { URI } from 'vscode-uri'
@@ -7,10 +8,6 @@ import { $$thisFixerPlugin } from './$$this-fixer-plugin'
78
import { ESObjectPlugin } from './es-object-plugin'
89
import { createEmptyVirtualCode, createVirtualCode, ETSVirtualCode } from './ets-code'
910

10-
function isEts(tsOrEts: typeof ets | typeof ts): tsOrEts is typeof ets {
11-
return 'ETS' in tsOrEts.ScriptKind && tsOrEts.ScriptKind.ETS === 8
12-
}
13-
1411
export interface ETSLanguagePluginOptions {
1512
/**
1613
* Paths excluded from virtual code. It is very useful when you want to disable files in the `openharmony` & `hms` sdk.
@@ -39,97 +36,132 @@ export function ETSLanguagePlugin(tsOrEts: typeof ts, options?: ETSLanguagePlugi
3936
export function ETSLanguagePlugin(tsOrEts: typeof ets, options?: ETSLanguagePluginOptions): LanguagePlugin<URI | string>
4037
export function ETSLanguagePlugin(tsOrEts: typeof ets | typeof ts, { excludePaths = [], tsdk = '' }: ETSLanguagePluginOptions = {}): LanguagePlugin<URI | string> {
4138
const isETSServerMode = isEts(tsOrEts)
42-
const isTSPluginMode = !isETSServerMode
4339

44-
// full feature virtual code
45-
const getFullVirtualCode = (snapshot: ts.IScriptSnapshot, languageId: string): VirtualCode => (
46-
createVirtualCode(snapshot, languageId, {
47-
completion: true,
48-
format: true,
49-
navigation: true,
50-
semantic: true,
51-
structure: true,
52-
verification: true,
53-
})
54-
)
40+
function getLanguageId(uri: URI | string): string | undefined {
41+
const filePath = typeof uri === 'string' ? uri : uri.fsPath
42+
if (filePath.endsWith('.ets')) return 'ets'
43+
if (filePath.endsWith('.ts')) return 'typescript'
44+
if (filePath.endsWith('.json') || filePath.endsWith('.json5') || filePath.endsWith('.jsonc') || filePath.endsWith('.tsbuildinfo')) return 'json'
45+
return undefined
46+
}
47+
48+
function getScriptKindByFilePath(filePath: string, defaultExtension: string = '.ets'): [ets.ScriptKind, string] {
49+
if (!filePath) return [7 satisfies typeof ets.ScriptKind.Deferred, defaultExtension]
50+
if (filePath.endsWith('.d.ts')) return [3 satisfies typeof ets.ScriptKind.TS, '.d.ts']
51+
if (filePath.endsWith('.d.ets')) return [8 satisfies typeof ets.ScriptKind.ETS, '.d.ets']
52+
if (filePath.endsWith('.d.cts')) return [3 satisfies typeof ets.ScriptKind.TS, '.d.cts']
53+
if (filePath.endsWith('.d.mts')) return [3 satisfies typeof ets.ScriptKind.TS, '.d.mts']
5554

56-
// disabled virtual code, but still keep the full content
57-
const getDisabledVirtualCode = (snapshot: ts.IScriptSnapshot, languageId: string): VirtualCode => (
58-
createVirtualCode(snapshot, languageId, {
59-
completion: false,
60-
format: false,
61-
navigation: false,
62-
semantic: false,
63-
structure: false,
64-
verification: false,
65-
})
66-
)
55+
const extension = path.extname(filePath)
56+
switch (extension) {
57+
case '.ts':
58+
case '.cts':
59+
case '.mts':
60+
return [3 satisfies typeof ets.ScriptKind.TS, extension]
61+
case '.ets':
62+
return [8 satisfies typeof ets.ScriptKind.ETS, extension]
63+
default:
64+
return [7 satisfies typeof ets.ScriptKind.Deferred, extension]
65+
}
66+
}
6767

68-
// disabled virtual code, and remove the full content to empty string
69-
const getFullDisabledVirtualCode = (snapshot: ts.IScriptSnapshot, languageId: string): VirtualCode => (
70-
createEmptyVirtualCode(snapshot, languageId, {
71-
completion: false,
72-
format: false,
73-
navigation: false,
74-
semantic: false,
75-
structure: false,
76-
verification: false,
77-
})
78-
)
68+
if (isETSServerMode) {
69+
return {
70+
getLanguageId,
71+
createVirtualCode(uri, languageId, snapshot) {
72+
const filePath = path.resolve(typeof uri === 'string' ? uri : uri.fsPath)
73+
if (languageId === 'ets') {
74+
return new ETSVirtualCode(
75+
filePath,
76+
tsOrEts.createSourceFile(filePath, snapshot.getText(0, snapshot.getLength()), 99 as any) as unknown as ts.SourceFile,
77+
'typescript',
78+
[$$thisFixerPlugin(), ESObjectPlugin()] as any,
79+
)
80+
}
81+
// json5、json files, directly using full feature virtual code
82+
if (filePath.endsWith('.json') || filePath.endsWith('.json5') || filePath.endsWith('.jsonc') || languageId === 'json' || languageId === 'jsonc') return getFullVirtualCode(snapshot, languageId)
83+
// tsdk files we must disable the full feature virtual code but still keep the full content
84+
if (filePath.startsWith(tsdk)) return getDisabledVirtualCode(snapshot, languageId)
85+
},
86+
typescript: {
87+
extraFileExtensions: [
88+
// eslint-disable-next-line ts/ban-ts-comment
89+
// @ts-expect-error
90+
{ extension: 'ets', isMixedContent: false, scriptKind: 8 satisfies ets.ScriptKind.ETS },
91+
// eslint-disable-next-line ts/ban-ts-comment
92+
// @ts-expect-error
93+
{ extension: 'd.ets', isMixedContent: false, scriptKind: 8 satisfies ets.ScriptKind.ETS },
94+
],
95+
resolveHiddenExtensions: true,
96+
getServiceScript(root: VirtualCode & { filePath: string }) {
97+
const [scriptKind, extension] = getScriptKindByFilePath(root.filePath)
98+
return {
99+
code: root,
100+
extension,
101+
scriptKind,
102+
} as unknown as TypeScriptServiceScript
103+
},
104+
},
105+
}
106+
}
79107

80108
return {
81-
getLanguageId(uri) {
82-
const filePath = typeof uri === 'string' ? uri : uri.fsPath
83-
if (filePath.endsWith('.ets')) return 'ets'
84-
if (filePath.endsWith('.ts')) return 'typescript'
85-
if (filePath.endsWith('.json') || filePath.endsWith('.json5') || filePath.endsWith('.jsonc')) return 'json'
86-
return undefined
87-
},
109+
getLanguageId,
88110
createVirtualCode(uri, languageId, snapshot) {
89111
const filePath = path.resolve(typeof uri === 'string' ? uri : uri.fsPath)
90112
const isInExcludePath = excludePaths.some(excludePath => filePath.startsWith(excludePath))
91-
const isInTsdkPath = filePath.startsWith(tsdk)
92-
const isDTS = filePath.endsWith('.d.ts')
93-
const isDETS = filePath.endsWith('.d.ets')
94-
95-
// json5、json files, directly using full feature virtual code
96-
if (filePath.endsWith('.json') || filePath.endsWith('.json5') || filePath.endsWith('.jsonc') || languageId === 'json' || languageId === 'jsonc') return getFullVirtualCode(snapshot, languageId)
97-
98-
// ets files, using ts-macro to generate the virtual code
99-
if (languageId === 'ets') {
100-
return new ETSVirtualCode(
101-
filePath,
102-
tsOrEts.createSourceFile(filePath, snapshot.getText(0, snapshot.getLength()), 99 as any) as ts.SourceFile,
103-
'typescript',
104-
[$$thisFixerPlugin(), ESObjectPlugin()] as any,
105-
)
106-
}
107-
// ETS Server mode
108-
if (isETSServerMode && !(isDTS || isDETS) && !isInExcludePath) return getDisabledVirtualCode(snapshot, languageId)
109-
// TS Plugin mode
110-
if (isTSPluginMode && (isDTS || isDETS) && isInExcludePath) {
111-
return getFullDisabledVirtualCode(snapshot, languageId)
112-
}
113-
// Proxy ts internal lib files, such as `lib.d.ts`, `lib.es2020.d.ts`, etc.
114-
if (isETSServerMode && (isDTS || isDETS) && isInTsdkPath) return getDisabledVirtualCode(snapshot, languageId)
113+
if ((filePath.endsWith('.d.ts') || filePath.endsWith('.d.ets')) && isInExcludePath) return getFullDisabledVirtualCode(snapshot, languageId, { filePath })
115114
},
116115
typescript: {
117-
// eslint-disable-next-line ts/ban-ts-comment
118-
// @ts-expect-error
119-
extraFileExtensions: isETSServerMode
120-
? [
121-
{ extension: 'ets', isMixedContent: false, scriptKind: 8 satisfies ets.ScriptKind.ETS },
122-
{ extension: 'd.ets', isMixedContent: false, scriptKind: 8 satisfies ets.ScriptKind.ETS },
123-
]
124-
: [],
125-
resolveHiddenExtensions: true,
126-
getServiceScript(root) {
116+
extraFileExtensions: [],
117+
getServiceScript(root: VirtualCode & { filePath: string }) {
118+
const [scriptKind, extension] = getScriptKindByFilePath(root.filePath, '.ts')
127119
return {
128120
code: root,
129-
extension: '.ets',
130-
scriptKind: 3 satisfies typeof ets.ScriptKind.TS,
121+
extension,
122+
scriptKind: scriptKind as ts.ScriptKind,
131123
}
132124
},
133125
},
134126
}
135127
}
128+
129+
function isEts(tsOrEts: typeof ets | typeof ts): tsOrEts is typeof ets {
130+
return 'ETS' in tsOrEts.ScriptKind && tsOrEts.ScriptKind.ETS === 8
131+
}
132+
133+
// full feature virtual code
134+
function getFullVirtualCode(snapshot: ts.IScriptSnapshot, languageId: string): VirtualCode {
135+
return createVirtualCode(snapshot, languageId, {
136+
completion: true,
137+
format: true,
138+
navigation: true,
139+
semantic: true,
140+
structure: true,
141+
verification: true,
142+
})
143+
}
144+
145+
// disabled virtual code, but still keep the full content
146+
function getDisabledVirtualCode(snapshot: ts.IScriptSnapshot, languageId: string): VirtualCode {
147+
return createVirtualCode(snapshot, languageId, {
148+
completion: false,
149+
format: false,
150+
navigation: false,
151+
semantic: false,
152+
structure: false,
153+
verification: false,
154+
})
155+
}
156+
157+
// disabled virtual code, and remove the full content to empty string
158+
function getFullDisabledVirtualCode<T extends Record<string, any>>(snapshot: ts.IScriptSnapshot, languageId: string, options?: T): VirtualCode & T {
159+
return createEmptyVirtualCode(snapshot, languageId, {
160+
completion: false,
161+
format: false,
162+
navigation: false,
163+
semantic: false,
164+
structure: false,
165+
verification: false,
166+
}, options)
167+
}

packages/language-server/src/index.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ connection.onInitialize(async (params) => {
6666
isFormattingEnabled: document => !((document.languageId === 'json' || document.languageId === 'jsonc')),
6767
disableAutoImportCache: true,
6868
})
69-
patchSemantic(typescriptServices)
69+
patchSemantic(typescriptServices, lspConfiguration)
7070

7171
// TODO
7272
connection.onRequest('ets/onDidChangeTextDocument', () => {})
@@ -89,17 +89,16 @@ connection.onInitialize(async (params) => {
8989
options.project.typescript.languageServiceHost.getCompilationSettings = () => {
9090
return lspConfiguration.getTsConfig(originalSettings as ets.CompilerOptions) as any
9191
}
92-
const patchedGetScriptKind = (fileName: string): ets.ScriptKind => {
92+
93+
options.project.typescript.languageServiceHost.getScriptKind = ((fileName: string): ets.ScriptKind => {
9394
if (fileName.endsWith('.ets')) return ets.ScriptKind.ETS
9495
else if (fileName.endsWith('.js')) return ets.ScriptKind.JS
9596
else if (fileName.endsWith('.jsx')) return ets.ScriptKind.JSX
9697
else if (fileName.endsWith('.ts')) return ets.ScriptKind.TS
9798
else if (fileName.endsWith('.tsx')) return ets.ScriptKind.TSX
9899
else if (fileName.endsWith('.json')) return ets.ScriptKind.JSON
99100
else return ets.ScriptKind.Unknown
100-
}
101-
patchedGetScriptKind.patched = true
102-
options.project.typescript.languageServiceHost.getScriptKind = patchedGetScriptKind as any
101+
}) as any
103102
},
104103
}
105104
}),

0 commit comments

Comments
 (0)