summaryrefslogtreecommitdiff
path: root/elpa/irony-20220110.849/server/src/Irony.cpp
diff options
context:
space:
mode:
authormattkae <mattkae@protonmail.com>2022-05-11 09:23:58 -0400
committermattkae <mattkae@protonmail.com>2022-05-11 09:23:58 -0400
commit3f4a0d5370ae6c34afe180df96add3b8522f4af1 (patch)
treeae901409e02bde8ee278475f8cf6818f8f680a60 /elpa/irony-20220110.849/server/src/Irony.cpp
initial commit
Diffstat (limited to 'elpa/irony-20220110.849/server/src/Irony.cpp')
-rw-r--r--elpa/irony-20220110.849/server/src/Irony.cpp638
1 files changed, 638 insertions, 0 deletions
diff --git a/elpa/irony-20220110.849/server/src/Irony.cpp b/elpa/irony-20220110.849/server/src/Irony.cpp
new file mode 100644
index 0000000..2157b32
--- /dev/null
+++ b/elpa/irony-20220110.849/server/src/Irony.cpp
@@ -0,0 +1,638 @@
+/**
+ * \file
+ * \author Guillaume Papin <guillaume.papin@epitech.eu>
+ *
+ * \brief irony-server "API" definitions.
+ *
+ * \sa Irony.h for more information.
+ *
+ * This file is distributed under the GNU General Public License. See
+ * COPYING for details.
+ */
+
+#include "Irony.h"
+
+#include "support/iomanip_quoted.h"
+
+#include <algorithm>
+#include <cassert>
+#include <iostream>
+#include <fstream>
+#include <cctype>
+
+namespace {
+
+std::string cxStringToStd(CXString cxString) {
+ std::string stdStr;
+
+ if (const char *cstr = clang_getCString(cxString)) {
+ stdStr = cstr;
+ }
+
+ clang_disposeString(cxString);
+ return stdStr;
+}
+
+const char *diagnosticSeverity(const CXDiagnostic &diagnostic) {
+ switch (clang_getDiagnosticSeverity(diagnostic)) {
+ case CXDiagnostic_Ignored:
+ return "ignored";
+ case CXDiagnostic_Note:
+ return "note";
+ case CXDiagnostic_Warning:
+ return "warning";
+ case CXDiagnostic_Error:
+ return "error";
+ case CXDiagnostic_Fatal:
+ return "fatal";
+ }
+
+ return "unknown";
+}
+
+void dumpDiagnostic(const CXDiagnostic &diagnostic) {
+ std::string file;
+ unsigned line = 0, column = 0, offset = 0;
+ CXSourceLocation location = clang_getDiagnosticLocation(diagnostic);
+ if (!clang_equalLocations(location, clang_getNullLocation())) {
+ CXFile cxFile;
+
+// clang_getInstantiationLocation() has been marked deprecated and
+// is aimed to be replaced by clang_getExpansionLocation().
+#if CINDEX_VERSION >= 6
+ clang_getExpansionLocation(location, &cxFile, &line, &column, &offset);
+#else
+ clang_getInstantiationLocation(location, &cxFile, &line, &column, &offset);
+#endif
+
+ file = cxStringToStd(clang_getFileName(cxFile));
+ }
+
+ const char *severity = diagnosticSeverity(diagnostic);
+
+ std::string message = cxStringToStd(clang_getDiagnosticSpelling(diagnostic));
+
+ std::cout << '(' << support::quoted(file) //
+ << ' ' << line //
+ << ' ' << column //
+ << ' ' << offset //
+ << ' ' << severity //
+ << ' ' << support::quoted(message) //
+ << ")\n";
+}
+
+bool readFileContent(const std::string &filename,
+ Irony::UnsavedBuffer &outBuf) {
+ std::ifstream ifs(filename.c_str(),
+ std::ios::in | std::ios::binary | std::ios::ate);
+
+ if (!ifs.is_open()) {
+ return false;
+ }
+
+ // FIXME: it's possible that this method of reading the file is 100% reliable,
+ // I can't confirm that tellg() is guaranteed to return a byte count.
+ // std::streamoff does not mention 'byte'.
+ // In practice it seems to work but this may be just luck.
+ // See also this discussion:
+ // - http://stackoverflow.com/questions/22984956/tellg-function-give-wrong-size-of-file/22986486#22986486
+ auto nbytes = ifs.tellg();
+
+ if (nbytes == std::ifstream::pos_type(-1)) {
+ return false;
+ }
+
+ outBuf.resize(nbytes);
+ ifs.seekg(0, std::ios::beg);
+
+ ifs.read(&outBuf[0], outBuf.size());
+
+ if (!ifs){
+ outBuf.clear();
+ return false;
+ }
+
+ return true;
+}
+
+} // unnamed namespace
+
+Irony::Irony()
+ : activeTu_(nullptr), activeCompletionResults_(nullptr), debug_(false) {
+}
+
+void Irony::parse(const std::string &file,
+ const std::vector<std::string> &flags) {
+ resetCache();
+ activeTu_ = tuManager_.parse(file, flags, cxUnsavedFiles_);
+ file_ = file;
+
+ if (activeTu_ == nullptr) {
+ std::cout << "(error . ("
+ << "parse-error"
+ << " \"failed to parse file\""
+ << " " << support::quoted(file) << "))\n";
+ return;
+ }
+
+ std::cout << "(success . t)\n";
+}
+
+void Irony::diagnostics() const {
+ unsigned diagnosticCount;
+
+ if (activeTu_ == nullptr) {
+ diagnosticCount = 0;
+ } else {
+ diagnosticCount = clang_getNumDiagnostics(activeTu_);
+ }
+
+ std::cout << "(\n";
+
+ for (unsigned i = 0; i < diagnosticCount; ++i) {
+ CXDiagnostic diagnostic = clang_getDiagnostic(activeTu_, i);
+
+ dumpDiagnostic(diagnostic);
+
+ clang_disposeDiagnostic(diagnostic);
+ }
+
+ std::cout << ")\n";
+}
+
+void Irony::resetCache() {
+ activeTu_ = nullptr;
+
+ if (activeCompletionResults_ != nullptr) {
+ clang_disposeCodeCompleteResults(activeCompletionResults_);
+ activeCompletionResults_ = nullptr;
+ }
+}
+
+void Irony::getType(unsigned line, unsigned col) const {
+ if (activeTu_ == nullptr) {
+ std::clog << "W: get-type - parse wasn't called\n";
+
+ std::cout << "nil\n";
+ return;
+ }
+
+ CXFile cxFile = clang_getFile(activeTu_, file_.c_str());
+ CXSourceLocation sourceLoc = clang_getLocation(activeTu_, cxFile, line, col);
+ CXCursor cursor = clang_getCursor(activeTu_, sourceLoc);
+
+ if (clang_Cursor_isNull(cursor)) {
+ // TODO: "error: no type at point"?
+ std::cout << "nil";
+ return;
+ }
+
+ CXType cxTypes[2];
+ cxTypes[0] = clang_getCursorType(cursor);
+ cxTypes[1] = clang_getCanonicalType(cxTypes[0]);
+
+ std::cout << "(";
+
+ for (const CXType &cxType : cxTypes) {
+ CXString typeDescr = clang_getTypeSpelling(cxType);
+ std::string typeStr = clang_getCString(typeDescr);
+ clang_disposeString(typeDescr);
+
+ if (typeStr.empty())
+ break;
+
+ std::cout << support::quoted(typeStr) << " ";
+ }
+
+ std::cout << ")\n";
+}
+
+namespace {
+
+class CompletionChunk {
+public:
+ explicit CompletionChunk(CXCompletionString completionString)
+ : completionString_(completionString)
+ , numChunks_(clang_getNumCompletionChunks(completionString_))
+ , chunkIdx_(0) {
+ }
+
+ bool hasNext() const {
+ return chunkIdx_ < numChunks_;
+ }
+
+ void next() {
+ if (!hasNext()) {
+ assert(0 && "out of range completion chunk");
+ abort();
+ }
+
+ ++chunkIdx_;
+ }
+
+ CXCompletionChunkKind kind() const {
+ return clang_getCompletionChunkKind(completionString_, chunkIdx_);
+ }
+
+ // TODO: operator>> so that one can re-use string allocated buffer
+ std::string text() const {
+ return cxStringToStd(
+ clang_getCompletionChunkText(completionString_, chunkIdx_));
+ }
+
+private:
+ CXCompletionString completionString_;
+ unsigned int numChunks_;
+ unsigned chunkIdx_;
+};
+
+} // unnamed namespace
+
+void Irony::complete(const std::string &file,
+ unsigned line,
+ unsigned col,
+ const std::vector<std::string> &flags) {
+ resetCache();
+
+ if (CXTranslationUnit tu =
+ tuManager_.getOrCreateTU(file, flags, cxUnsavedFiles_)) {
+ activeCompletionResults_ =
+ clang_codeCompleteAt(tu,
+ file.c_str(),
+ line,
+ col,
+ const_cast<CXUnsavedFile *>(cxUnsavedFiles_.data()),
+ cxUnsavedFiles_.size(),
+ (clang_defaultCodeCompleteOptions() &
+ ~CXCodeComplete_IncludeCodePatterns)
+#if HAS_BRIEF_COMMENTS_IN_COMPLETION
+ |
+ CXCodeComplete_IncludeBriefComments
+#endif
+ );
+ }
+
+ if (activeCompletionResults_ == nullptr) {
+ std::cout << "(error . ("
+ << "complete-error"
+ << " \"failed to perform code completion\""
+ << " " << support::quoted(file) << " " << line << " " << col
+ << "))\n";
+ return;
+ }
+
+ clang_sortCodeCompletionResults(activeCompletionResults_->Results,
+ activeCompletionResults_->NumResults);
+
+ std::cout << "(success . t)\n";
+}
+
+namespace {
+
+bool hasUppercase(const std::string &prefix)
+{
+ for (char c : prefix) {
+ if (std::isupper(c)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool isEqual(const bool insensitive, const char a, const char b)
+{
+ if (insensitive) {
+ return std::tolower(a) == std::tolower(b);
+ } else {
+ return a == b;
+ }
+}
+
+bool startsWith(const std::string& str, const std::string &prefix, bool caseInsensitive)
+{
+ if (str.length() < prefix.length()) {
+ return false;
+ }
+
+ const auto charCmp = [&](const char a, const char b) {
+ return isEqual(caseInsensitive, a, b);
+ };
+
+ auto res = std::mismatch(prefix.begin(), prefix.end(), str.begin(), charCmp);
+ return res.first == prefix.end();
+}
+
+bool isStyleCaseInsensitive(const std::string &prefix, PrefixMatchStyle style)
+{
+ if (style == PrefixMatchStyle::SmartCase) {
+ // For SmartCase style, do case insensitive matching only there isn't upper
+ // case letter.
+ if (!hasUppercase(prefix)) {
+ style = PrefixMatchStyle::CaseInsensitive;
+ }
+ }
+ return style == PrefixMatchStyle::CaseInsensitive;
+}
+
+} // unnamed namespace
+
+void Irony::completionDiagnostics() const {
+ unsigned diagnosticCount;
+
+ if (activeCompletionResults_ == nullptr) {
+ diagnosticCount = 0;
+ } else {
+ diagnosticCount =
+ clang_codeCompleteGetNumDiagnostics(activeCompletionResults_);
+ }
+
+ std::cout << "(\n";
+
+ for (unsigned i = 0; i < diagnosticCount; ++i) {
+ CXDiagnostic diagnostic =
+ clang_codeCompleteGetDiagnostic(activeCompletionResults_, i);
+
+ dumpDiagnostic(diagnostic);
+ clang_disposeDiagnostic(diagnostic);
+ }
+
+ std::cout << ")\n";
+}
+
+void Irony::candidates(const std::string &prefix, PrefixMatchStyle style) const {
+ if (activeCompletionResults_ == nullptr) {
+ std::cout << "nil\n";
+ return;
+ }
+
+ bool caseInsensitive = isStyleCaseInsensitive(prefix, style);
+
+ CXCodeCompleteResults *completions = activeCompletionResults_;
+
+ std::cout << "(\n";
+
+ // re-use the same buffers to avoid unnecessary allocations
+ std::string typedtext, brief, resultType, prototype, postCompCar;
+
+ std::vector<unsigned> postCompCdr;
+
+ for (unsigned i = 0; i < completions->NumResults; ++i) {
+ CXCompletionResult candidate = completions->Results[i];
+ CXAvailabilityKind availability =
+ clang_getCompletionAvailability(candidate.CompletionString);
+
+ unsigned priority =
+ clang_getCompletionPriority(candidate.CompletionString);
+ unsigned annotationStart = 0;
+ bool typedTextSet = false;
+ bool hasPrefix = true;
+
+
+ if (availability == CXAvailability_NotAccessible ||
+ availability == CXAvailability_NotAvailable) {
+ continue;
+ }
+
+ typedtext.clear();
+ brief.clear();
+ resultType.clear();
+ prototype.clear();
+ postCompCar.clear();
+ postCompCdr.clear();
+
+ for (CompletionChunk chunk(candidate.CompletionString); chunk.hasNext();
+ chunk.next()) {
+ char ch = 0;
+
+ auto chunkKind = chunk.kind();
+
+ switch (chunkKind) {
+ case CXCompletionChunk_ResultType:
+ resultType = chunk.text();
+ break;
+
+ case CXCompletionChunk_TypedText:
+ case CXCompletionChunk_Text:
+ case CXCompletionChunk_Placeholder:
+ case CXCompletionChunk_Informative:
+ case CXCompletionChunk_CurrentParameter:
+ prototype += chunk.text();
+ break;
+
+ case CXCompletionChunk_LeftParen: ch = '('; break;
+ case CXCompletionChunk_RightParen: ch = ')'; break;
+ case CXCompletionChunk_LeftBracket: ch = '['; break;
+ case CXCompletionChunk_RightBracket: ch = ']'; break;
+ case CXCompletionChunk_LeftBrace: ch = '{'; break;
+ case CXCompletionChunk_RightBrace: ch = '}'; break;
+ case CXCompletionChunk_LeftAngle: ch = '<'; break;
+ case CXCompletionChunk_RightAngle: ch = '>'; break;
+ case CXCompletionChunk_Comma: ch = ','; break;
+ case CXCompletionChunk_Colon: ch = ':'; break;
+ case CXCompletionChunk_SemiColon: ch = ';'; break;
+ case CXCompletionChunk_Equal: ch = '='; break;
+ case CXCompletionChunk_HorizontalSpace: ch = ' '; break;
+ case CXCompletionChunk_VerticalSpace: ch = '\n'; break;
+
+ case CXCompletionChunk_Optional:
+ // ignored for now
+ break;
+ }
+
+ if (ch != 0) {
+ prototype += ch;
+ // commas look better followed by a space
+ if (ch == ',') {
+ prototype += ' ';
+ }
+ }
+
+ if (typedTextSet) {
+ if (ch != 0) {
+ postCompCar += ch;
+ if (ch == ',') {
+ postCompCar += ' ';
+ }
+ } else if (chunkKind == CXCompletionChunk_Text ||
+ chunkKind == CXCompletionChunk_TypedText) {
+ postCompCar += chunk.text();
+ } else if (chunkKind == CXCompletionChunk_Placeholder ||
+ chunkKind == CXCompletionChunk_CurrentParameter) {
+ postCompCdr.push_back(postCompCar.size());
+ postCompCar += chunk.text();
+ postCompCdr.push_back(postCompCar.size());
+ }
+ }
+
+ // Consider only the first typed text. The CXCompletionChunk_TypedText
+ // doc suggests that exactly one typed text will be given but at least
+ // in Objective-C it seems that more than one can appear, see:
+ // https://github.com/Sarcasm/irony-mode/pull/78#issuecomment-37115538
+ if (chunkKind == CXCompletionChunk_TypedText && !typedTextSet) {
+ typedtext = chunk.text();
+ if (!startsWith(typedtext, prefix, caseInsensitive)) {
+ hasPrefix = false;
+ break;
+ }
+ // annotation is what comes after the typedtext
+ annotationStart = prototype.size();
+ typedTextSet = true;
+ }
+ }
+
+ if (!hasPrefix) {
+ continue;
+ }
+ if (!typedTextSet) {
+ // clang may generate candidates without any typedText, and we may
+ // generate some output like:
+ // ("" 1 "bool" "" "hasUppercase(const std::string &prefix)"
+ // 0 ("") available)
+ // That will cause infinite completion in irony.el
+ continue;
+ }
+
+#if HAS_BRIEF_COMMENTS_IN_COMPLETION
+ brief = cxStringToStd(
+ clang_getCompletionBriefComment(candidate.CompletionString));
+#endif
+
+ // see irony-completion.el#irony-completion-candidates
+ std::cout << '(' << support::quoted(typedtext)
+ << ' ' << priority
+ << ' ' << support::quoted(resultType)
+ << ' ' << support::quoted(brief)
+ << ' ' << support::quoted(prototype)
+ << ' ' << annotationStart
+ << " (" << support::quoted(postCompCar);
+ for (unsigned index : postCompCdr)
+ std::cout << ' ' << index;
+ std::cout << ")"
+ << ")\n";
+ }
+
+ std::cout << ")\n";
+}
+
+void Irony::computeCxUnsaved() {
+ cxUnsavedFiles_.clear();
+
+ for (const auto &p : filenameToContent_) {
+ CXUnsavedFile cxUnsavedFile;
+
+ cxUnsavedFile.Filename = p.first.c_str();
+ cxUnsavedFile.Contents = p.second.data();
+ cxUnsavedFile.Length = p.second.size();
+ cxUnsavedFiles_.push_back(cxUnsavedFile);
+ }
+}
+
+void Irony::setUnsaved(const std::string &file,
+ const std::string &unsavedContentFile) {
+ resetCache();
+
+ UnsavedBuffer content;
+ if (!readFileContent(unsavedContentFile, content)) {
+ filenameToContent_.erase(file);
+ std::cout << "(error . ("
+ << "file-read-error"
+ << " \"failed to read unsaved buffer\""
+ << " " << support::quoted(file) << " "
+ << support::quoted(unsavedContentFile) << ")\n";
+ } else {
+ filenameToContent_[file] = content;
+ std::cout << "(success . t)\n";
+ }
+
+ computeCxUnsaved();
+}
+
+void Irony::resetUnsaved(const std::string &file) {
+ resetCache();
+
+ const auto erasedCount = filenameToContent_.erase(file);
+
+ if (erasedCount == 0) {
+ std::cout << "(error . ("
+ << "no-such-entry"
+ << " \"failed reset unsaved buffer\""
+ << " " << support::quoted(file) << ")\n";
+ } else {
+ std::cout << "(success . t)\n";
+ }
+
+ computeCxUnsaved();
+}
+
+void Irony::getCompileOptions(const std::string &buildDir,
+ const std::string &file) const {
+#if !(HAS_COMPILATION_DATABASE)
+
+ (void)buildDir;
+ (void)file;
+
+ CXString cxVersionString = clang_getClangVersion();
+
+ std::cout << "(error . ("
+ << "unsupported"
+ << " \"compilation database requires Clang >= 3.2\""
+ << " " << support::quoted(clang_getCString(cxVersionString))
+ << "))\n";
+
+ clang_disposeString(cxVersionString);
+
+ return;
+
+#else
+ CXCompilationDatabase_Error error;
+ CXCompilationDatabase db =
+ compDBCache_.fromDirectory(buildDir.c_str(), &error);
+
+ switch (error) {
+ case CXCompilationDatabase_CanNotLoadDatabase:
+ std::cout << "(error . ("
+ << "cannot-load-database"
+ << " \"failed to load compilation database from directory\""
+ << " " << support::quoted(buildDir) << "))\n";
+ return;
+
+ case CXCompilationDatabase_NoError:
+ break;
+ }
+
+ CXCompileCommands compileCommands =
+ clang_CompilationDatabase_getCompileCommands(db, file.c_str());
+
+ std::cout << "(success . (\n";
+
+ for (unsigned i = 0, numCompileCommands =
+ clang_CompileCommands_getSize(compileCommands);
+ i < numCompileCommands; ++i) {
+ CXCompileCommand compileCommand =
+ clang_CompileCommands_getCommand(compileCommands, i);
+
+ std::cout << "("
+ << "(";
+ for (unsigned j = 0,
+ numArgs = clang_CompileCommand_getNumArgs(compileCommand);
+ j < numArgs; ++j) {
+ CXString arg = clang_CompileCommand_getArg(compileCommand, j);
+ std::cout << support::quoted(clang_getCString(arg)) << " ";
+ clang_disposeString(arg);
+ }
+
+ std::cout << ")"
+ << " . ";
+
+ CXString directory = clang_CompileCommand_getDirectory(compileCommand);
+ std::cout << support::quoted(clang_getCString(directory));
+ clang_disposeString(directory);
+
+ std::cout << ")\n";
+ }
+
+ std::cout << "))\n";
+
+ clang_CompileCommands_dispose(compileCommands);
+#endif
+}