• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2024-2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import ts from 'ohos-typescript';
17
18import Logger, { LOG_MODULE_TYPE } from '../../utils/logger';
19const logger = Logger.getLogger(LOG_MODULE_TYPE.ARKANALYZER, 'Position');
20
21const LOW_BITS_SIZE = 16;
22const LOW_BITS_MASK = 0xffff;
23const HIGH_BITS_MASK = 0xffff0000;
24const MIN_NUMBER = 0;
25const MAX_NUMBER = 0xffff;
26const INVALID_LINE = -1;
27
28export type LineCol = number;
29
30export function setLine(lineCol: LineCol, lineNo: number): LineCol {
31    if (lineNo < MIN_NUMBER) {
32        lineNo = MIN_NUMBER;
33    }
34    if (lineNo > MAX_NUMBER) {
35        logger.warn(`setLine overflow ${lineNo}`);
36        lineNo = MAX_NUMBER;
37    }
38
39    return (lineNo << LOW_BITS_SIZE) | (lineCol & LOW_BITS_MASK);
40}
41
42export function setCol(lineCol: LineCol, colNo: number): LineCol {
43    if (colNo < MIN_NUMBER) {
44        colNo = MIN_NUMBER;
45    }
46    if (colNo > MAX_NUMBER) {
47        logger.warn(`setCol overflow ${colNo}`);
48        colNo = MAX_NUMBER;
49    }
50
51    return (lineCol & HIGH_BITS_MASK) | colNo;
52}
53
54export function setLineCol(lineNo: number, colNo: number): LineCol {
55    let lineCol: LineCol = 0;
56    lineCol = setLine(lineCol, lineNo);
57    lineCol = setCol(lineCol, colNo);
58    return lineCol;
59}
60
61export function getLineNo(lineCol: LineCol): number {
62    let line = lineCol >>> LOW_BITS_SIZE;
63    if (line === MIN_NUMBER) {
64        return INVALID_LINE;
65    }
66    return line;
67}
68
69export function getColNo(lineCol: LineCol): number {
70    let col = lineCol & LOW_BITS_MASK;
71    if (col === MIN_NUMBER) {
72        return INVALID_LINE;
73    }
74    return col;
75}
76
77/**
78 * @category core/base
79 */
80export class LineColPosition {
81    private readonly lineCol: LineCol;
82
83    public static readonly DEFAULT: LineColPosition = new LineColPosition(INVALID_LINE, INVALID_LINE);
84
85    constructor(lineNo: number, colNo: number) {
86        this.lineCol = setLineCol(lineNo, colNo);
87    }
88
89    public getLineNo(): number {
90        return getLineNo(this.lineCol);
91    }
92
93    public getColNo(): number {
94        return getColNo(this.lineCol);
95    }
96
97    public static buildFromNode(node: ts.Node, sourceFile: ts.SourceFile): LineColPosition {
98        let { line, character } = ts.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile));
99        // line start from 1.
100        return new LineColPosition(line + 1, character + 1);
101    }
102}
103
104export class FullPosition {
105    private readonly first: LineCol;
106    private readonly last: LineCol;
107
108    public static readonly DEFAULT: FullPosition = new FullPosition(INVALID_LINE, INVALID_LINE, INVALID_LINE, INVALID_LINE);
109
110    constructor(firstLine: number, firstCol: number, lastLine: number, lastCol: number) {
111        this.first = setLineCol(firstLine, firstCol);
112        this.last = setLineCol(lastLine, lastCol);
113    }
114
115    public getFirstLine(): number {
116        return getLineNo(this.first);
117    }
118
119    public getLastLine(): number {
120        return getLineNo(this.last);
121    }
122
123    public getFirstCol(): number {
124        return getColNo(this.first);
125    }
126
127    public getLastCol(): number {
128        return getColNo(this.last);
129    }
130
131    public static buildFromNode(node: ts.Node, sourceFile: ts.SourceFile): FullPosition {
132        const { line: startLine, character: startCharacter } = ts.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile));
133        const { line: endLine, character: endCharacter } = ts.getLineAndCharacterOfPosition(sourceFile, node.getEnd());
134
135        // line start from 1
136        return new FullPosition(startLine + 1, startCharacter + 1, endLine + 1, endCharacter + 1);
137    }
138
139    public static merge(leftMostPosition: FullPosition, rightMostPosition: FullPosition): FullPosition {
140        return new FullPosition(
141            leftMostPosition.getFirstLine(),
142            leftMostPosition.getFirstCol(),
143            rightMostPosition.getLastLine(),
144            rightMostPosition.getLastCol()
145        );
146    }
147}
148