diff options
Diffstat (limited to 'graphics/asymptote/LspCpp/LibLsp/lsp/utils.cpp')
-rw-r--r-- | graphics/asymptote/LspCpp/LibLsp/lsp/utils.cpp | 602 |
1 files changed, 602 insertions, 0 deletions
diff --git a/graphics/asymptote/LspCpp/LibLsp/lsp/utils.cpp b/graphics/asymptote/LspCpp/LibLsp/lsp/utils.cpp new file mode 100644 index 0000000000..532f4bf6d7 --- /dev/null +++ b/graphics/asymptote/LspCpp/LibLsp/lsp/utils.cpp @@ -0,0 +1,602 @@ +#include "utils.h" + +#include <algorithm> +#include <cassert> +#include <cctype> +#include <cstring> +#include <fstream> +#include <functional> + +#include <queue> +#include <sstream> +#include <string> +#include <unordered_map> +#include <sys/stat.h> + +#include "lsPosition.h" +#include "utf8.h" +#ifdef _WIN32 +#include <Windows.h> +#endif + + +// DEFAULT_RESOURCE_DIRECTORY is passed with quotes for non-MSVC compilers, ie, +// foo vs "foo". +#if defined(_MSC_VER) +#define _STRINGIFY(x) #x +#define ENSURE_STRING_MACRO_ARGUMENT(x) _STRINGIFY(x) +#else +#define ENSURE_STRING_MACRO_ARGUMENT(x) x +#endif +#include <boost/filesystem/path.hpp> +#include <boost/filesystem/operations.hpp> +#include <boost/algorithm/string.hpp> +namespace lsp +{ + + +// See http://stackoverflow.com/a/2072890 +bool EndsWith(std::string value, std::string ending) { + if (ending.size() > value.size()) + return false; + return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); +} + +bool StartsWith(std::string value, std::string start) { + if (start.size() > value.size()) + return false; + return std::equal(start.begin(), start.end(), value.begin()); +} + +bool AnyStartsWith(const std::vector<std::string>& values, + const std::string& start) { + return std::any_of( + std::begin(values), std::end(values), + [&start](const std::string& value) { return StartsWith(value, start); }); +} + +bool StartsWithAny(const std::string& value, + const std::vector<std::string>& startings) { + return std::any_of(std::begin(startings), std::end(startings), + [&value](const std::string& starting) { + return StartsWith(value, starting); + }); +} + +bool EndsWithAny(const std::string& value, + const std::vector<std::string>& endings) { + return std::any_of( + std::begin(endings), std::end(endings), + [&value](const std::string& ending) { return EndsWith(value, ending); }); +} + +bool FindAnyPartial(const std::string& value, + const std::vector<std::string>& values) { + return std::any_of(std::begin(values), std::end(values), + [&value](const std::string& v) { + return value.find(v) != std::string::npos; + }); +} + +std::string GetDirName(std::string path) { + + ReplaceAll(path, "\\", "/"); + if (path.size() && path.back() == '/') + path.pop_back(); + size_t last_slash = path.find_last_of('/'); + if (last_slash == std::string::npos) + return "./"; + return path.substr(0, last_slash + 1); +} + +std::string GetBaseName(const std::string& path) { + size_t last_slash = path.find_last_of('/'); + if (last_slash != std::string::npos && (last_slash + 1) < path.size()) + return path.substr(last_slash + 1); + return path; +} + +std::string StripFileType(const std::string& path) { + size_t last_period = path.find_last_of('.'); + if (last_period != std::string::npos) + return path.substr(0, last_period); + return path; +} + +// See http://stackoverflow.com/a/29752943 +std::string ReplaceAll(const std::string& source, + const std::string& from, + const std::string& to) { + std::string result; + result.reserve(source.length()); // avoids a few memory allocations + + std::string::size_type last_pos = 0; + std::string::size_type find_pos; + + while (std::string::npos != (find_pos = source.find(from, last_pos))) { + result.append(source, last_pos, find_pos - last_pos); + result += to; + last_pos = find_pos + from.length(); + } + + // Care for the rest after last occurrence + result += source.substr(last_pos); + + return result; +} + +std::vector<std::string> SplitString(const std::string& str, + const std::string& delimiter) { + // http://stackoverflow.com/a/13172514 + std::vector<std::string> strings; + + std::string::size_type pos = 0; + std::string::size_type prev = 0; + while ((pos = str.find(delimiter, prev)) != std::string::npos) { + strings.emplace_back(str.substr(prev, pos - prev)); + prev = pos + 1; + } + + // To get the last substring (or only, if delimiter is not found) + strings.emplace_back(str.substr(prev)); + + return strings; +} + +void EnsureEndsInSlash(std::string& path) { + if (path.empty() || path[path.size() - 1] != '/') + path += '/'; +} + +std::string EscapeFileName(std::string path) { + if (path.size() && path.back() == '/') + path.pop_back(); + std::replace(path.begin(), path.end(), '\\', '@'); + std::replace(path.begin(), path.end(), '/', '@'); + std::replace(path.begin(), path.end(), ':', '@'); + return path; +} + +// http://stackoverflow.com/a/6089413 +std::istream& SafeGetline(std::istream& is, std::string& t) { + t.clear(); + + // The characters in the stream are read one-by-one using a std::streambuf. + // That is faster than reading them one-by-one using the std::istream. Code + // that uses streambuf this way must be guarded by a sentry object. The sentry + // object performs various tasks, such as thread synchronization and updating + // the stream state. + + std::istream::sentry se(is, true); + std::streambuf* sb = is.rdbuf(); + + for (;;) { + int c = sb->sbumpc(); + if (c == EOF) { + // Also handle the case when the last line has no line ending + if (t.empty()) + is.setstate(std::ios::eofbit); + return is; + } + + t += (char)c; + + if (c == '\n') + return is; + } +} + +bool FileExists(const std::string& filename) { + std::ifstream cache(filename); + return cache.is_open(); +} + +boost::optional<std::string> ReadContent(const AbsolutePath& filename) { + + std::ifstream cache; + cache.open(filename.path); + + try { + return std::string(std::istreambuf_iterator<char>(cache), + std::istreambuf_iterator<char>()); + } catch (std::ios_base::failure&) { + return {}; + } +} + +std::vector<std::string> ReadLinesWithEnding(const AbsolutePath& filename) { + std::vector<std::string> result; + + std::ifstream input(filename.path); + for (std::string line; SafeGetline(input, line);) + result.emplace_back(line); + + return result; +} + +bool WriteToFile(const std::string& filename, const std::string& content) { + std::ofstream file(filename, + std::ios::out | std::ios::trunc | std::ios::binary); + if (!file.good()) { + + return false; + } + + file << content; + return true; +} + + +std::string FormatMicroseconds(long long microseconds) { + long long milliseconds = microseconds / 1000; + long long remaining = microseconds - milliseconds; + + // Only show two digits after the dot. + while (remaining >= 100) + remaining /= 10; + + return std::to_string(milliseconds) + "." + std::to_string(remaining) + "ms"; +} + + + +std::string UpdateToRnNewlines(std::string output) { + size_t idx = 0; + while (true) { + idx = output.find('\n', idx); + + // No more matches. + if (idx == std::string::npos) + break; + + // Skip an existing "\r\n" match. + if (idx > 0 && output[idx - 1] == '\r') { + ++idx; + continue; + } + + // Replace "\n" with "\r|n". + output.replace(output.begin() + idx, output.begin() + idx + 1, "\r\n"); + } + + return output; +} + + + +bool IsAbsolutePath(const std::string& path) { + return IsUnixAbsolutePath(path) || IsWindowsAbsolutePath(path); +} + +bool IsUnixAbsolutePath(const std::string& path) { + return !path.empty() && path[0] == '/'; +} + +bool IsWindowsAbsolutePath(const std::string& path) { + auto is_drive_letter = [](char c) { + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); + }; + + return path.size() > 3 && path[1] == ':' && + (path[2] == '/' || path[2] == '\\') && is_drive_letter(path[0]); +} + +bool IsDirectory(const std::string& path) { + struct stat path_stat; + + if (stat(path.c_str(), &path_stat) != 0) { + perror("cannot access path"); + return false; + } + + return path_stat.st_mode & S_IFDIR; +} + + +std::string ws2s(std::wstring const& wstr) { + + std::string narrow; + utf8::utf32to8(wstr.begin(), wstr.end(), std::back_inserter(narrow)); + return narrow; +} +std::wstring s2ws(const std::string& str) { + std::wstring wide; + utf8::utf8to32(str.begin(), str.end(), std::back_inserter(wide)); + return wide; +} + + +#ifdef _WIN32 + +#else +// Returns the canonicalized absolute pathname, without expanding symbolic +// links. This is a variant of realpath(2), C++ rewrite of +// https://github.com/freebsd/freebsd/blob/master/lib/libc/stdlib/realpath.c +AbsolutePath RealPathNotExpandSymlink(std::string path, + bool ensure_exists) { + if (path.empty()) { + errno = EINVAL; + return {}; + } + if (path[0] == '\0') { + errno = ENOENT; + return {}; + } + + // Do not use PATH_MAX because it is tricky on Linux. + // See https://eklitzke.org/path-max-is-tricky + char tmp[1024]; + std::string resolved; + size_t i = 0; + struct stat sb; + if (path[0] == '/') { + resolved = "/"; + i = 1; + } + else { + if (!getcwd(tmp, sizeof tmp) && ensure_exists) + return {}; + resolved = tmp; + } + + while (i < path.size()) { + auto j = path.find('/', i); + if (j == std::string::npos) + j = path.size(); + auto next_token = path.substr(i, j - i); + i = j + 1; + if (resolved.back() != '/') + resolved += '/'; + if (next_token.empty() || next_token == ".") { + // Handle consequential slashes and "." + continue; + } + else if (next_token == "..") { + // Strip the last path component except when it is single "/" + if (resolved.size() > 1) + resolved.resize(resolved.rfind('/', resolved.size() - 2) + 1); + continue; + } + // Append the next path component. + // Here we differ from realpath(3), we use stat(2) instead of + // lstat(2) because we do not want to resolve symlinks. + resolved += next_token; + if (stat(resolved.c_str(), &sb) != 0 && ensure_exists) + return {}; + if (!S_ISDIR(sb.st_mode) && j < path.size() && ensure_exists) { + errno = ENOTDIR; + return {}; + } + } + + // Remove trailing slash except when a single "/". + if (resolved.size() > 1 && resolved.back() == '/') + resolved.pop_back(); + return AbsolutePath(resolved, true /*validate*/); +} +#endif + + +AbsolutePath NormalizePath(const std::string& path0, + bool ensure_exists , + bool force_lower_on_windows) { +#ifdef _WIN32 + + std::wstring path = lsp::s2ws(path0); + + wchar_t buffer[MAX_PATH] = (L""); + + // Normalize the path name, ie, resolve `..`. + unsigned long len = GetFullPathNameW(path.c_str(), MAX_PATH, buffer, nullptr); + if (!len) + return {}; + path = std::wstring(buffer, len); + + // Get the actual casing of the path, ie, if the file on disk is `C:\FooBar` + // and this function is called with `c:\fooBar` this will return `c:\FooBar`. + // (drive casing is lowercase). + if (ensure_exists) { + len = GetLongPathNameW(path.c_str(), buffer, MAX_PATH); + if (!len) + return {}; + path = std::wstring(buffer, len); + } + + // Empty paths have no meaning. + if (path.empty()) + return {}; + + // We may need to normalize the drive name to upper-case; at the moment + // vscode sends lower-case path names. + /* + path[0] = toupper(path[0]); + */ + // Make the path all lower-case, since windows is case-insensitive. + if (force_lower_on_windows) { + for (size_t i = 0; i < path.size(); ++i) + path[i] = (wchar_t)tolower(path[i]); + } + + // cquery assumes forward-slashes. + std::replace(path.begin(), path.end(), '\\', '/'); + + + return AbsolutePath(lsp::ws2s(path), false /*validate*/); +#else + + return RealPathNotExpandSymlink(path0, ensure_exists); + +#endif + + +} + +// VSCode (UTF-16) disagrees with Emacs lsp-mode (UTF-8) on how to represent +// text documents. +// We use a UTF-8 iterator to approximate UTF-16 in the specification (weird). +// This is good enough and fails only for UTF-16 surrogate pairs. +int GetOffsetForPosition(lsPosition position, const std::string& content) { + size_t i = 0; + // Iterate lines until we have found the correct line. + while (position.line > 0 && i < content.size()) { + if (content[i] == '\n') + position.line--; + i++; + } + // Iterate characters on the target line. + while (position.character > 0 && i < content.size()) { + if (uint8_t(content[i++]) >= 128) { + // Skip 0b10xxxxxx + while (i < content.size() && uint8_t(content[i]) >= 128 && + uint8_t(content[i]) < 192) + i++; + } + position.character--; + } + return int(i); +} + + +lsPosition GetPositionForOffset(size_t offset,const std::string& content) { + lsPosition result; + for (size_t i = 0; i < offset && i < content.length(); ++i) { + if (content[i] == '\n') { + result.line++; + result.character = 0; + } + else { + result.character++; + } + } + return result; +} + +lsPosition CharPos(const std::string& search, + char character, + int character_offset) { + lsPosition result; + size_t index = 0; + while (index < search.size()) { + char c = search[index]; + if (c == character) + break; + if (c == '\n') { + result.line += 1; + result.character = 0; + } + else { + result.character += 1; + } + ++index; + } + assert(index < search.size()); + result.character += character_offset; + return result; +} + +void scanDirsUseRecursive(const std::wstring& rootPath, std::vector<std::wstring>& ret) +{ + namespace fs = boost::filesystem; + fs::path fullpath(rootPath); + if (!fs::exists(fullpath)) { return; } + fs::recursive_directory_iterator end_iter; + for (fs::recursive_directory_iterator iter(fullpath); iter != end_iter; iter++) { + try { + if (fs::is_directory(*iter)) { + ret.push_back(iter->path().wstring()); + } + } + catch (const std::exception& ex) { + continue; + } + } +} + +void scanDirsNoRecursive(const std::wstring& rootPath, std::vector<std::wstring>& ret) +{ + namespace fs = boost::filesystem; + boost::filesystem::path myPath(rootPath); + if (!fs::exists(rootPath)) { return; } + boost::filesystem::directory_iterator endIter; + for (boost::filesystem::directory_iterator iter(myPath); iter != endIter; iter++) { + if (boost::filesystem::is_directory(*iter)) { + ret.push_back(iter->path().wstring()); + } + } +} + +void scanFilesUseRecursive( + const std::wstring& rootPath, + std::vector<std::wstring>& ret, + std::wstring suf) { + namespace fs = boost::filesystem; + boost::to_lower(suf); + + fs::path fullpath(rootPath); + if (!fs::exists(fullpath)) { return; } + fs::recursive_directory_iterator end_iter; + for (fs::recursive_directory_iterator iter(fullpath); iter != end_iter; iter++) { + try { + if (!fs::is_directory(*iter) && fs::is_regular_file(*iter)) { + auto temp_path = iter->path().wstring(); + auto size = suf.size(); + if (!size) + { + ret.push_back(std::move(temp_path)); + } + else + { + + if (temp_path.size() < size) continue; + auto suf_temp = temp_path.substr(temp_path.size() - size); + boost::to_lower(suf_temp); + if (suf_temp == suf) + { + ret.push_back(std::move(temp_path)); + } + } + } + } + catch (const std::exception&) { + continue; + } + } +} + +void scanFileNamesUseRecursive(const std::wstring& rootPath, std::vector<std::wstring>& ret, + std::wstring strSuf) +{ + scanFilesUseRecursive(rootPath, ret, strSuf); + std::vector<std::wstring> names; + for (auto& it : ret) + { + if (it.size() >= rootPath.size()) + { + names.push_back(it.substr(rootPath.size())); + } + } + ret.swap(names); +} + +void scanFileNamesUseRecursive(const std::string& rootPath, std::vector<std::string>& ret, std::string strSuf) +{ + std::vector<std::wstring> out; + scanFileNamesUseRecursive(s2ws(rootPath), out, s2ws(strSuf)); + for (auto& it : out) + { + ret.push_back(ws2s(it)); + } +} + +void scanFilesUseRecursive(const std::string& rootPath, std::vector<std::string>& ret, std::string strSuf) +{ + std::vector<std::wstring> out; + scanFilesUseRecursive(s2ws(rootPath), out, s2ws(strSuf)); + for (auto& it : out) + { + ret.push_back(ws2s(it)); + } +} + + +} |