diff --git a/ets2panda/bindings/native/src/lsp.cpp b/ets2panda/bindings/native/src/lsp.cpp index 1054d5b948437512a82d62e6935bd74e47d1b41e..f4f29b2440981e65e48a9cbecd5a41f18b3774e9 100644 --- a/ets2panda/bindings/native/src/lsp.cpp +++ b/ets2panda/bindings/native/src/lsp.cpp @@ -1838,4 +1838,25 @@ KNativePointer impl_getDefinitionDataFromNode(KNativePointer astNodePtr, KString LSPAPI const *impl = GetImpl(); return new DefinitionInfo(impl->getDefinitionDataFromNode(ast, nodeNamePtr.data())); } -TS_INTEROP_2(getDefinitionDataFromNode, KNativePointer, KNativePointer, KStringPtr) \ No newline at end of file +TS_INTEROP_2(getDefinitionDataFromNode, KNativePointer, KNativePointer, KStringPtr) + +KInt impl_getSourceLocationLine(KNativePointer locationPtr) +{ + auto *location = reinterpret_cast *>(locationPtr); + return location->first; +} +TS_INTEROP_1(getSourceLocationLine, KInt, KNativePointer) + +KInt impl_getSourceLocationColumn(KNativePointer locationPtr) +{ + auto *location = reinterpret_cast *>(locationPtr); + return location->second; +} +TS_INTEROP_1(getSourceLocationColumn, KInt, KNativePointer) + +KNativePointer impl_getColAndLineByOffset(KStringPtr &sourceCodePtr, KInt offset) +{ + LSPAPI const *impl = GetImpl(); + return new std::pair(impl->getColAndLineByOffset(sourceCodePtr.data(), offset)); +} +TS_INTEROP_2(getColAndLineByOffset, KNativePointer, KStringPtr, KInt) diff --git a/ets2panda/bindings/src/common/Es2pandaNativeModule.ts b/ets2panda/bindings/src/common/Es2pandaNativeModule.ts index 1693d391f6d4a5e01f3173e88dc4676eec9059c6..e667da211d9eb9a9c6d1622d7141442e2519e5bf 100644 --- a/ets2panda/bindings/src/common/Es2pandaNativeModule.ts +++ b/ets2panda/bindings/src/common/Es2pandaNativeModule.ts @@ -630,6 +630,14 @@ export class Es2pandaNativeModule { throw new Error('Not implemented'); } + _getSourceLocationLine(ptr: KNativePointer): KInt { + throw new Error('Not implemented'); + } + + _getSourceLocationColumn(ptr: KNativePointer): KInt { + throw new Error('Not implemented'); + } + _getHighlightTextSpan(ptr: KNativePointer): KPtr { throw new Error('Not implemented'); } @@ -948,6 +956,10 @@ export class Es2pandaNativeModule { throw new Error('Not implemented'); } + _getColAndLineByOffset(sourceCode: String, offset: KInt): KInt { + throw new Error('Not implemented'); + } + _MemInitialize(pandaLibPath: KStringPtr): void { throw new Error('Not implemented'); } diff --git a/ets2panda/bindings/src/lsp/lspNode.ts b/ets2panda/bindings/src/lsp/lspNode.ts index 492350d708ede2822d13b30c4aa46f007edb110b..33650fc8e41af4023ff125bf40e2eca1d6aebe08 100644 --- a/ets2panda/bindings/src/lsp/lspNode.ts +++ b/ets2panda/bindings/src/lsp/lspNode.ts @@ -229,6 +229,16 @@ export class LspTextSpan extends LspNode { readonly length: KInt; } +export class LspSourceLocation extends LspNode { + constructor(peer: KNativePointer) { + super(peer); + this.line = global.es2panda._getSourceLocationLine(peer); + this.column = global.es2panda._getSourceLocationColumn(peer); + } + readonly line: number; + readonly column: number; +} + export interface TextSpan { start: KInt; length: KInt; diff --git a/ets2panda/bindings/src/lsp/lsp_helper.ts b/ets2panda/bindings/src/lsp/lsp_helper.ts index c715a854bdcfd0fec97ce3b49807eaef13e58a9b..a90676decb636aaabf2424e196e9dcc2cc34bd46 100644 --- a/ets2panda/bindings/src/lsp/lsp_helper.ts +++ b/ets2panda/bindings/src/lsp/lsp_helper.ts @@ -47,7 +47,8 @@ import { LspRenameLocation, LspRenameInfoType, LspRenameInfoSuccess, - LspRenameInfoFailure + LspRenameInfoFailure, + LspSourceLocation } from './lspNode'; import { passStringArray, unpackString } from '../common/private'; import { Es2pandaContextState } from '../generated/Es2pandaEnums'; @@ -243,6 +244,11 @@ export class Lsp { return global.es2panda._getOffsetByColAndLine(sourceCode, line, column); } + getColAndLineByOffset(filename: String, offset: number): LspSourceLocation { + const sourceCode = this.getFileSource(filename.valueOf()); + return new LspSourceLocation(global.es2panda._getColAndLineByOffset(sourceCode, offset)); + } + getDefinitionAtPosition(filename: String, offset: number, nodeInfos?: NodeInfo[]): LspDefinitionData { if (nodeInfos) { return this.getDefinitionAtPositionByNodeInfos(filename, nodeInfos); diff --git a/ets2panda/bindings/test/cases.ts b/ets2panda/bindings/test/cases.ts index 583e8a93d3d8e6a5e1dc135f76861e28a38fd9f7..cd9a6dd119b3068d9f8c07b9708b11c0c1cfc15e 100644 --- a/ets2panda/bindings/test/cases.ts +++ b/ets2panda/bindings/test/cases.ts @@ -187,6 +187,10 @@ export const basicCases: TestCases = { expectedFilePath: resolveTestPath('test/expected/getOffsetByColAndLine.json'), '1': [resolveTestPath('test/testcases/getOffsetByColAndLine/getOffsetByColAndLine1.ets'), 51, 14] }, + getColAndLineByOffset: { + expectedFilePath: resolveTestPath('test/expected/getColAndLineByOffset.json'), + '1': [resolveTestPath('test/testcases/getColAndLineByOffset/getColAndLineByOffset1.ets'), 1035] + }, entry: { expectedFilePath: '', '1': [resolveTestPath('test/testcases/entry/Index.ets'), 615] diff --git a/ets2panda/bindings/test/expected/getColAndLineByOffset.json b/ets2panda/bindings/test/expected/getColAndLineByOffset.json new file mode 100644 index 0000000000000000000000000000000000000000..775479eeba7ac69f5884ff771b9eb40cb4899f47 --- /dev/null +++ b/ets2panda/bindings/test/expected/getColAndLineByOffset.json @@ -0,0 +1,6 @@ +{ + "1": { + "line": 35, + "column": 8 + } +} diff --git a/ets2panda/bindings/test/testcases/getColAndLineByOffset/getColAndLineByOffset1.ets b/ets2panda/bindings/test/testcases/getColAndLineByOffset/getColAndLineByOffset1.ets new file mode 100644 index 0000000000000000000000000000000000000000..aece61fb54097614fdede1cf87e302f5f888a1e5 --- /dev/null +++ b/ets2panda/bindings/test/testcases/getColAndLineByOffset/getColAndLineByOffset1.ets @@ -0,0 +1,53 @@ +/* + * 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. + */ + +// comment of line + +import { memo, __memo_context_type, __memo_id_type } from '@ohos.arkui.stateManagement' +import { + Entry + Text, + TextAttributes, + Column, + Component, + Button, + ButtonAttribute, + ClickEvent, + UserView +} from '@ohos.arkui.component' +import { State, MutableSate, stateOf, observableProxy } from '@ohos.arkui.stateManagement' +import hilog from '@ohos.hilog' + +@Entry +@Component +struct Index { + @State message: string = 'Hello World'; + + build() { + Row() { + Column() { + Text(this.message) + .fontSize(50) + .fontWeight(FontWeight.Bold) + } + .onClick(() => { + console.info("hello, :") + console.log(result.toString()) + }) + .width('100%') + } + .height('100%') + } +} diff --git a/ets2panda/lexer/token/sourceLocation.cpp b/ets2panda/lexer/token/sourceLocation.cpp index cb094910fcd01414e8d1cdcd24b9dffa7610aef1..ce80ab7c211fd67cbc408b60421d85acba5ae870 100644 --- a/ets2panda/lexer/token/sourceLocation.cpp +++ b/ets2panda/lexer/token/sourceLocation.cpp @@ -101,6 +101,49 @@ SourceLocation LineIndex::GetLocation(SourcePosition pos) const noexcept return SourceLocation(line + 1, col + 1, pos.Program()); } +std::pair LineIndex::GetLocation(size_t offset) const noexcept +{ + if (entries_.empty() || offset == 0) { + return {1, 1}; + } + + size_t line = 0; + size_t left = 0; + size_t right = entries_.size(); + + while (left < right) { + size_t mid = left + (right - left) / 2; + if (entries_[mid].lineStart <= offset) { + line = mid; + left = mid + 1; + } else { + right = mid; + } + } + + if (line >= entries_.size()) { + return {entries_.size() + 1, 1}; + } + + const auto &entry = entries_[line]; + size_t remainingOffset = offset - entry.lineStart; + size_t col = 0; + + for (const auto &range : entry.ranges) { + size_t rangeBytes = range.cnt * range.byteSize; + + if (remainingOffset < rangeBytes) { + col += remainingOffset / range.byteSize; + break; + } + + col += range.cnt; + remainingOffset -= rangeBytes; + } + + return {line + 1, col + 1}; +} + size_t LineIndex::GetOffset(SourceLocation loc) const noexcept { ES2PANDA_ASSERT(loc.line != 0); diff --git a/ets2panda/lexer/token/sourceLocation.h b/ets2panda/lexer/token/sourceLocation.h index 491199d36abed0f09ee9abe0c21ea6be81e7bc11..b0bf909e3f267db4df35632b44a9296d27cad923 100644 --- a/ets2panda/lexer/token/sourceLocation.h +++ b/ets2panda/lexer/token/sourceLocation.h @@ -207,6 +207,7 @@ public: ~LineIndex() = default; SourceLocation GetLocation(SourcePosition pos) const noexcept; + std::pair GetLocation(size_t offset) const noexcept; size_t GetOffset(SourceLocation loc) const noexcept; private: diff --git a/ets2panda/lsp/include/api.h b/ets2panda/lsp/include/api.h index 925159082ee1c67698093fe3df6011150c09296b..8feb3be6823fd80ca46f771ce37666b22a39fd09 100644 --- a/ets2panda/lsp/include/api.h +++ b/ets2panda/lsp/include/api.h @@ -546,6 +546,7 @@ typedef struct LSPAPI { InlayHintList (*provideInlayHints)(es2panda_Context *context, const TextSpan *span); SignatureHelpItems (*getSignatureHelpItems)(es2panda_Context *context, size_t position); size_t (*getOffsetByColAndLine)(const std::string &sourceCode, size_t line, size_t column); + std::pair (*getColAndLineByOffset)(const std::string &sourceCode, size_t offset); std::vector (*getCodeFixesAtPosition)(es2panda_Context *context, size_t start_position, size_t end_position, std::vector &errorCodes, CodeFixOptions &codeFixOptions); diff --git a/ets2panda/lsp/src/api.cpp b/ets2panda/lsp/src/api.cpp index 50fb219f826613b4ac7fd8f72b87f5a564021ce8..8f942b3daae489bcdcb9bc76be00cfbe08342607 100644 --- a/ets2panda/lsp/src/api.cpp +++ b/ets2panda/lsp/src/api.cpp @@ -437,6 +437,12 @@ size_t GetOffsetByColAndLine(const std::string &sourceCode, size_t line, size_t return index.GetOffset(lexer::SourceLocation(line, column, nullptr)); } +std::pair GetColAndLineByOffset(const std::string &sourceCode, size_t offset) +{ + auto index = lexer::LineIndex(util::StringView(sourceCode)); + return index.GetLocation(offset); +} + std::vector GetCodeFixesAtPosition(es2panda_Context *context, size_t startPosition, size_t endPosition, std::vector &errorCodes, CodeFixOptions &codeFixOptions) @@ -543,6 +549,7 @@ LSPAPI g_lspImpl = {GetDefinitionAtPosition, ProvideInlayHints, GetSignatureHelpItems, GetOffsetByColAndLine, + GetColAndLineByOffset, GetCodeFixesAtPosition, GetCombinedCodeFix, GetNameOrDottedNameSpan, diff --git a/ets2panda/test/unit/lsp/get_offset_by_col_and_line.cpp b/ets2panda/test/unit/lsp/get_offset_by_col_and_line.cpp index 008c9285db9434702a2b816f3ab34fc90174d18c..cfa26d373734209b02724dd9bd3dcc346148449c 100644 --- a/ets2panda/test/unit/lsp/get_offset_by_col_and_line.cpp +++ b/ets2panda/test/unit/lsp/get_offset_by_col_and_line.cpp @@ -22,7 +22,7 @@ namespace { class LSPOffsetTests : public LSPAPITests {}; -TEST_F(LSPAPITests, getOffsetComment) +TEST_F(LSPOffsetTests, getOffsetComment) { LSPAPI const *lspApi = GetImpl(); const size_t line = 4; @@ -37,4 +37,20 @@ let aaa = 'default string'; ASSERT_EQ(res, expectedOffset); } +TEST_F(LSPOffsetTests, getLineAndColByOffset) +{ + LSPAPI const *lspApi = GetImpl(); + const size_t offset = 28; + auto res = lspApi->getColAndLineByOffset(R"delimiter( +// comment of line 2 + +let aaa = 'default string'; +)delimiter", + offset); + const size_t expectedLine = 4; + const size_t expectedCol = 6; + ASSERT_EQ(res.first, expectedLine); + ASSERT_EQ(res.second, expectedCol); +} + } // namespace