forked from microsoft/tolerant-php-parser
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathFilePositionMap.php
More file actions
115 lines (101 loc) · 4.51 KB
/
FilePositionMap.php
File metadata and controls
115 lines (101 loc) · 4.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
<?php
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
namespace Microsoft\PhpParser;
/**
* FilePositionMap can be used to get the line number for a large number of nodes (starting from 1).
* It works the most efficiently when the requested node is close to the previously requested node.
*
* Other designs that weren't chosen:
* - Precomputing all of the start/end offsets when initializing was slower - Some offsets weren't needed, and walking the tree was slower.
* - Caching line numbers for previously requested offsets wasn't really necessary, since offsets are usually close together and weren't requested repeatedly.
*/
class FilePositionMap {
/** @var string the full file contents */
private $fileContents;
/** @var int - Precomputed strlen($file_contents) */
private $fileContentsLength;
/** @var int the 0-based byte offset of the most recent request for a line number. */
private $currentOffset;
/** @var int the 1-based line number for $this->currentOffset (updated whenever currentOffset is updated) */
private $lineForCurrentOffset;
public function __construct(string $file_contents) {
$this->fileContents = $file_contents;
$this->fileContentsLength = \strlen($file_contents);
$this->currentOffset = 0;
$this->lineForCurrentOffset = 1;
}
/**
* @param Node|Token $node
*/
public function getStartLine($node) : int {
return $this->getLineNumberForOffset($node->getStartPosition());
}
/**
* @param Node|Token $node
* Similar to getStartLine but includes the column
*/
public function getStartLineCharacterPositionForOffset($node) : LineCharacterPosition {
return $this->getLineCharacterPositionForOffset($node->getStartPosition());
}
/** @param Node|Token $node */
public function getEndLine($node) : int {
return $this->getLineNumberForOffset($node->getEndPosition());
}
/**
* @param Node|Token $node
* Similar to getStartLine but includes the column
*/
public function getEndLineCharacterPosition($node) : LineCharacterPosition {
return $this->getLineCharacterPositionForOffset($node->getEndPosition());
}
/**
* @param int $offset
* Similar to getStartLine but includes both the line and the column
*/
public function getLineCharacterPositionForOffset(int $offset) : LineCharacterPosition {
$line = $this->getLineNumberForOffset($offset);
$character = $this->getColumnForOffset($offset);
return new LineCharacterPosition($line, $character);
}
/**
* @param int $offset - A 0-based byte offset
* @return int - gets the 1-based line number for $offset
*/
public function getLineNumberForOffset(int $offset) : int {
if ($offset < 0) {
$offset = 0;
} elseif ($offset > $this->fileContentsLength) {
$offset = $this->fileContentsLength;
}
$currentOffset = $this->currentOffset;
if ($offset > $currentOffset) {
$this->lineForCurrentOffset += \substr_count($this->fileContents, "\n", $currentOffset, $offset - $currentOffset);
$this->currentOffset = $offset;
} elseif ($offset < $currentOffset) {
$this->lineForCurrentOffset -= \substr_count($this->fileContents, "\n", $offset, $currentOffset - $offset);
$this->currentOffset = $offset;
}
return $this->lineForCurrentOffset;
}
/**
* @param int $offset - A 0-based byte offset
* @return int - gets the 1-based column number for $offset
*/
public function getColumnForOffset(int $offset) : int {
$length = $this->fileContentsLength;
if ($offset <= 1) {
return 1;
} elseif ($offset > $length) {
$offset = $length;
}
// Postcondition: offset >= 1, ($lastNewlinePos < $offset)
// If there was no previous newline, lastNewlinePos = 0
// Start strrpos check from the character before the current character,
// in case the current character is a newline.
$lastNewlinePos = \strrpos($this->fileContents, "\n", -$length + $offset - 1);
return 1 + $offset - ($lastNewlinePos === false ? 0 : $lastNewlinePos + 1);
}
}