Files
carbon-lang/toolchain/source/source_buffer.cpp
T
Jon Ross-Perkins 9e466b9335 Cache calculated file state in LSP (#4897)
Add caching of parsed documents, and testing of the textDocument
handlers. This is based on #4896, which splits out some of the
boilerplate to calls.

Note, this caches the entire parse state because we'll want to try to
emit diagnostics when we see the update, without waiting. It may be
helpful to do that asynchronously, but we don't want to wait for another
call (such as documentSymbol). Really, we'll probably want to also add
check for diagnostics, at least.
2025-02-07 01:04:42 +00:00

88 lines
3.2 KiB
C++

// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include "toolchain/source/source_buffer.h"
#include <limits>
#include "llvm/Support/ErrorOr.h"
#include "toolchain/diagnostics/file_diagnostics.h"
namespace Carbon {
auto SourceBuffer::MakeFromStdin(DiagnosticConsumer& consumer)
-> std::optional<SourceBuffer> {
return MakeFromMemoryBuffer(llvm::MemoryBuffer::getSTDIN(), "<stdin>",
/*is_regular_file=*/false, consumer);
}
auto SourceBuffer::MakeFromFile(llvm::vfs::FileSystem& fs,
llvm::StringRef filename,
DiagnosticConsumer& consumer)
-> std::optional<SourceBuffer> {
FileDiagnosticEmitter emitter(&consumer);
llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>> file =
fs.openFileForRead(filename);
if (file.getError()) {
CARBON_DIAGNOSTIC(ErrorOpeningFile, Error,
"error opening file for read: {0}", std::string);
emitter.Emit(filename, ErrorOpeningFile, file.getError().message());
return std::nullopt;
}
llvm::ErrorOr<llvm::vfs::Status> status = (*file)->status();
if (status.getError()) {
CARBON_DIAGNOSTIC(ErrorStattingFile, Error, "error statting file: {0}",
std::string);
emitter.Emit(filename, ErrorStattingFile, file.getError().message());
return std::nullopt;
}
// `stat` on a file without a known size gives a size of 0, which causes
// `llvm::vfs::File::getBuffer` to produce an empty buffer. Use a size of -1
// in this case so we get the complete file contents.
bool is_regular_file = status->isRegularFile();
int64_t size = is_regular_file ? status->getSize() : -1;
return MakeFromMemoryBuffer(
(*file)->getBuffer(filename, size, /*RequiresNullTerminator=*/false),
filename, is_regular_file, consumer);
}
auto SourceBuffer::MakeFromStringCopy(llvm::StringRef filename,
llvm::StringRef text,
DiagnosticConsumer& consumer)
-> std::optional<SourceBuffer> {
return MakeFromMemoryBuffer(
llvm::MemoryBuffer::getMemBufferCopy(text, filename), filename,
/*is_regular_file=*/true, consumer);
}
auto SourceBuffer::MakeFromMemoryBuffer(
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> buffer,
llvm::StringRef filename, bool is_regular_file,
DiagnosticConsumer& consumer) -> std::optional<SourceBuffer> {
FileDiagnosticEmitter emitter(&consumer);
if (buffer.getError()) {
CARBON_DIAGNOSTIC(ErrorReadingFile, Error, "error reading file: {0}",
std::string);
emitter.Emit(filename, ErrorReadingFile, buffer.getError().message());
return std::nullopt;
}
if (buffer.get()->getBufferSize() >= std::numeric_limits<int32_t>::max()) {
CARBON_DIAGNOSTIC(FileTooLarge, Error,
"file is over the 2GiB input limit; size is {0} bytes",
int64_t);
emitter.Emit(filename, FileTooLarge, buffer.get()->getBufferSize());
return std::nullopt;
}
return SourceBuffer(filename.str(), std::move(buffer.get()), is_regular_file);
}
} // namespace Carbon