104 lines
2.9 KiB
Python
104 lines
2.9 KiB
Python
import re
|
|
from collections import defaultdict
|
|
from typing import Dict, List, Optional, Tuple
|
|
|
|
from tools.file_util import retrieve_file_content
|
|
|
|
from lib.ide_service import Position, SymbolNode
|
|
|
|
|
|
def split_tokens(text: str) -> Dict[str, List[int]]:
|
|
"""
|
|
Split a line of text into tokens.
|
|
Return a dictionary of token -> character numbers.
|
|
|
|
Not a perfect implementation, but may be enough for now.
|
|
"""
|
|
matches = re.finditer(r"\b\w+\b", text)
|
|
result = defaultdict(list)
|
|
for match in matches:
|
|
token = match.group()
|
|
result[token].append(match.start())
|
|
|
|
return result
|
|
|
|
|
|
def locate_symbol_by_name(
|
|
symbol_name: str, abspath: str, line_numbers: Optional[List[int]] = None
|
|
) -> List[Position]:
|
|
"""
|
|
Find the locations of the specified symbol in the specified file.
|
|
Line and column numbers are 0-based.
|
|
|
|
symbol_name: The name of the symbol to find.
|
|
abspath: The absolute path to the file to search.
|
|
line_numbers: The line numbers to search for the symbol.
|
|
If None, search the entire file.
|
|
|
|
return: a list of Position
|
|
"""
|
|
line_set = set(line_numbers) if line_numbers else None
|
|
|
|
positions: List[Position] = []
|
|
with open(abspath, "r") as file:
|
|
for i, line in enumerate(file):
|
|
if line_set and i not in line_set:
|
|
continue
|
|
|
|
tokens = split_tokens(line)
|
|
chars = tokens.get(symbol_name, [])
|
|
for char in chars:
|
|
positions.append(Position(line=i, character=char))
|
|
|
|
return positions
|
|
|
|
|
|
def find_symbol_nodes(
|
|
symbols: List[SymbolNode], name: Optional[str] = None, line: Optional[int] = None
|
|
) -> List[Tuple[SymbolNode, int]]:
|
|
"""
|
|
Find the symbols with the specified name and line number.
|
|
|
|
return: a list of tuples (symbol, depth)
|
|
"""
|
|
assert name is not None or line is not None
|
|
|
|
res = []
|
|
stack = [(s, 0) for s in symbols]
|
|
while stack:
|
|
symbol, depth = stack.pop()
|
|
flag = True
|
|
if name and symbol.name != name:
|
|
flag = False
|
|
if line and symbol.range.start.line != line:
|
|
flag = False
|
|
|
|
if flag:
|
|
res.append((symbol, depth))
|
|
else:
|
|
stack.extend((c, depth + 1) for c in reversed(symbol.children))
|
|
|
|
return res
|
|
|
|
|
|
def get_symbol_content(
|
|
symbol: SymbolNode,
|
|
file_content: Optional[str] = None,
|
|
abspath: Optional[str] = None,
|
|
) -> str:
|
|
"""
|
|
Get the content of the symbol in the file.
|
|
"""
|
|
if file_content is None and abspath is None:
|
|
raise ValueError("Either file_content or abspath should be provided")
|
|
|
|
if file_content is None:
|
|
file_content = retrieve_file_content(abspath, None)
|
|
|
|
lines = file_content.split("\n")
|
|
|
|
content = lines[symbol.range.start.line : symbol.range.end.line]
|
|
content.append(lines[symbol.range.end.line][: symbol.range.end.character])
|
|
|
|
return "\n".join(content)
|