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