146 lines
3.7 KiB
C++
146 lines
3.7 KiB
C++
/*!
|
||
@file src/SourceManager/SourceManager.hpp
|
||
@brief 源代码管理
|
||
@author PuqiAR (im@puqiar.top)
|
||
@date 2026-02-14
|
||
*/
|
||
|
||
#pragma once
|
||
|
||
#include <Core/SourceLocations.hpp>
|
||
#include <Deps/Deps.hpp>
|
||
|
||
|
||
#include <fstream>
|
||
|
||
namespace Fig
|
||
{
|
||
class SourceManager
|
||
{
|
||
private:
|
||
String filePath;
|
||
String source;
|
||
std::vector<String> lines;
|
||
std::vector<size_t> lineStartIndex; // 每行在整个源字符串中的起始 index
|
||
|
||
void preprocessLineIndices()
|
||
{
|
||
lineStartIndex.clear();
|
||
lineStartIndex.push_back(0);
|
||
|
||
for (size_t i = 0; i < source.length(); ++i)
|
||
{
|
||
if (source[i] == U'\n')
|
||
{
|
||
lineStartIndex.push_back(i + 1);
|
||
}
|
||
else if (source[i] == U'\r')
|
||
{
|
||
// 处理 CRLF,只在 \n 处记录
|
||
if (i + 1 < source.length() && source[i + 1] == U'\n')
|
||
continue;
|
||
|
||
lineStartIndex.push_back(i + 1);
|
||
}
|
||
}
|
||
}
|
||
|
||
public:
|
||
bool read = false;
|
||
String &Read()
|
||
{
|
||
std::fstream fs(filePath.toStdString());
|
||
if (!fs.is_open())
|
||
{
|
||
read = false;
|
||
return source;
|
||
}
|
||
std::string line;
|
||
while (std::getline(fs, line))
|
||
{
|
||
source += line + '\n';
|
||
lines.push_back(String(line));
|
||
}
|
||
if (!source.empty() && source.back() == U'\n')
|
||
source.pop_back();
|
||
|
||
if (lines.empty())
|
||
{
|
||
lines.push_back(String()); // 填充一个空的
|
||
}
|
||
read = true;
|
||
preprocessLineIndices();
|
||
return source;
|
||
}
|
||
|
||
SourceManager() {}
|
||
SourceManager(String _path)
|
||
{
|
||
filePath = std::move(_path);
|
||
}
|
||
|
||
bool HasLine(int64_t _line) const
|
||
{
|
||
return _line <= lines.size() && _line >= 1;
|
||
}
|
||
|
||
const String &GetLine(size_t _line) const
|
||
{
|
||
assert(_line <= lines.size() && "SourceManager: GetLine failed, index out of range");
|
||
return lines[_line - 1];
|
||
}
|
||
|
||
String GetSub(size_t _index_start, size_t _length) const
|
||
{
|
||
return source.substr(_index_start, _length);
|
||
}
|
||
|
||
const String &GetSource() const
|
||
{
|
||
return source;
|
||
}
|
||
|
||
String &GetSource()
|
||
{
|
||
return source;
|
||
}
|
||
|
||
std::pair<size_t, size_t> GetLineColumn(size_t index) const
|
||
{
|
||
if (lineStartIndex.empty())
|
||
{
|
||
return {1, 1};
|
||
}
|
||
|
||
// clamp index 到合法范围(Parser报错可能传入EOF位置)
|
||
// size_t lastLine = lineStartIndex.size() - 1;
|
||
if (index < lineStartIndex[0])
|
||
{
|
||
return {1, 1};
|
||
}
|
||
|
||
// upper_bound 找到第一个 > index 的行起点
|
||
auto it = std::ranges::upper_bound(lineStartIndex.begin(), lineStartIndex.end(), index);
|
||
|
||
size_t line;
|
||
if (it == lineStartIndex.begin())
|
||
{
|
||
line = 0;
|
||
}
|
||
else
|
||
{
|
||
line = static_cast<size_t>(it - lineStartIndex.begin() - 1);
|
||
}
|
||
|
||
size_t column = index - lineStartIndex[line] + 1;
|
||
return {line + 1, column};
|
||
}
|
||
|
||
void LoadFromMemory(String src)
|
||
{
|
||
source = std::move(src);
|
||
read = true;
|
||
preprocessLineIndices();
|
||
}
|
||
};
|
||
}; // namespace Fig
|