diff --git a/ets2panda/bindings/native/src/lsp.cpp b/ets2panda/bindings/native/src/lsp.cpp index 60b407f1ec4d3415e1dcd43976e2ddc0816339bc..85301e94bfc1b1fb5f504d491254651f36927754 100644 --- a/ets2panda/bindings/native/src/lsp.cpp +++ b/ets2panda/bindings/native/src/lsp.cpp @@ -48,6 +48,72 @@ KNativePointer impl_getSemanticDiagnostics(KNativePointer context) } TS_INTEROP_1(getSemanticDiagnostics, KNativePointer, KNativePointer) +KNativePointer impl_getClassPropertyInfo(KNativePointer context, KInt position, KBoolean shouldCollectInherited) +{ + LSPAPI const *ctx = GetImpl(); + auto info = ctx->getClassPropertyInfo(reinterpret_cast(context), + static_cast(position), shouldCollectInherited != 0); + return new std::vector(info); +} +TS_INTEROP_3(getClassPropertyInfo, KNativePointer, KNativePointer, KInt, KBoolean) + +KNativePointer impl_getNameFromPropertyInfo(KNativePointer infoPtr) +{ + auto info = reinterpret_cast(infoPtr); + return new std::string(info->name); +} +TS_INTEROP_1(getNameFromPropertyInfo, KNativePointer, KNativePointer) + +KNativePointer impl_getFieldListPropertyFromPropertyInfo(KNativePointer infoPtr) +{ + auto info = reinterpret_cast(infoPtr); + std::vector ptrs; + for (auto &el : info->properties) { + ptrs.push_back(new FieldListProperty(el)); + } + return new std::vector(ptrs); +} +TS_INTEROP_1(getFieldListPropertyFromPropertyInfo, KNativePointer, KNativePointer) + +KNativePointer impl_getKindFromPropertyInfo(KNativePointer infoPtr) +{ + auto info = reinterpret_cast(infoPtr); + return new std::string(info->kind); +} +TS_INTEROP_1(getKindFromPropertyInfo, KNativePointer, KNativePointer) + +KNativePointer impl_getModifierKindsFromPropertyInfo(KNativePointer infoPtr) +{ + auto info = reinterpret_cast(infoPtr); + std::vector ptrs; + for (auto &el : info->modifierKinds.value()) { + ptrs.push_back(new std::string(el)); + } + return new std::vector(ptrs); +} +TS_INTEROP_1(getModifierKindsFromPropertyInfo, KNativePointer, KNativePointer) + +KNativePointer impl_getDisplayNameFromPropertyInfo(KNativePointer infoPtr) +{ + auto info = reinterpret_cast(infoPtr); + return new std::string(info->displayName); +} +TS_INTEROP_1(getDisplayNameFromPropertyInfo, KNativePointer, KNativePointer) + +KNativePointer impl_getStartFromPropertyInfo(KNativePointer infoPtr) +{ + auto info = reinterpret_cast(infoPtr); + return new std::size_t(info->start); +} +TS_INTEROP_1(getStartFromPropertyInfo, KNativePointer, KNativePointer) + +KNativePointer impl_getEndFromPropertyInfo(KNativePointer infoPtr) +{ + auto info = reinterpret_cast(infoPtr); + return new std::size_t(info->end); +} +TS_INTEROP_1(getEndFromPropertyInfo, KNativePointer, KNativePointer) + KNativePointer impl_getSyntacticDiagnostics(KNativePointer context) { LSPAPI const *ctx = GetImpl(); diff --git a/ets2panda/bindings/src/Es2pandaNativeModule.ts b/ets2panda/bindings/src/Es2pandaNativeModule.ts index f27fcdead3165df693f642e5fdb65e27f11dd2ea..77a1bba6b5a1e9adb5de839bb8999474acbc9ec0 100644 --- a/ets2panda/bindings/src/Es2pandaNativeModule.ts +++ b/ets2panda/bindings/src/Es2pandaNativeModule.ts @@ -145,51 +145,39 @@ export class Es2pandaNativeModule { throw new Error('Not implemented'); } - _getTypeHierarchies(searchContext: KNativePointer, context: KNativePointer, position: KInt): KPtr { + _getClassPropertyInfo(context: KNativePointer, position: KInt, shouldCollectInherited: boolean): KPtr { throw new Error('Not implemented'); } - _getFileNameFromTypeHierarchiesInfo(ptr: KNativePointer): KPtr { + _getFieldsInfoFromPropertyInfo(ptr: KNativePointer): KPtr { throw new Error('Not implemented'); } - _getNameFromTypeHierarchiesInfo(ptr: KNativePointer): KPtr { + _getNameFromPropertyInfo(ptr: KNativePointer): KPtr { throw new Error('Not implemented'); } - _getTypeFromTypeHierarchiesInfo(ptr: KNativePointer): KInt { + _getFieldListPropertyFromPropertyInfo(ptr: KNativePointer): KPtr { throw new Error('Not implemented'); } - _getPositionFromTypeHierarchiesInfo(ptr: KNativePointer): KInt { + _getModifierKindsFromPropertyInfo(ptr: KNativePointer): KPtr { throw new Error('Not implemented'); } - _getSuperFromTypeHierarchiesInfo(ptr: KNativePointer): KPtr { + _getDisplayNameFromPropertyInfo(ptr: KNativePointer): KPtr { throw new Error('Not implemented'); } - _getSubFromTypeHierarchiesInfo(ptr: KNativePointer): KPtr { + _getStartFromPropertyInfo(ptr: KNativePointer): KInt { throw new Error('Not implemented'); } - _getFileNameFromTypeHierarchies(ptr: KNativePointer): KPtr { + _getEndFromPropertyInfo(ptr: KNativePointer): KInt { throw new Error('Not implemented'); } - _getNameFromTypeHierarchies(ptr: KNativePointer): KPtr { - throw new Error('Not implemented'); - } - - _getTypeFromTypeHierarchies(ptr: KNativePointer): KInt { - throw new Error('Not implemented'); - } - - _getPosFromTypeHierarchies(ptr: KNativePointer): KInt { - throw new Error('Not implemented'); - } - - _getSubOrSuper(ptr: KNativePointer): KPtr { + _getKindFromPropertyInfo(ptr: KNativePointer): KInt { throw new Error('Not implemented'); } @@ -316,7 +304,7 @@ export class Es2pandaNativeModule { throw new Error('Not implemented'); } - _getSpanOfEnclosingComment(context: KNativePointer, position: KInt, onlyMultiLine: boolean): KPtr { + _getSpanOfEnclosingComment(filename: String, position: KInt): KPtr { throw new Error('Not implemented'); } @@ -479,94 +467,6 @@ export class Es2pandaNativeModule { _toLineColumnOffset(context: KNativePointer, position: KInt): KPtr { throw new Error('Not implemented'); } - - _getInlayHintText(ptr: KPtr): KPtr { - throw new Error('Not implemented'); - } - - _getInlayHintNumber(ptr: KPtr): KInt { - throw new Error('Not implemented'); - } - - _getInlayHintKind(ptr: KPtr): KInt { - throw new Error('Not implemented'); - } - - _getInlayHintWhitespaceBefore(ptr: KPtr): KBoolean { - throw new Error('Not implemented'); - } - - _getInlayHintWhitespaceAfter(ptr: KPtr): KBoolean { - throw new Error('Not implemented'); - } - - _getInlayHintList(context: KPtr, span: KPtr): KPtr { - throw new Error('Not implemented'); - } - - _getInlayHints(context: KPtr): KPtr { - throw new Error('Not implemented'); - } - - _createTextSpan(start: KInt, length: KInt): KPtr { - throw new Error('Not implemented'); - } - - _getSignatureHelpParameterDocumentation(ptr: KPtr): KPtr { - throw new Error('Not implemented'); - } - - _getSignatureHelpParameterDisplayParts(ptr: KPtr): KPtr { - throw new Error('Not implemented'); - } - - _getSignatureHelpParameterName(ptr: KPtr): KPtr { - throw new Error('Not implemented'); - } - - _getSignatureHelpItemPrefix(ptr: KPtr): KPtr { - throw new Error('Not implemented'); - } - - _getSignatureHelpItemSuffix(ptr: KPtr): KPtr { - throw new Error('Not implemented'); - } - - _getSignatureHelpItemSeparator(ptr: KPtr): KPtr { - throw new Error('Not implemented'); - } - - _getSignatureHelpItemParameter(ptr: KPtr): KPtr { - throw new Error('Not implemented'); - } - - _getSignatureHelpItemDocumentation(ptr: KPtr): KPtr { - throw new Error('Not implemented'); - } - - _getSignatureHelpItem(ptr: KPtr): KPtr { - throw new Error('Not implemented'); - } - - _getApplicableSpan(ptr: KPtr): KPtr { - throw new Error('Not implemented'); - } - - _getSelectedItemIndex(ptr: KPtr): KInt { - throw new Error('Not implemented'); - } - - _getArgumentIndex(ptr: KPtr): KInt { - throw new Error('Not implemented'); - } - - _getArgumentCount(ptr: KPtr): KInt { - throw new Error('Not implemented'); - } - - _getSignatureHelpItems(context: KPtr, position: KInt): KPtr { - throw new Error('Not implemented'); - } } export function initEs2panda(): Es2pandaNativeModule { diff --git a/ets2panda/bindings/src/lspNode.ts b/ets2panda/bindings/src/lspNode.ts index f85a4f901ead9e27afe9415a80d3bde0f7754595..a54ed17047ede0bfdebc17a135290d333abc1b81 100644 --- a/ets2panda/bindings/src/lspNode.ts +++ b/ets2panda/bindings/src/lspNode.ts @@ -20,8 +20,6 @@ import { isNullPtr } from './Wrapper'; import { global } from './global'; import { NativePtrDecoder } from './Platform'; -enum HierarchyType { OTHERS, INTERFACE, CLASS }; - export abstract class LspNode { readonly peer: KNativePointer; @@ -199,11 +197,6 @@ export class LspTextSpan extends LspNode { readonly length: KInt; } -export interface TextSpan { - start: KInt; - length: KInt; -} - export class LspSymbolDisplayPart extends LspNode { constructor(peer: KNativePointer) { super(peer); @@ -406,150 +399,48 @@ export class LspLineAndCharacter extends LspNode { } } -export class LspTypeHierarchies extends LspNode { - constructor(peer: KNativePointer) { - super(peer); - this.fileName = unpackString(global.es2panda._getFileNameFromTypeHierarchies(peer)); - this.name = unpackString(global.es2panda._getNameFromTypeHierarchies(peer)); - this.type = global.es2panda._getTypeFromTypeHierarchies(peer); - this.pos = global.es2panda._getPosFromTypeHierarchies(peer); - this.subOrSuper = new NativePtrDecoder() - .decode(global.es2panda._getSubOrSuper(peer)) - .map((elPeer: KNativePointer) => { - return new LspTypeHierarchies(elPeer); - }); - } - readonly fileName: String; - readonly name: String; - readonly type: HierarchyType; - readonly pos: KInt; - subOrSuper: LspTypeHierarchies[]; -} - -export class LspTypeHierarchiesInfo extends LspNode { - constructor(peer: KNativePointer) { - super(peer); - this.fileName = unpackString(global.es2panda._getFileNameFromTypeHierarchiesInfo(peer)); - this.name = unpackString(global.es2panda._getNameFromTypeHierarchiesInfo(peer)); - this.type = global.es2panda._getTypeFromTypeHierarchiesInfo(peer); - this.pos = global.es2panda._getPositionFromTypeHierarchiesInfo(peer); - this.superHierarchies = new LspTypeHierarchies(global.es2panda._getSuperFromTypeHierarchiesInfo(peer)); - this.subHierarchies = new LspTypeHierarchies(global.es2panda._getSubFromTypeHierarchiesInfo(peer)); - } - readonly fileName: String; - readonly name: String; - readonly type: HierarchyType; - readonly pos: KInt; - readonly superHierarchies: LspTypeHierarchies; - readonly subHierarchies: LspTypeHierarchies; -} - -enum LspInlayHintKind { - TYPE, - PARAMETER, - ENUM -} - -export class LspInlayHint extends LspNode { - constructor(peer: KNativePointer) { - super(peer); - this.text = unpackString(global.es2panda._getInlayHintText(peer)); - this.number = global.es2panda._getInlayHintNumber(peer); - this.kind = global.es2panda._getInlayHintKind(peer); - this.whitespaceBefore = global.es2panda._getInlayHintWhitespaceBefore(peer); - this.whitespaceAfter = global.es2panda._getInlayHintWhitespaceAfter(peer); - } - readonly text: string; - readonly number: number; - readonly kind: LspInlayHintKind; - readonly whitespaceBefore: KBoolean; - readonly whitespaceAfter: KBoolean; -} - -export class LspInlayHintList extends LspNode { - constructor(peer: KNativePointer) { - super(peer); - this.inlayHints = new NativePtrDecoder() - .decode(global.es2panda._getInlayHints(peer)) - .map((elPeer: KNativePointer) => { - return new LspInlayHint(elPeer); - }); - } - readonly inlayHints: LspInlayHint[]; -} - -export class LspSignatureHelpParameter extends LspNode { +export class LspClassPropertyInfo extends LspNode { constructor(peer: KNativePointer) { super(peer); - this.name = unpackString(global.es2panda._getSignatureHelpParameterName(peer)); - this.documentation = new NativePtrDecoder() - .decode(global.es2panda._getSignatureHelpParameterDocumentation(peer)) - .map((elPeer: KNativePointer) => { - return new LspSymbolDisplayPart(elPeer); - }); - this.displayParts = new NativePtrDecoder() - .decode(global.es2panda._getSignatureHelpParameterDisplayParts(peer)) + this.fieldsInfo = new NativePtrDecoder() + .decode(global.es2panda._getFieldsInfoFromPropertyInfo(peer)) .map((elPeer: KNativePointer) => { - return new LspSymbolDisplayPart(elPeer); + return new FieldsInfo(elPeer); }); } - readonly name: string; - readonly documentation: LspSymbolDisplayPart[]; - readonly displayParts: LspSymbolDisplayPart[]; + readonly fieldsInfo: FieldsInfo[]; } -export class LspSignatureHelpItem extends LspNode { +export class FieldsInfo extends LspNode { constructor(peer: KNativePointer) { super(peer); - this.prefixDisplayParts = new NativePtrDecoder() - .decode(global.es2panda._getSignatureHelpItemPrefix(peer)) + this.name = unpackString(global.es2panda._getNameFromPropertyInfo(peer)); + this.properties = new NativePtrDecoder() + .decode(global.es2panda._getFieldListPropertyFromPropertyInfo(peer)) .map((elPeer: KNativePointer) => { - return new LspSymbolDisplayPart(elPeer); - }); - this.suffixDisplayParts = new NativePtrDecoder() - .decode(global.es2panda._getSignatureHelpItemSuffix(peer)) - .map((elPeer: KNativePointer) => { - return new LspSymbolDisplayPart(elPeer); - }); - this.separatorDisplayParts = new NativePtrDecoder() - .decode(global.es2panda._getSignatureHelpItemSeparator(peer)) - .map((elPeer: KNativePointer) => { - return new LspSymbolDisplayPart(elPeer); - }); - this.parameters = new NativePtrDecoder() - .decode(global.es2panda._getSignatureHelpItemParameter(peer)) - .map((elPeer: KNativePointer) => { - return new LspSignatureHelpParameter(elPeer); - }); - this.documentation = new NativePtrDecoder() - .decode(global.es2panda._getSignatureHelpItemDocumentation(peer)) - .map((elPeer: KNativePointer) => { - return new LspSymbolDisplayPart(elPeer); + return new FieldListProperty(elPeer); }); } - readonly prefixDisplayParts: LspSymbolDisplayPart[]; - readonly suffixDisplayParts: LspSymbolDisplayPart[]; - readonly separatorDisplayParts: LspSymbolDisplayPart[]; - readonly parameters: LspSignatureHelpParameter[]; - readonly documentation: LspSymbolDisplayPart[]; + readonly name: String; + readonly properties: FieldListProperty[]; } -export class LspSignatureHelpItems extends LspNode { +export class FieldListProperty extends LspNode { constructor(peer: KNativePointer) { super(peer); - this.items = new NativePtrDecoder() - .decode(global.es2panda._getSignatureHelpItem(peer)) + this.kind = unpackString(global.es2panda._getKindFromPropertyInfo(peer)); + this.modifierKinds = new NativePtrDecoder() + .decode(global.es2panda._getModifierKindsFromPropertyInfo(peer)) .map((elPeer: KNativePointer) => { - return new LspSignatureHelpItem(elPeer); + return new String(elPeer); }); - this.applicableSpan = new LspTextSpan(global.es2panda._getApplicableSpan(peer)); - this.selectedItemIndex = global.es2panda._getSelectedItemIndex(peer); - this.argumentIndex = global.es2panda._getArgumentIndex(peer); - this.argumentCount = global.es2panda._getArgumentCount(peer); + this.displayName = unpackString(global.es2panda._getDisplayNameFromPropertyInfo(peer)); + this.start = global.es2panda._getStartFromPropertyInfo(peer); + this.end = global.es2panda._getEndFromPropertyInfo(peer); } - readonly items: LspSignatureHelpItem[]; - readonly applicableSpan: LspTextSpan; - readonly selectedItemIndex: number; - readonly argumentIndex: number; - readonly argumentCount: number; -} + readonly kind: String; + readonly modifierKinds: String[]; + readonly displayName: String; + readonly start: number; + readonly end: number; +} \ No newline at end of file diff --git a/ets2panda/bindings/src/lsp_helper.ts b/ets2panda/bindings/src/lsp_helper.ts index f7adf2393b750664cf9096479670e84b7819536f..c97a3eef79677054cf77c3f9b7e8d1b017d35e41 100644 --- a/ets2panda/bindings/src/lsp_helper.ts +++ b/ets2panda/bindings/src/lsp_helper.ts @@ -25,12 +25,7 @@ import { LspReferenceLocationList, LspLineAndCharacter, LspReferenceData, - LspTypeHierarchiesInfo, - LspTextSpan, - LspInlayHint, - LspInlayHintList, - TextSpan, - LspSignatureHelpItems + LspClassPropertyInfo, } from './lspNode'; import { unpackString } from './private'; import { Es2pandaContextState } from './generated/Es2pandaEnums'; @@ -235,7 +230,7 @@ export class Lsp { return Array.from(new Set(result)); } - getTypeHierarchies(filename: String, offset: number): LspTypeHierarchiesInfo | null { + getClassPropertyInfo(filename: String, offset: number, shouldCollectInherited: boolean = false): LspClassPropertyInfo { let lspDriverHelper = new LspDriverHelper(); let filePath = path.resolve(filename.valueOf()); let arktsconfig = this.fileNameToArktsconfig[filePath]; @@ -247,48 +242,13 @@ export class Lsp { lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); - let ptr = global.es2panda._getTypeHierarchies(localCtx, localCtx, offset); + let ptr = global.es2panda._getClassPropertyInfo(localCtx, offset, shouldCollectInherited); PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - let ref = new LspTypeHierarchiesInfo(ptr); - if (ref.fileName === '') { - lspDriverHelper.destroyContext(localCtx); - lspDriverHelper.destroyConfig(localCfg); - return null; - } - let result: LspTypeHierarchiesInfo[] = []; - let moduleName = path.basename(path.dirname(arktsconfig)); - let buildConfig: BuildConfig = this.moduleToBuildConfig[moduleName]; - for (let i = 0; i < buildConfig.compileFiles.length; i++) { - let filePath = path.resolve(buildConfig.compileFiles[i]); - let arktsconfig = this.fileNameToArktsconfig[filePath]; - let ets2pandaCmd = ['-', '--extension', 'ets', '--arktsconfig', arktsconfig]; - let searchCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); - const source = fs.readFileSync(filePath, 'utf8').toString().replace(/\r\n/g, '\n'); - let searchCtx = lspDriverHelper.createCtx(source, filePath, searchCfg); - PluginDriver.getInstance().getPluginContext().setContextPtr(searchCtx); - lspDriverHelper.proceedToState(searchCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - lspDriverHelper.proceedToState(searchCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); - let ptr = global.es2panda._getTypeHierarchies(searchCtx, localCtx, offset); - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - lspDriverHelper.destroyContext(searchCtx); - lspDriverHelper.destroyConfig(searchCfg); - let refs = new LspTypeHierarchiesInfo(ptr); - if (i > 0) { - result[0].subHierarchies.subOrSuper = result[0].subHierarchies.subOrSuper.concat(refs.subHierarchies.subOrSuper); - } else { - result.push(refs); - } - } - for (let j = 0; j < result[0].subHierarchies.subOrSuper.length; j++) { - let res = this.getTypeHierarchies(result[0].subHierarchies.subOrSuper[j].fileName, result[0].subHierarchies.subOrSuper[j].pos); - if (res !== null) { - result[0].subHierarchies.subOrSuper[j].subOrSuper = result[0].subHierarchies.subOrSuper[j].subOrSuper.concat(res.subHierarchies.subOrSuper); - } - } - return result[0]; + lspDriverHelper.destroyContext(localCtx); + lspDriverHelper.destroyConfig(localCfg); + return new LspClassPropertyInfo(ptr); } - + getSyntacticDiagnostics(filename: String): LspDiagsNode { let lspDriverHelper = new LspDriverHelper(); let filePath = path.resolve(filename.valueOf()); @@ -412,65 +372,4 @@ export class Lsp { lspDriverHelper.destroyConfig(localCfg); return new LspLineAndCharacter(ptr); } - - getSpanOfEnclosingComment(filename: String, offset: number, onlyMultiLine: boolean): LspTextSpan { - let lspDriverHelper = new LspDriverHelper(); - let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; - let ets2pandaCmd = ['-', '--extension', 'ets', '--arktsconfig', arktsconfig]; - let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); - const source = this.getFileContent(filePath).replace(/\r\n/g, '\n'); - let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg); - PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); - let ptr = global.es2panda._getSpanOfEnclosingComment(localCtx, offset, onlyMultiLine); - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - lspDriverHelper.destroyContext(localCtx); - lspDriverHelper.destroyConfig(localCfg); - return new LspTextSpan(ptr); - } - - provideInlayHints(filename: String, span: TextSpan): LspInlayHint[] { - let lspDriverHelper = new LspDriverHelper(); - let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; - let ets2pandaCmd = ['-', '--extension', 'ets', '--arktsconfig', arktsconfig]; - let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); - const source = this.getFileContent(filePath).replace(/\r\n/g, '\n'); - let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg); - PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); - const nativeSpan = global.es2panda._createTextSpan(span.start, span.length); - let ptr = global.es2panda._getInlayHintList(localCtx, nativeSpan); - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - lspDriverHelper.destroyContext(localCtx); - lspDriverHelper.destroyConfig(localCfg); - const inlayHintList = new LspInlayHintList(ptr); - const inlayHints: LspInlayHint[] = []; - inlayHints.push(...inlayHintList.inlayHints); - return inlayHints; - } - - getSignatureHelpItems(filename: String, offset: number): LspSignatureHelpItems { - let lspDriverHelper = new LspDriverHelper(); - let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; - let ets2pandaCmd = ['-', '--extension', 'ets', '--arktsconfig', arktsconfig]; - let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); - const source = this.getFileContent(filePath).replace(/\r\n/g, '\n'); - let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg); - PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); - let ptr = global.es2panda._getSignatureHelpItems(localCtx, offset); - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - lspDriverHelper.destroyContext(localCtx); - lspDriverHelper.destroyConfig(localCfg); - return new LspSignatureHelpItems(ptr); - } } diff --git a/ets2panda/lsp/BUILD.gn b/ets2panda/lsp/BUILD.gn index b3b6afb70a20160b9d9397d358c17e6c162fcbfc..c8615da07dcb93094dfad780603aa10917af3f0c 100644 --- a/ets2panda/lsp/BUILD.gn +++ b/ets2panda/lsp/BUILD.gn @@ -51,6 +51,7 @@ ohos_source_set("libes2panda_lsp_static") { "src/find_safe_delete_location.cpp", "src/find_references.cpp", "src/find_rename_locations.cpp", + "src/get_class_property_info.cpp", "src/get_adjusted_location.cpp", "src/get_definition_and_bound_span.cpp", "src/inlay_hints.cpp", diff --git a/ets2panda/lsp/CMakeLists.txt b/ets2panda/lsp/CMakeLists.txt index da3f1aa41f4f3a06f67e3fb74d79f8d8b9bfc53c..fcdc387a1b73ace9485632f03534aa368cc67cf1 100644 --- a/ets2panda/lsp/CMakeLists.txt +++ b/ets2panda/lsp/CMakeLists.txt @@ -37,6 +37,7 @@ set(ES2PANDA_LSP_SRC ./src/create_type_help_items.cpp ./src/script_element_kind.cpp ./src/signature_help_items.cpp + ./src/get_class_property_info.cpp ./src/signature_help.cpp ./src/todo_comments.cpp ./src/get_definition_and_bound_span.cpp diff --git a/ets2panda/lsp/include/api.h b/ets2panda/lsp/include/api.h index 471e1d56f5126ccdc71097546ae8960e91642883..fcd1b6d9e41387ea5fd449e097d8831b3ea80071 100644 --- a/ets2panda/lsp/include/api.h +++ b/ets2panda/lsp/include/api.h @@ -205,6 +205,28 @@ typedef struct DocumentHighlights { } } DocumentHighlights; +struct FieldListProperty { + std::string kind; + std::optional> modifierKinds; + std::string displayName; + size_t start; + size_t end; + + FieldListProperty(std::string k, std::optional> m, std::string d, size_t s, size_t e) + : kind(std::move(k)), modifierKinds(std::move(m)), displayName(std::move(d)), start(s), end(e) + { + } +}; + +struct FieldsInfo { + std::string name; + std::vector properties; + bool operator<(const FieldsInfo &other) const + { + return name < other.name; + } +}; + typedef struct DocumentHighlightsReferences { std::vector documentHighlights_; } DocumentHighlightsReferences; @@ -391,6 +413,7 @@ typedef struct LSPAPI { std::vector (*findReferences)( ark::es2panda::lsp::CancellationToken *tkn, const std::vector &srcFiles, const ark::es2panda::SourceFile &srcFile, size_t position); + std::vector (*getClassPropertyInfo)(es2panda_Context *context, size_t pos, bool shouldCollectInherited); DiagnosticReferences (*getSuggestionDiagnostics)(es2panda_Context *context); ark::es2panda::lsp::CompletionInfo (*getCompletionsAtPosition)(es2panda_Context *context, size_t position); ark::es2panda::lsp::ClassHierarchy (*getClassHierarchyInfo)(es2panda_Context *context, size_t position); diff --git a/ets2panda/lsp/include/class_hierarchy.h b/ets2panda/lsp/include/class_hierarchy.h index 6354c98b9e783f6e12fc1d35bb33fe6ade6d18b1..7e3e2b568157139562902a0f5ab70fdf110e3cb7 100644 --- a/ets2panda/lsp/include/class_hierarchy.h +++ b/ets2panda/lsp/include/class_hierarchy.h @@ -29,5 +29,6 @@ void GetSuperTypeHierarchies(const ir::AstNode *node, TypeHierarchies &typeHiera std::set &superLists); TypeHierarchiesInfo GetTypeHierarchiesImpl(es2panda_Context *context, size_t pos, const ir::AstNode *declaration = nullptr); +const ir::AstNode *GetEffectiveBaseTypeNode(const ir::AstNode *node); } // namespace ark::es2panda::lsp #endif \ No newline at end of file diff --git a/ets2panda/lsp/include/get_class_property_info.h b/ets2panda/lsp/include/get_class_property_info.h new file mode 100644 index 0000000000000000000000000000000000000000..c73cd691655e7213d281033d5430ff6e7239a1da --- /dev/null +++ b/ets2panda/lsp/include/get_class_property_info.h @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at* + * + * http://www.apache.org/licenses/LICENSE-2.0* + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GET_CLASS_PROPERTY_INFO_H +#define GET_CLASS_PROPERTY_INFO_H + +#include +#include +#include +#include + +#include "ast_verifier/helpers.h" +#include "cancellation_token.h" +#include "es2panda.h" +#include "class_hierarchy.h" +#include "api.h" +#include "internal_api.h" +#include "public/public.h" + +namespace ark::es2panda::lsp { +// NOLINTBEGIN(misc-non-private-member-variables-in-classes) +struct InstallPackageAction { + const std::string type_; + std::optional file; + std::optional packageName; + + InstallPackageAction() : type_("install package") {} +}; + +using CodeActionCommand = InstallPackageAction; + +struct RefactorEditInfoes { + std::vector edits; + std::optional renameFilename; + std::optional renameNumber; + std::optional> commands; +}; + +// NOLINTEND(misc-non-private-member-variables-in-classes) +/** + * @brief Get class property info at position, optionally including inherited. + * @param context Parser context (es2panda_Context*). + * @param pos Position to locate class declaration. + * @param shouldCollectInherited Collect inherited properties if true (default: false). + * @return Vector of FieldsInfo. Empty if context/position invalid or class not found. + */ +std::vector GetClassPropertyInfo(es2panda_Context *context, size_t pos, + bool shouldCollectInherited = false); +} // namespace ark::es2panda::lsp + +#endif \ No newline at end of file diff --git a/ets2panda/lsp/src/api.cpp b/ets2panda/lsp/src/api.cpp index 1bf80478ef524e5ed9df851d07ddf725b94620b1..43869c4b70b05cb4931c50e1057cacabc71a5da6 100644 --- a/ets2panda/lsp/src/api.cpp +++ b/ets2panda/lsp/src/api.cpp @@ -34,6 +34,7 @@ #include "line_column_offset.h" #include "script_element_kind.h" #include "services/services.h" +#include "get_class_property_info.h" #include "inlay_hints.h" #include "signature_help.h" @@ -263,6 +264,12 @@ std::vector FindRenameLocationsWithCancellat return res; } +std::vector GetClassPropertyInfoWrapper(es2panda_Context *context, size_t position, + bool shouldCollectInherited) +{ + return GetClassPropertyInfo(context, position, shouldCollectInherited); +} + DiagnosticReferences GetSuggestionDiagnostics(es2panda_Context *context) { DiagnosticReferences res {}; @@ -348,6 +355,7 @@ LSPAPI g_lspImpl = {GetDefinitionAtPosition, FindRenameLocationsWithCancellationWrapper, FindSafeDeleteLocation, FindReferencesWrapper, + GetClassPropertyInfoWrapper, GetSuggestionDiagnostics, GetCompletionsAtPosition, GetClassHierarchyInfo, diff --git a/ets2panda/lsp/src/get_class_property_info.cpp b/ets2panda/lsp/src/get_class_property_info.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e0228eea95e8a63370d7227d3d6bc5cee28c9e91 --- /dev/null +++ b/ets2panda/lsp/src/get_class_property_info.cpp @@ -0,0 +1,106 @@ +/** + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at* + * + * http://www.apache.org/licenses/LICENSE-2.0* + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "get_class_property_info.h" +#include "compiler/lowering/util.h" +#include "macros.h" + +namespace ark::es2panda::lsp { + +std::vector GeneratePropertyModifiers(const ir::ClassProperty *property) +{ + std::vector modifiers; + + if (property->IsPublic()) { + modifiers.emplace_back("public"); + } else if (property->IsPrivate()) { + modifiers.emplace_back("private"); + } else if (property->IsProtected()) { + modifiers.emplace_back("protected"); + } + + if (property->IsStatic()) { + modifiers.emplace_back("static"); + } + + if (property->IsReadonly()) { + modifiers.emplace_back("readonly"); + } + + return modifiers; +} + +void CollectClassProperties(const ir::AstNode *classNode, std::vector &result) +{ + if (classNode == nullptr || !classNode->IsClassDeclaration()) { + return; + } + + FieldsInfo classInfo; + classInfo.name = GetIdentifierName(const_cast(classNode)); + + auto classBody = classNode->AsClassDeclaration()->Definition()->Body(); + for (auto node : classBody) { + if (!node->IsClassProperty()) { + continue; + } + + auto property = node->AsClassProperty(); + auto modifiers = GeneratePropertyModifiers(property); + std::optional> modifiersOpt(modifiers); + + FieldListProperty propertyInfo("classField", std::move(modifiersOpt), GetIdentifierName(property), + property->Start().index, property->End().index); + + classInfo.properties.push_back(propertyInfo); + } + + result.push_back(classInfo); +} + +void CollectInheritedProperties(const ir::AstNode *classNode, std::vector &result) +{ + while (classNode != nullptr) { + auto superClass = ark::es2panda::lsp::GetEffectiveBaseTypeNode(classNode); + if (superClass != nullptr) { + CollectClassProperties(superClass, result); + } + classNode = superClass; + } +} + +std::vector GetClassPropertyInfo(es2panda_Context *context, size_t pos, bool shouldCollectInherited) +{ + std::vector result; + auto classNode = ark::es2panda::lsp::GetTargetDeclarationNodeByPosition(context, pos); + if (classNode == nullptr) { + return result; + } + CollectClassProperties(classNode, result); + if (shouldCollectInherited) { + CollectInheritedProperties(classNode, result); + } + return result; +} +} // namespace ark::es2panda::lsp \ No newline at end of file diff --git a/ets2panda/test/unit/lsp/CMakeLists.txt b/ets2panda/test/unit/lsp/CMakeLists.txt index 4fd54eb2da9242c4f0629e230d735b2e7d68da57..4f9df45155323a3dee656681d8844b258156224f 100644 --- a/ets2panda/test/unit/lsp/CMakeLists.txt +++ b/ets2panda/test/unit/lsp/CMakeLists.txt @@ -128,6 +128,10 @@ ets2panda_add_gtest(lsp_api_test_get_class_hierarchy_info CPP_SOURCES class_hierarchy_info_test.cpp ) +ets2panda_add_gtest(lsp_api_test_get_class_property_info CPP_SOURCES + get_class_property_info_test.cpp +) + ets2panda_add_gtest(lsp_api_gilap_test CPP_SOURCES get_implementation_location.cpp ) diff --git a/ets2panda/test/unit/lsp/get_class_property_info_test.cpp b/ets2panda/test/unit/lsp/get_class_property_info_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b6d8c37e24a78850931124f9ab47a23d587b0d28 --- /dev/null +++ b/ets2panda/test/unit/lsp/get_class_property_info_test.cpp @@ -0,0 +1,181 @@ +/** + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at* + * + * http://www.apache.org/licenses/LICENSE-2.0* + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "es2panda.h" +#include "lsp/include/api.h" +#include "lsp/include/get_class_property_info.h" +#include "lsp/include/cancellation_token.h" +#include "lsp_api_test.h" +#include + +namespace { +// NOLINTBEGIN +using ark::es2panda::lsp::Initializer; + +class LspGetClassPropertyInfoTests : public LSPAPITests {}; +std::vector fileNames = {"GetClassPropertyInfoFile.ets"}; +std::vector fileContents = { + R"( +enum BloodType { + A = 'A', + AB = 'AB' +} + +class Address { + province: string = ''; + city: string = ''; +} + +interface Control { + state: number +} + +interface SelectableControl extends Control { + select(): void +} + +class SelectableControlClass extends Address implements SelectableControl { + select(): void { + throw new Error("Method not implemented."); + } + + private state1: number = 0; + protected readonly hobbies: string[] = []; +} + +enum Sex { + Male = 'Male' +} + +export class Person extends SelectableControlClass implements SelectableControl { + static MAX_HEIGHT: number = 250; + static BLOOD_TYPES: BloodType = BloodType.AB; + static defaultAddress: Address = { + province: '北京', + city: '北京市', + }; + name: string = ''; + age: number = Person.MAX_HEIGHT; + weight: number = 0; + sex: Sex = Sex.Male; + bloodType: BloodType = BloodType.A; + address: Address = new Address(); + hobbies: string[] = []; + maritalStatus: 'single' | 'married' | 'divorced' = 'single'; + birthday: Date = new Date(); + location: [number, number] = [0, 0]; + avatar: Resource = $r('app.media.startIcon'); + attributes: Map = new Map(); + isEmployed: boolean = false; + private privateIsEmployed: boolean = false; + protected protectedIsEmployed: boolean = false; + protected readonly readonlyIsEmployed: boolean = false; + onUpdate: (() => void) | null = null; +} +)"}; +std::vector>> expectedResult = { + {"MAX_HEIGHT", 561, 585, "classField", {"public", "static"}}, + {"BLOOD_TYPES", 596, 633, "classField", {"public", "static"}}, + {"defaultAddress", 644, 722, "classField", {"public", "static"}}, + {"name", 726, 743, "classField", {"public"}}, + {"age", 747, 778, "classField", {"public"}}, + {"weight", 782, 800, "classField", {"public"}}, + {"sex", 804, 823, "classField", {"public"}}, + {"bloodType", 827, 861, "classField", {"public"}}, + {"address", 865, 898, "classField", {"public"}}, + {"hobbies", 901, 923, "classField", {"public"}}, + {"maritalStatus", 927, 986, "classField", {"public"}}, + {"birthday", 990, 1018, "classField", {"public"}}, + {"location", 1021, 1056, "classField", {"public"}}, + {"avatar", 1060, 1104, "classField", {"public"}}, + {"attributes", 1108, 1152, "classField", {"public"}}, + {"isEmployed", 1155, 1182, "classField", {"public"}}, + {"privateIsEmployed", 1194, 1228, "classField", {"private"}}, + {"protectedIsEmployed", 1242, 1278, "classField", {"protected"}}, + {"readonlyIsEmployed", 1301, 1336, "classField", {"protected", "readonly"}}, + {"onUpdate", 1340, 1376, "classField", {"public"}}}; + +void CheckClassPropertiesMatch(const std::vector &actualProperties) +{ + for (size_t i = 0; i < actualProperties.size(); ++i) { + const auto &perp = actualProperties[i]; + const auto &expected = expectedResult[i]; + + const auto &expectedName = std::get<0>(expected); + const auto expectedStart = std::get<1>(expected); + const auto expectedEnd = std::get<2>(expected); + const auto &expectedKind = std::get<3>(expected); + const auto &expectedModifiers = std::get<4>(expected); + + bool nameMatch = (perp.displayName == expectedName); + bool startMatch = (perp.start == expectedStart); + bool endMatch = (perp.end == expectedEnd); + bool kindMatch = (perp.kind == expectedKind); + + bool modifiersMatch = true; + if (perp.modifierKinds.has_value()) { + const auto &actualModifiers = perp.modifierKinds.value(); + modifiersMatch = (actualModifiers == expectedModifiers); // 严格比较顺序 + } else { + modifiersMatch = expectedModifiers.empty(); + } + + bool currentMatch = nameMatch && startMatch && endMatch && kindMatch && modifiersMatch; + ASSERT_EQ(true, currentMatch); + } +} + +TEST_F(LspGetClassPropertyInfoTests, GetClassPropertyInfoMethod1) +{ + constexpr size_t EXPECTED_CLASS_COUNT = 3; + constexpr size_t EXPECTED_CLASS_COUNT_ONE = 1; + constexpr size_t EXPECTED_PROP_COUNT = 20; + + auto filePaths = CreateTempFile(fileNames, fileContents); + std::vector sourceFiles; + + for (size_t i = 0; i < filePaths.size(); ++i) { + sourceFiles.emplace_back(filePaths[i], fileContents[i]); + } + ASSERT_EQ(fileNames.size(), sourceFiles.size()); + + Initializer initializer; + size_t sourceIndex = 0; + size_t tokenOffset = 800; + auto filePath = std::string {sourceFiles[sourceIndex].filePath}; + auto fileContent = std::string {sourceFiles[sourceIndex].source}; + auto context = initializer.CreateContext(filePath.c_str(), ES2PANDA_STATE_CHECKED, fileContent.c_str()); + + auto infos = ark::es2panda::lsp::GetClassPropertyInfo(context, tokenOffset, true); + ASSERT_EQ(EXPECTED_CLASS_COUNT, infos.size()); + + auto infos2 = ark::es2panda::lsp::GetClassPropertyInfo(context, tokenOffset); + initializer.DestroyContext(context); + ASSERT_EQ(EXPECTED_CLASS_COUNT_ONE, infos2.size()); + + FieldsInfo info = infos2[0]; + ASSERT_EQ(EXPECTED_PROP_COUNT, info.properties.size()); + ASSERT_EQ("Person", info.name); + CheckClassPropertiesMatch(info.properties); +} +// NOLINTEND +} // namespace \ No newline at end of file