Initial commit.
This commit is contained in:
70
.emacs.d/elpa/irony-20160203.1207/server/src/CMakeLists.txt
Normal file
70
.emacs.d/elpa/irony-20160203.1207/server/src/CMakeLists.txt
Normal file
@@ -0,0 +1,70 @@
|
||||
include(CheckLibClangBuiltinHeadersDir)
|
||||
|
||||
find_package(LibClang REQUIRED)
|
||||
|
||||
include_directories(${LIBCLANG_INCLUDE_DIRS})
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
check_libclang_builtin_headers_dir()
|
||||
|
||||
if (LIBCLANG_BUILTIN_HEADERS_DIR)
|
||||
# look for CLANG_BUILTIN_HEADERS_DIR usage in the code for an explanation
|
||||
add_definitions(-DCLANG_BUILTIN_HEADERS_DIR=\"${LIBCLANG_BUILTIN_HEADERS_DIR}\")
|
||||
endif()
|
||||
|
||||
# not to be taken as a module-definition file to link on Windows
|
||||
set_source_files_properties(Commands.def PROPERTIES HEADER_FILE_ONLY TRUE)
|
||||
|
||||
add_executable(irony-server
|
||||
support/arraysize.h
|
||||
support/CommandLineParser.cpp
|
||||
support/CommandLineParser.h
|
||||
support/iomanip_quoted.h
|
||||
support/NonCopyable.h
|
||||
support/CIndex.h
|
||||
support/TemporaryFile.cpp
|
||||
support/TemporaryFile.h
|
||||
|
||||
Command.cpp
|
||||
Commands.def
|
||||
Command.h
|
||||
Irony.cpp
|
||||
Irony.h
|
||||
TUManager.cpp
|
||||
TUManager.h
|
||||
|
||||
main.cpp)
|
||||
|
||||
# retrieve the package version from irony.el
|
||||
function(irony_find_package_version OUTPUT_VAR)
|
||||
# this is a hack that force CMake to reconfigure, it is necessary to see if
|
||||
# the version in irony.el has changed, this is not possible to add the
|
||||
# definitions at build time
|
||||
configure_file(${PROJECT_SOURCE_DIR}/../irony.el
|
||||
${CMAKE_CURRENT_BINARY_DIR}/irony.el
|
||||
COPYONLY)
|
||||
|
||||
set(version_header "\;\; Version: ")
|
||||
file(STRINGS ${CMAKE_CURRENT_BINARY_DIR}/irony.el version
|
||||
LIMIT_COUNT 1
|
||||
REGEX "^${version_header}*")
|
||||
|
||||
if (NOT version)
|
||||
message (FATAL_ERROR "couldn't find irony.el's version header!")
|
||||
endif()
|
||||
|
||||
string(LENGTH ${version_header} version_header_length)
|
||||
string(SUBSTRING ${version} ${version_header_length} -1 package_version)
|
||||
set(${OUTPUT_VAR} ${package_version} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
irony_find_package_version(IRONY_PACKAGE_VERSION)
|
||||
message(STATUS "Irony package version is '${IRONY_PACKAGE_VERSION}'")
|
||||
|
||||
set_source_files_properties(main.cpp
|
||||
PROPERTIES
|
||||
COMPILE_DEFINITIONS IRONY_PACKAGE_VERSION=\"${IRONY_PACKAGE_VERSION}\")
|
||||
|
||||
target_link_libraries(irony-server ${LIBCLANG_LIBRARIES})
|
||||
|
||||
install(TARGETS irony-server DESTINATION bin)
|
288
.emacs.d/elpa/irony-20160203.1207/server/src/Command.cpp
Normal file
288
.emacs.d/elpa/irony-20160203.1207/server/src/Command.cpp
Normal file
@@ -0,0 +1,288 @@
|
||||
/**
|
||||
* \file
|
||||
* \author Guillaume Papin <guillaume.papin@epitech.eu>
|
||||
*
|
||||
* \brief Command parser definitions.
|
||||
*
|
||||
* This file is distributed under the GNU General Public License. See
|
||||
* COPYING for details.
|
||||
*/
|
||||
|
||||
#include "Command.h"
|
||||
|
||||
#include "support/CommandLineParser.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
|
||||
namespace {
|
||||
|
||||
struct StringConverter {
|
||||
StringConverter(std::string *dest) : dest_(dest) {
|
||||
}
|
||||
|
||||
bool operator()(const std::string &str) {
|
||||
*dest_ = str;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string *dest_;
|
||||
};
|
||||
|
||||
struct UnsignedIntConverter {
|
||||
UnsignedIntConverter(unsigned *dest) : dest_(dest) {
|
||||
}
|
||||
|
||||
bool operator()(const std::string &str) {
|
||||
char *end;
|
||||
long num = std::strtol(str.c_str(), &end, 10);
|
||||
|
||||
if (end != (str.c_str() + str.size()))
|
||||
return false;
|
||||
|
||||
if (errno == ERANGE)
|
||||
return false;
|
||||
|
||||
if (num < 0)
|
||||
return false;
|
||||
|
||||
unsigned long unum = static_cast<unsigned long>(num);
|
||||
if (unum > std::numeric_limits<unsigned>::max())
|
||||
return false;
|
||||
|
||||
*dest_ = unum;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned *dest_;
|
||||
};
|
||||
|
||||
/// Convert "on" and "off" to a boolean
|
||||
struct OptionConverter {
|
||||
OptionConverter(bool *dest) : dest_(dest) {
|
||||
}
|
||||
|
||||
bool operator()(const std::string &str) {
|
||||
if (str == "on") {
|
||||
*dest_ = true;
|
||||
} else if (str == "off") {
|
||||
*dest_ = false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool *dest_;
|
||||
};
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, const Command::Action &action) {
|
||||
os << "Command::";
|
||||
|
||||
switch (action) {
|
||||
#define X(sym, str, help) \
|
||||
case Command::sym: \
|
||||
os << #sym; \
|
||||
break;
|
||||
#include "Commands.def"
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, const Command &command) {
|
||||
os << "Command{action=" << command.action << ", "
|
||||
<< "file='" << command.file << "', "
|
||||
<< "dir='" << command.dir << "', "
|
||||
<< "line=" << command.line << ", "
|
||||
<< "column=" << command.column << ", "
|
||||
<< "flags=[";
|
||||
bool first = true;
|
||||
for (const std::string &flag : command.flags) {
|
||||
if (!first)
|
||||
os << ", ";
|
||||
os << "'" << flag << "'";
|
||||
first = false;
|
||||
}
|
||||
os << "], "
|
||||
<< "unsavedFiles.count=" << command.unsavedFiles.size() << ", "
|
||||
<< "opt=" << (command.opt ? "on" : "off");
|
||||
|
||||
return os << "}";
|
||||
}
|
||||
|
||||
static Command::Action actionFromString(const std::string &actionStr) {
|
||||
#define X(sym, str, help) \
|
||||
if (actionStr == str) \
|
||||
return Command::sym;
|
||||
|
||||
#include "Commands.def"
|
||||
|
||||
return Command::Unknown;
|
||||
}
|
||||
|
||||
CommandParser::CommandParser() : tempFile_("irony-server") {
|
||||
}
|
||||
|
||||
Command *CommandParser::parse(const std::vector<std::string> &argv) {
|
||||
command_.clear();
|
||||
|
||||
if (argv.begin() == argv.end()) {
|
||||
std::clog << "error: no command specified.\n"
|
||||
"See 'irony-server help' to list available commands\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
const std::string &actionStr = argv[0];
|
||||
|
||||
command_.action = actionFromString(actionStr);
|
||||
|
||||
bool handleUnsaved = false;
|
||||
bool readCompileOptions = false;
|
||||
std::vector<std::function<bool(const std::string &)>> positionalArgs;
|
||||
|
||||
switch (command_.action) {
|
||||
case Command::SetDebug:
|
||||
positionalArgs.push_back(OptionConverter(&command_.opt));
|
||||
break;
|
||||
|
||||
case Command::Parse:
|
||||
positionalArgs.push_back(StringConverter(&command_.file));
|
||||
handleUnsaved = true;
|
||||
readCompileOptions = true;
|
||||
break;
|
||||
|
||||
case Command::Complete:
|
||||
positionalArgs.push_back(StringConverter(&command_.file));
|
||||
positionalArgs.push_back(UnsignedIntConverter(&command_.line));
|
||||
positionalArgs.push_back(UnsignedIntConverter(&command_.column));
|
||||
handleUnsaved = true;
|
||||
readCompileOptions = true;
|
||||
break;
|
||||
|
||||
case Command::GetType:
|
||||
positionalArgs.push_back(UnsignedIntConverter(&command_.line));
|
||||
positionalArgs.push_back(UnsignedIntConverter(&command_.column));
|
||||
break;
|
||||
|
||||
case Command::Diagnostics:
|
||||
case Command::Help:
|
||||
case Command::Exit:
|
||||
// no-arguments commands
|
||||
break;
|
||||
|
||||
case Command::GetCompileOptions:
|
||||
positionalArgs.push_back(StringConverter(&command_.dir));
|
||||
positionalArgs.push_back(StringConverter(&command_.file));
|
||||
break;
|
||||
|
||||
case Command::Unknown:
|
||||
std::clog << "error: invalid command specified: " << actionStr << "\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto argIt = argv.begin() + 1;
|
||||
int argCount = std::distance(argIt, argv.end());
|
||||
|
||||
// parse optional arguments come first
|
||||
while (argIt != argv.end()) {
|
||||
// '-' is allowed as a "default" file, this isn't an option but a positional
|
||||
// argument
|
||||
if ((*argIt)[0] != '-' || *argIt == "-")
|
||||
break;
|
||||
|
||||
const std::string &opt = *argIt;
|
||||
|
||||
++argIt;
|
||||
argCount--;
|
||||
|
||||
if (handleUnsaved) {
|
||||
// TODO: handle multiple unsaved files
|
||||
if (opt == "--num-unsaved=1") {
|
||||
command_.unsavedFiles.resize(1);
|
||||
}
|
||||
} else {
|
||||
std::clog << "error: invalid option for '" << actionStr << "': '" << opt
|
||||
<< "' unknown\n";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (argCount != static_cast<int>(positionalArgs.size())) {
|
||||
std::clog << "error: invalid number of arguments for '" << actionStr
|
||||
<< "' (requires " << positionalArgs.size() << " got " << argCount
|
||||
<< ")\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (auto fn : positionalArgs) {
|
||||
if (!fn(*argIt)) {
|
||||
std::clog << "error: parsing command '" << actionStr
|
||||
<< "': invalid argument '" << *argIt << "'\n";
|
||||
return 0;
|
||||
}
|
||||
++argIt;
|
||||
}
|
||||
|
||||
// '-' is used as a special file to inform that the buffer hasn't been saved
|
||||
// on disk and only the buffer content is available. libclang needs a file, so
|
||||
// this is treated as a special value for irony-server to create a temporary
|
||||
// file for this. note taht libclang will gladly accept '-' as a filename but
|
||||
// we don't want to let this happen since irony already reads stdin.
|
||||
if (command_.file == "-") {
|
||||
command_.file = tempFile_.getPath();
|
||||
}
|
||||
|
||||
// When a file is provided, the next line contains the compilation options to
|
||||
// pass to libclang.
|
||||
if (readCompileOptions) {
|
||||
std::string compileOptions;
|
||||
std::getline(std::cin, compileOptions);
|
||||
|
||||
command_.flags = unescapeCommandLine(compileOptions);
|
||||
}
|
||||
|
||||
// read unsaved files
|
||||
// filename
|
||||
// filesize
|
||||
// <file content...>
|
||||
for (auto &p : command_.unsavedFiles) {
|
||||
std::getline(std::cin, p.first);
|
||||
|
||||
unsigned length;
|
||||
std::string filesizeStr;
|
||||
std::getline(std::cin, filesizeStr);
|
||||
|
||||
UnsignedIntConverter uintConverter(&length);
|
||||
|
||||
if (!uintConverter(filesizeStr)) {
|
||||
std::clog << "error: invalid file size '" << filesizeStr << "'\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
p.second.resize(length);
|
||||
std::cin.read(p.second.data(), p.second.size());
|
||||
|
||||
CXUnsavedFile cxUnsavedFile;
|
||||
|
||||
cxUnsavedFile.Filename = p.first.c_str();
|
||||
cxUnsavedFile.Contents = p.second.data();
|
||||
cxUnsavedFile.Length = p.second.size();
|
||||
command_.cxUnsavedFiles.push_back(cxUnsavedFile);
|
||||
|
||||
char nl;
|
||||
std::cin.read(&nl, 1);
|
||||
if (nl != '\n') {
|
||||
std::clog << "error: missing newline for unsaved file content\n";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return &command_;
|
||||
}
|
70
.emacs.d/elpa/irony-20160203.1207/server/src/Command.h
Normal file
70
.emacs.d/elpa/irony-20160203.1207/server/src/Command.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/**-*-C++-*-
|
||||
* \file
|
||||
* \author Guillaume Papin <guillaume.papin@epitech.eu>
|
||||
*
|
||||
* \brief Command parser declarations.
|
||||
*
|
||||
* This file is distributed under the GNU General Public License. See
|
||||
* COPYING for details.
|
||||
*/
|
||||
|
||||
#ifndef IRONY_MODE_SERVER_COMMAND_H_
|
||||
#define IRONY_MODE_SERVER_COMMAND_H_
|
||||
|
||||
#include "support/CIndex.h"
|
||||
#include "support/TemporaryFile.h"
|
||||
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class TemporaryFile;
|
||||
|
||||
struct Command {
|
||||
Command() {
|
||||
clear();
|
||||
}
|
||||
|
||||
void clear() {
|
||||
action = Unknown;
|
||||
flags.clear();
|
||||
file.clear();
|
||||
dir.clear();
|
||||
line = 0;
|
||||
column = 0;
|
||||
unsavedFiles.clear();
|
||||
cxUnsavedFiles.clear();
|
||||
opt = false;
|
||||
}
|
||||
|
||||
#define X(sym, str, desc) sym,
|
||||
enum Action {
|
||||
#include "Commands.def"
|
||||
} action;
|
||||
|
||||
std::vector<std::string> flags;
|
||||
std::string file;
|
||||
std::string dir;
|
||||
unsigned line;
|
||||
unsigned column;
|
||||
// pair of (filename, content)
|
||||
std::vector<std::pair<std::string, std::vector<char>>> unsavedFiles;
|
||||
std::vector<CXUnsavedFile> cxUnsavedFiles;
|
||||
bool opt;
|
||||
};
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, const Command::Action &action);
|
||||
std::ostream &operator<<(std::ostream &os, const Command &command);
|
||||
|
||||
class CommandParser {
|
||||
public:
|
||||
CommandParser();
|
||||
|
||||
Command *parse(const std::vector<std::string> &argv);
|
||||
|
||||
private:
|
||||
Command command_;
|
||||
TemporaryFile tempFile_;
|
||||
};
|
||||
|
||||
#endif // IRONY_MODE_SERVER_COMMAND_H_
|
28
.emacs.d/elpa/irony-20160203.1207/server/src/Commands.def
Normal file
28
.emacs.d/elpa/irony-20160203.1207/server/src/Commands.def
Normal file
@@ -0,0 +1,28 @@
|
||||
/**-*-C++-*-
|
||||
* \file
|
||||
* \author Guillaume Papin <guillaume.papin@epitech.eu>
|
||||
*
|
||||
* \brief Command list.
|
||||
*
|
||||
* This file is distributed under the GNU General Public License. See
|
||||
* COPYING for details.
|
||||
*/
|
||||
|
||||
#ifndef X
|
||||
#error Please define the 'X(id, command, description)' macro before inclusion!
|
||||
#endif
|
||||
|
||||
X(Complete, "complete", "FILE LINE COL - perform code completion at a given location")
|
||||
X(Diagnostics, "diagnostics", "print the diagnostics of the last parse")
|
||||
X(Exit, "exit", "exit interactive mode, print nothing")
|
||||
X(GetCompileOptions, "get-compile-options", "BUILD_DIR FILE - "
|
||||
"get compile options for FILE from JSON database in PROJECT_ROOT")
|
||||
X(GetType, "get-type", "LINE COL - get type of symbol at a given location")
|
||||
X(Help, "help", "show this message")
|
||||
X(Parse, "parse", "FILE - parse the given file")
|
||||
X(SetDebug, "set-debug", "[on|off] - enable or disable verbose logging")
|
||||
|
||||
// sentinel value, should be the last one
|
||||
X(Unknown, "<unkown>", "<unspecified>")
|
||||
|
||||
#undef X
|
436
.emacs.d/elpa/irony-20160203.1207/server/src/Irony.cpp
Normal file
436
.emacs.d/elpa/irony-20160203.1207/server/src/Irony.cpp
Normal file
@@ -0,0 +1,436 @@
|
||||
/**
|
||||
* \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>
|
||||
|
||||
static std::string cxStringToStd(CXString cxString) {
|
||||
std::string stdStr;
|
||||
|
||||
if (const char *cstr = clang_getCString(cxString)) {
|
||||
stdStr = cstr;
|
||||
}
|
||||
|
||||
clang_disposeString(cxString);
|
||||
return stdStr;
|
||||
}
|
||||
|
||||
Irony::Irony() : activeTu_(nullptr), debug_(false) {
|
||||
}
|
||||
|
||||
static const char *diagnosticSeverity(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";
|
||||
}
|
||||
|
||||
static void dumpDiagnostics(const CXTranslationUnit &tu) {
|
||||
std::cout << "(\n";
|
||||
|
||||
std::string file;
|
||||
|
||||
for (unsigned i = 0, diagnosticCount = clang_getNumDiagnostics(tu);
|
||||
i < diagnosticCount;
|
||||
++i) {
|
||||
CXDiagnostic diagnostic = clang_getDiagnostic(tu, i);
|
||||
|
||||
CXSourceLocation location = clang_getDiagnosticLocation(diagnostic);
|
||||
|
||||
unsigned line, column, offset;
|
||||
if (clang_equalLocations(location, clang_getNullLocation())) {
|
||||
file.clear();
|
||||
line = 0;
|
||||
column = 0;
|
||||
offset = 0;
|
||||
} else {
|
||||
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";
|
||||
|
||||
clang_disposeDiagnostic(diagnostic);
|
||||
}
|
||||
|
||||
std::cout << ")\n";
|
||||
}
|
||||
|
||||
void Irony::parse(const std::string &file,
|
||||
const std::vector<std::string> &flags,
|
||||
const std::vector<CXUnsavedFile> &unsavedFiles) {
|
||||
activeTu_ = tuManager_.parse(file, flags, unsavedFiles);
|
||||
file_ = file;
|
||||
std::cout << (activeTu_ ? "t" : "nil") << "\n";
|
||||
}
|
||||
|
||||
void Irony::diagnostics() const {
|
||||
if (activeTu_ == nullptr) {
|
||||
std::clog << "W: diagnostics - parse wasn't called\n";
|
||||
|
||||
std::cout << "nil\n";
|
||||
return;
|
||||
}
|
||||
|
||||
dumpDiagnostics(activeTu_);
|
||||
}
|
||||
|
||||
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_);
|
||||
}
|
||||
|
||||
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,
|
||||
const std::vector<CXUnsavedFile> &unsavedFiles) {
|
||||
CXTranslationUnit tu = tuManager_.getOrCreateTU(file, flags, unsavedFiles);
|
||||
|
||||
if (tu == nullptr) {
|
||||
std::cout << "nil\n";
|
||||
return;
|
||||
}
|
||||
|
||||
if (CXCodeCompleteResults *completions =
|
||||
clang_codeCompleteAt(tu,
|
||||
file.c_str(),
|
||||
line,
|
||||
col,
|
||||
const_cast<CXUnsavedFile *>(unsavedFiles.data()),
|
||||
unsavedFiles.size(),
|
||||
(clang_defaultCodeCompleteOptions() &
|
||||
~CXCodeComplete_IncludeCodePatterns)
|
||||
#if HAS_BRIEF_COMMENTS_IN_COMPLETION
|
||||
|
|
||||
CXCodeComplete_IncludeBriefComments
|
||||
#endif
|
||||
)) {
|
||||
|
||||
if (debug_) {
|
||||
unsigned numDiags = clang_codeCompleteGetNumDiagnostics(completions);
|
||||
std::clog << "debug: complete: " << numDiags << " diagnostic(s)\n";
|
||||
for (unsigned i = 0; i < numDiags; ++i) {
|
||||
CXDiagnostic diagnostic =
|
||||
clang_codeCompleteGetDiagnostic(completions, i);
|
||||
CXString s = clang_formatDiagnostic(
|
||||
diagnostic, clang_defaultDiagnosticDisplayOptions());
|
||||
|
||||
std::clog << clang_getCString(s) << std::endl;
|
||||
clang_disposeString(s);
|
||||
clang_disposeDiagnostic(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
clang_sortCodeCompletionResults(completions->Results,
|
||||
completions->NumResults);
|
||||
|
||||
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;
|
||||
|
||||
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();
|
||||
// annotation is what comes after the typedtext
|
||||
annotationStart = prototype.size();
|
||||
typedTextSet = true;
|
||||
}
|
||||
}
|
||||
|
||||
#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";
|
||||
}
|
||||
|
||||
clang_disposeCodeCompleteResults(completions);
|
||||
std::cout << ")\n";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Irony::getCompileOptions(const std::string &buildDir,
|
||||
const std::string &file) const {
|
||||
#if !(HAS_COMPILATION_DATABASE)
|
||||
|
||||
(void)buildDir;
|
||||
(void)file;
|
||||
|
||||
std::cout << "nil\n";
|
||||
return;
|
||||
|
||||
#else
|
||||
CXCompilationDatabase_Error error;
|
||||
CXCompilationDatabase db =
|
||||
clang_CompilationDatabase_fromDirectory(buildDir.c_str(), &error);
|
||||
|
||||
switch (error) {
|
||||
case CXCompilationDatabase_CanNotLoadDatabase:
|
||||
std::clog << "I: could not load compilation database in '" << buildDir
|
||||
<< "'\n";
|
||||
std::cout << "nil\n";
|
||||
return;
|
||||
|
||||
case CXCompilationDatabase_NoError:
|
||||
break;
|
||||
}
|
||||
|
||||
CXCompileCommands compileCommands =
|
||||
clang_CompilationDatabase_getCompileCommands(db, file.c_str());
|
||||
|
||||
std::cout << "(\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);
|
||||
clang_CompilationDatabase_dispose(db);
|
||||
#endif
|
||||
}
|
126
.emacs.d/elpa/irony-20160203.1207/server/src/Irony.h
Normal file
126
.emacs.d/elpa/irony-20160203.1207/server/src/Irony.h
Normal file
@@ -0,0 +1,126 @@
|
||||
/**-*-C++-*-
|
||||
* \file
|
||||
* \author Guillaume Papin <guillaume.papin@epitech.eu>
|
||||
*
|
||||
* \brief irony-server "API" declarations.
|
||||
*
|
||||
* Contains the commands that the Emacs package relies on. These commands are
|
||||
* mostly wrappers around a subset of the features provided by libclang. Command
|
||||
* results are printed to \c std::cout as s-expr, in order to make it easy for
|
||||
* Emacs to consume.
|
||||
*
|
||||
* This file is distributed under the GNU General Public License. See
|
||||
* COPYING for details.
|
||||
*/
|
||||
|
||||
#ifndef IRONY_MODE_SERVER_IRONY_H_
|
||||
#define IRONY_MODE_SERVER_IRONY_H_
|
||||
|
||||
#include "TUManager.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class Irony {
|
||||
public:
|
||||
Irony();
|
||||
|
||||
bool isDebugEnabled() const {
|
||||
return debug_;
|
||||
}
|
||||
|
||||
/// \name Modifiers
|
||||
/// \{
|
||||
|
||||
/// \brief Set or unset debugging of commands.
|
||||
void setDebug(bool enable) {
|
||||
debug_ = enable;
|
||||
}
|
||||
|
||||
/// Parse or reparse the given file and compile options.
|
||||
///
|
||||
/// If the compile options have changed, the translation unit is re-created to
|
||||
/// take this into account.
|
||||
///
|
||||
/// Output \c nil or \c t, whether or not parsing the translation unit
|
||||
/// succeeded.
|
||||
///
|
||||
/// \sa diagnostics(), getType()
|
||||
void parse(const std::string &file,
|
||||
const std::vector<std::string> &flags,
|
||||
const std::vector<CXUnsavedFile> &unsavedFiles);
|
||||
|
||||
/// \}
|
||||
|
||||
/// \name Observers
|
||||
/// \{
|
||||
|
||||
/// \brief Retrieve the last parse diagnostics for the given file.
|
||||
void diagnostics() const;
|
||||
|
||||
/// \brief Get types of symbol at a given location.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// \code
|
||||
/// typedef int MyType;
|
||||
/// MyType a;
|
||||
/// \endcode
|
||||
///
|
||||
/// Type of cursor location for 'a' is:
|
||||
///
|
||||
/// \code{.el}
|
||||
/// ("MyType" "int")
|
||||
/// \endcode
|
||||
///
|
||||
/// TODO: test with CXString(), seems to be twice the same string
|
||||
///
|
||||
void getType(unsigned line, unsigned col) const;
|
||||
|
||||
/// \brief Perform code completion at a given location.
|
||||
///
|
||||
/// Print the list of candidate if any. The empty list is printed on error.
|
||||
///
|
||||
/// Example output:
|
||||
///
|
||||
/// \code{.el}
|
||||
/// (
|
||||
/// ("foo")
|
||||
/// ("bar")
|
||||
/// ("baz")
|
||||
/// )
|
||||
/// \endcode
|
||||
///
|
||||
void complete(const std::string &file,
|
||||
unsigned line,
|
||||
unsigned col,
|
||||
const std::vector<std::string> &flags,
|
||||
const std::vector<CXUnsavedFile> &unsavedFiles);
|
||||
|
||||
/// \brief Get compile options from JSON database.
|
||||
///
|
||||
/// \param buildDir Directory containing compile_commands.json
|
||||
/// \param file File to obtain compile commands for.
|
||||
///
|
||||
/// Example output:
|
||||
///
|
||||
/// \code{.el}
|
||||
/// (
|
||||
/// (("-Wfoo" "-DBAR" "-Iqux") . "/path/to/working/directory")
|
||||
/// (("-Wfoo-alt" "-DBAR_ALT" "-Iqux/alt") . "/alt/working/directory")
|
||||
/// )
|
||||
/// \endcode
|
||||
///
|
||||
void getCompileOptions(const std::string &buildDir,
|
||||
const std::string &file) const;
|
||||
|
||||
/// \}
|
||||
|
||||
private:
|
||||
TUManager tuManager_;
|
||||
CXTranslationUnit activeTu_;
|
||||
std::string file_;
|
||||
bool debug_;
|
||||
};
|
||||
|
||||
#endif // IRONY_MODE_SERVER_IRONY_H_
|
180
.emacs.d/elpa/irony-20160203.1207/server/src/TUManager.cpp
Normal file
180
.emacs.d/elpa/irony-20160203.1207/server/src/TUManager.cpp
Normal file
@@ -0,0 +1,180 @@
|
||||
/**
|
||||
* \file
|
||||
* \author Guillaume Papin <guillaume.papin@epitech.eu>
|
||||
*
|
||||
* \brief See TUManager.hh
|
||||
*
|
||||
* This file is distributed under the GNU General Public License. See
|
||||
* COPYING for details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "TUManager.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
TUManager::TUManager()
|
||||
: index_(clang_createIndex(0, 0))
|
||||
, translationUnits_()
|
||||
, parseTUOptions_(clang_defaultEditingTranslationUnitOptions()) {
|
||||
|
||||
// this seems necessary to trigger "correct" reparse (/codeCompleteAt)
|
||||
// clang_reparseTranslationUnit documentation states:
|
||||
//
|
||||
// > * \param TU The translation unit whose contents will be re-parsed. The
|
||||
// > * translation unit must originally have been built with
|
||||
// > * \c clang_createTranslationUnitFromSourceFile().
|
||||
//
|
||||
// clang_createTranslationUnitFromSourceFile() is just a call to
|
||||
// clang_parseTranslationUnit() with
|
||||
// CXTranslationUnit_DetailedPreprocessingRecord enabled but because we want
|
||||
// some other flags to be set we can't just call
|
||||
// clang_createTranslationUnitFromSourceFile()
|
||||
parseTUOptions_ |= CXTranslationUnit_DetailedPreprocessingRecord;
|
||||
|
||||
#if HAS_BRIEF_COMMENTS_IN_COMPLETION
|
||||
parseTUOptions_ |= CXTranslationUnit_IncludeBriefCommentsInCodeCompletion;
|
||||
#endif
|
||||
|
||||
// XXX: Completion results caching doesn't seem to work right, changes at the
|
||||
// top of the file (i.e: new declarations) aren't detected and do not appear
|
||||
// in completion results.
|
||||
parseTUOptions_ &= ~CXTranslationUnit_CacheCompletionResults;
|
||||
|
||||
// XXX: A bug in old version of Clang (at least '3.1-8') caused the completion
|
||||
// to fail on the standard library types when
|
||||
// CXTranslationUnit_PrecompiledPreamble is used. We disable this option for
|
||||
// old versions of libclang. As a result the completion will work but
|
||||
// significantly slower.
|
||||
//
|
||||
// -- https://github.com/Sarcasm/irony-mode/issues/4
|
||||
if (CINDEX_VERSION < 6) {
|
||||
parseTUOptions_ &= ~CXTranslationUnit_PrecompiledPreamble;
|
||||
}
|
||||
}
|
||||
|
||||
TUManager::~TUManager() {
|
||||
clang_disposeIndex(index_);
|
||||
}
|
||||
|
||||
CXTranslationUnit &TUManager::tuRef(const std::string &filename,
|
||||
const std::vector<std::string> &flags) {
|
||||
CXTranslationUnit &tu = translationUnits_[filename];
|
||||
|
||||
// if the flags changed since the last time, invalidate the translation unit
|
||||
auto &flagsCache = flagsPerFileCache_[filename];
|
||||
if (flagsCache.size() != flags.size() ||
|
||||
!std::equal(flagsCache.begin(), flagsCache.end(), flags.begin())) {
|
||||
if (tu) {
|
||||
clang_disposeTranslationUnit(tu);
|
||||
tu = nullptr;
|
||||
}
|
||||
// remember the flags for the next parse
|
||||
flagsCache = flags;
|
||||
}
|
||||
return tu;
|
||||
}
|
||||
|
||||
CXTranslationUnit
|
||||
TUManager::parse(const std::string &filename,
|
||||
const std::vector<std::string> &flags,
|
||||
const std::vector<CXUnsavedFile> &unsavedFiles) {
|
||||
CXTranslationUnit &tu = tuRef(filename, flags);
|
||||
|
||||
if (tu == nullptr) {
|
||||
std::vector<const char *> argv;
|
||||
|
||||
#ifdef CLANG_BUILTIN_HEADERS_DIR
|
||||
// Make sure libclang find its builtin headers, this is a known issue with
|
||||
// libclang, see:
|
||||
// - http://lists.cs.uiuc.edu/pipermail/cfe-dev/2012-July/022893.html
|
||||
//
|
||||
// > Make sure that Clang is using its own . It will be in a directory
|
||||
// > ending in clang/3.2/include/ where 3.2 is the version of clang that you
|
||||
// > are using. You may need to explicitly add it to your header search.
|
||||
// > Usually clang finds this directory relative to the executable with
|
||||
// > CompilerInvocation::GetResourcesPath(Argv0, MainAddr), but using just
|
||||
// > the libraries, it can't automatically find it.
|
||||
argv.push_back("-isystem");
|
||||
argv.push_back(CLANG_BUILTIN_HEADERS_DIR);
|
||||
#endif
|
||||
|
||||
for (auto &flag : flags) {
|
||||
argv.push_back(flag.c_str());
|
||||
}
|
||||
|
||||
tu = clang_parseTranslationUnit(
|
||||
index_,
|
||||
filename.c_str(),
|
||||
argv.data(),
|
||||
static_cast<int>(argv.size()),
|
||||
const_cast<CXUnsavedFile *>(unsavedFiles.data()),
|
||||
unsavedFiles.size(),
|
||||
parseTUOptions_);
|
||||
}
|
||||
|
||||
if (tu == nullptr) {
|
||||
std::clog << "error: libclang couldn't parse '" << filename << "'\n";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Reparsing is necessary to enable optimizations.
|
||||
//
|
||||
// From the clang mailing list (cfe-dev):
|
||||
// From: Douglas Gregor
|
||||
// Subject: Re: Clang indexing library performance
|
||||
// ...
|
||||
// You want to use the "default editing options" when parsing the translation
|
||||
// unit
|
||||
// clang_defaultEditingTranslationUnitOptions()
|
||||
// and then reparse at least once. That will enable the various
|
||||
// code-completion optimizations that should bring this time down
|
||||
// significantly.
|
||||
if (clang_reparseTranslationUnit(
|
||||
tu,
|
||||
unsavedFiles.size(),
|
||||
const_cast<CXUnsavedFile *>(unsavedFiles.data()),
|
||||
clang_defaultReparseOptions(tu))) {
|
||||
// a 'fatal' error occured (even a diagnostic is impossible)
|
||||
clang_disposeTranslationUnit(tu);
|
||||
std::clog << "error: libclang couldn't reparse '" << filename << "'\n";
|
||||
tu = 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return tu;
|
||||
}
|
||||
|
||||
CXTranslationUnit
|
||||
TUManager::getOrCreateTU(const std::string &filename,
|
||||
const std::vector<std::string> &flags,
|
||||
const std::vector<CXUnsavedFile> &unsavedFiles) {
|
||||
if (auto tu = tuRef(filename, flags))
|
||||
return tu;
|
||||
|
||||
return parse(filename, flags, unsavedFiles);
|
||||
}
|
||||
|
||||
void TUManager::invalidateCachedTU(const std::string &filename) {
|
||||
TranslationUnitsMap::iterator it = translationUnits_.find(filename);
|
||||
|
||||
if (it != translationUnits_.end()) {
|
||||
if (CXTranslationUnit &tu = it->second)
|
||||
clang_disposeTranslationUnit(tu);
|
||||
|
||||
translationUnits_.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void TUManager::invalidateAllCachedTUs() {
|
||||
TranslationUnitsMap::iterator it = translationUnits_.begin();
|
||||
|
||||
while (it != translationUnits_.end()) {
|
||||
if (CXTranslationUnit &tu = it->second) {
|
||||
clang_disposeTranslationUnit(tu);
|
||||
translationUnits_.erase(it++); // post-increment keeps the iterator valid
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
109
.emacs.d/elpa/irony-20160203.1207/server/src/TUManager.h
Normal file
109
.emacs.d/elpa/irony-20160203.1207/server/src/TUManager.h
Normal file
@@ -0,0 +1,109 @@
|
||||
/**-*-C++-*-
|
||||
* \file
|
||||
* \author Guillaume Papin <guillaume.papin@epitech.eu>
|
||||
*
|
||||
* \brief Translation Unit manager.
|
||||
*
|
||||
* Keeps a cache of translation units, reparsing or recreating them as
|
||||
* necessary.
|
||||
*
|
||||
* This file is distributed under the GNU General Public License. See
|
||||
* COPYING for details.
|
||||
*/
|
||||
|
||||
#ifndef IRONY_MODE_SERVER_TUMANAGER_H_
|
||||
#define IRONY_MODE_SERVER_TUMANAGER_H_
|
||||
|
||||
#include "support/CIndex.h"
|
||||
#include "support/NonCopyable.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class TUManager : public util::NonCopyable {
|
||||
public:
|
||||
TUManager();
|
||||
~TUManager();
|
||||
|
||||
/**
|
||||
* \brief Parse \p filename with flag \p flags.
|
||||
*
|
||||
* The first time call \c clang_parseTranslationUnit() and save the TU in the
|
||||
* member \c translationUnits_, The next call with the same \p filename will
|
||||
* call \c clang_reparseTranslationUnit().
|
||||
*
|
||||
* usage:
|
||||
* \code
|
||||
* std::vector<std::string> flags;
|
||||
* flags.push_back("-I../utils");
|
||||
* CXTranslationUnit tu = tuManager.parse("file.cpp", flags);
|
||||
*
|
||||
* if (! tu)
|
||||
* std::cerr << "parsing translation unit failed\n";
|
||||
* \endcode
|
||||
*
|
||||
* \return The translation unit, if the parsing failed the translation unit
|
||||
* will be \c NULL.
|
||||
*/
|
||||
CXTranslationUnit parse(const std::string &filename,
|
||||
const std::vector<std::string> &flags,
|
||||
const std::vector<CXUnsavedFile> &unsavedFiles);
|
||||
|
||||
/**
|
||||
* \brief Retrieve, creating it if necessary the TU associated to filename.
|
||||
*
|
||||
* \return The translation unit. Will be NULL if the translation unit couldn't
|
||||
* be created.
|
||||
*/
|
||||
CXTranslationUnit getOrCreateTU(const std::string &filename,
|
||||
const std::vector<std::string> &flags,
|
||||
const std::vector<CXUnsavedFile> &unsavedFiles);
|
||||
|
||||
/**
|
||||
* \brief Invalidate a given cached TU, the next use of a TU will require
|
||||
* reparsing.
|
||||
*
|
||||
* This can be useful for example: when the flags used to compile a file have
|
||||
* changed.
|
||||
*
|
||||
* \param filename The filename for which the associated
|
||||
* translation unit flags need to be invalidated.
|
||||
*
|
||||
* \sa invalidateAllCachedTUs()
|
||||
*/
|
||||
void invalidateCachedTU(const std::string &filename);
|
||||
|
||||
/**
|
||||
* \brief Invalidate all cached TU, the next use of a TU will require
|
||||
* reparsing.
|
||||
*
|
||||
* \sa invalidateCachedTU()
|
||||
*/
|
||||
void invalidateAllCachedTUs();
|
||||
|
||||
private:
|
||||
/**
|
||||
* \brief Get a reference to the translation unit that matches \p filename
|
||||
* with the given set of flags.
|
||||
*
|
||||
* The TU will be null if it has never been parsed or if the flags have
|
||||
* changed.
|
||||
*
|
||||
* \todo Find a proper name.
|
||||
*/
|
||||
CXTranslationUnit &tuRef(const std::string &filename,
|
||||
const std::vector<std::string> &flags);
|
||||
|
||||
private:
|
||||
typedef std::map<const std::string, CXTranslationUnit> TranslationUnitsMap;
|
||||
typedef std::map<const std::string, std::vector<std::string>> FilenameFlagsMap;
|
||||
|
||||
private:
|
||||
CXIndex index_;
|
||||
TranslationUnitsMap translationUnits_; // cache variable
|
||||
FilenameFlagsMap flagsPerFileCache_;
|
||||
unsigned parseTUOptions_;
|
||||
};
|
||||
|
||||
#endif /* !IRONY_MODE_SERVER_TUMANAGER_H_ */
|
235
.emacs.d/elpa/irony-20160203.1207/server/src/main.cpp
Normal file
235
.emacs.d/elpa/irony-20160203.1207/server/src/main.cpp
Normal file
@@ -0,0 +1,235 @@
|
||||
/**
|
||||
* \file
|
||||
* \author Guillaume Papin <guillaume.papin@epitech.eu>
|
||||
*
|
||||
* This file is distributed under the GNU General Public License. See
|
||||
* COPYING for details.
|
||||
*/
|
||||
|
||||
#include "Irony.h"
|
||||
#include "Command.h"
|
||||
|
||||
#include "support/CIndex.h"
|
||||
#include "support/CommandLineParser.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
static void printHelp() {
|
||||
std::cout << "usage: irony-server [OPTIONS...] [COMMAND] [ARGS...]\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
" -v, --version\n"
|
||||
" -h, --help\n"
|
||||
" -i, --interactive\n"
|
||||
" -d, --debug\n"
|
||||
" --log-file PATH\n"
|
||||
"\n"
|
||||
"Commands:\n";
|
||||
|
||||
#define X(sym, str, desc) \
|
||||
if (Command::sym != Command::Unknown) \
|
||||
std::cout << std::left << std::setw(25) << " " str << desc << "\n";
|
||||
#include "Commands.def"
|
||||
}
|
||||
|
||||
static void printVersion() {
|
||||
// do not change the format for the first line, external programs should be
|
||||
// able to rely on it
|
||||
std::cout << "irony-server version " IRONY_PACKAGE_VERSION "\n";
|
||||
|
||||
CXString cxVersionString = clang_getClangVersion();
|
||||
std::cout << clang_getCString(cxVersionString) << "\n";
|
||||
clang_disposeString(cxVersionString);
|
||||
}
|
||||
|
||||
static void dumpUnsavedFiles(Command &command) {
|
||||
for (int i = 0; i < static_cast<int>(command.unsavedFiles.size()); ++i) {
|
||||
std::clog << "unsaved file " << i + 1 << ": "
|
||||
<< command.unsavedFiles[i].first << "\n"
|
||||
<< "----\n";
|
||||
std::copy(command.unsavedFiles[i].second.begin(),
|
||||
command.unsavedFiles[i].second.end(),
|
||||
std::ostream_iterator<char>(std::clog));
|
||||
std::clog << "----" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
struct CommandProviderInterface {
|
||||
virtual ~CommandProviderInterface() { }
|
||||
|
||||
virtual std::vector<std::string> nextCommand() = 0;
|
||||
};
|
||||
|
||||
struct CommandLineCommandProvider : CommandProviderInterface {
|
||||
CommandLineCommandProvider(const std::vector<std::string> &argv)
|
||||
: argv_(argv), firstCall_(true) {
|
||||
}
|
||||
|
||||
std::vector<std::string> nextCommand() {
|
||||
if (firstCall_) {
|
||||
firstCall_ = false;
|
||||
return argv_;
|
||||
}
|
||||
|
||||
return std::vector<std::string>(1, "exit");
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::string> argv_;
|
||||
bool firstCall_;
|
||||
};
|
||||
|
||||
struct InteractiveCommandProvider : CommandProviderInterface {
|
||||
std::vector<std::string> nextCommand() {
|
||||
std::string line;
|
||||
|
||||
if (std::getline(std::cin, line)) {
|
||||
return unescapeCommandLine(line);
|
||||
}
|
||||
|
||||
return std::vector<std::string>(1, "exit");
|
||||
}
|
||||
};
|
||||
|
||||
struct RestoreClogOnExit {
|
||||
RestoreClogOnExit() : rdbuf_(std::clog.rdbuf()) {
|
||||
}
|
||||
|
||||
~RestoreClogOnExit() {
|
||||
std::clog.rdbuf(rdbuf_);
|
||||
}
|
||||
|
||||
private:
|
||||
RestoreClogOnExit(const RestoreClogOnExit &);
|
||||
RestoreClogOnExit &operator=(const RestoreClogOnExit &);
|
||||
|
||||
private:
|
||||
std::streambuf *rdbuf_;
|
||||
};
|
||||
|
||||
int main(int ac, const char *av[]) {
|
||||
std::vector<std::string> argv(&av[1], &av[ac]);
|
||||
|
||||
// stick to STL streams, no mix of C and C++ for IO operations
|
||||
std::ios_base::sync_with_stdio(false);
|
||||
|
||||
bool interactiveMode = false;
|
||||
|
||||
Irony irony;
|
||||
|
||||
if (ac == 1) {
|
||||
printHelp();
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::ofstream logFile;
|
||||
|
||||
// When logging to a specific file, std::clog.rdbuf() is replaced by the log
|
||||
// file's one. When we return from the main, this buffer is deleted (at the
|
||||
// same time as logFile) but std::clog is still active, and will try to
|
||||
// release the rdbuf() which has already been released in logFile's
|
||||
// destructor. To avoid this we restore std::clog()'s original rdbuf on exit.
|
||||
RestoreClogOnExit clogBufferRestorer;
|
||||
|
||||
unsigned optCount = 0;
|
||||
while (optCount < argv.size()) {
|
||||
const std::string &opt = argv[optCount];
|
||||
|
||||
if (opt.c_str()[0] != '-')
|
||||
break;
|
||||
|
||||
if (opt == "--help" || opt == "-h") {
|
||||
printHelp();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (opt == "--version" || opt == "-v") {
|
||||
printVersion();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (opt == "--interactive" || opt == "-i") {
|
||||
interactiveMode = true;
|
||||
} else if (opt == "--debug" || opt == "-d") {
|
||||
irony.setDebug(true);
|
||||
} else if (opt == "--log-file" && (optCount + 1) < argv.size()) {
|
||||
++optCount;
|
||||
logFile.open(argv[optCount]);
|
||||
std::clog.rdbuf(logFile.rdbuf());
|
||||
} else {
|
||||
std::cerr << "error: invalid option '" << opt << "'\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
++optCount;
|
||||
}
|
||||
|
||||
argv.erase(argv.begin(), argv.begin() + optCount);
|
||||
|
||||
CommandParser commandParser;
|
||||
std::unique_ptr<CommandProviderInterface> commandProvider;
|
||||
|
||||
if (interactiveMode) {
|
||||
commandProvider.reset(new InteractiveCommandProvider());
|
||||
} else {
|
||||
commandProvider.reset(new CommandLineCommandProvider(argv));
|
||||
}
|
||||
|
||||
while (Command *c = commandParser.parse(commandProvider->nextCommand())) {
|
||||
if (c->action != Command::Exit) {
|
||||
std::clog << "execute: " << *c << std::endl;
|
||||
|
||||
if (irony.isDebugEnabled()) {
|
||||
dumpUnsavedFiles(*c);
|
||||
}
|
||||
}
|
||||
|
||||
switch (c->action) {
|
||||
case Command::Help:
|
||||
printHelp();
|
||||
break;
|
||||
|
||||
case Command::Complete:
|
||||
irony.complete(c->file, c->line, c->column, c->flags, c->cxUnsavedFiles);
|
||||
break;
|
||||
|
||||
case Command::Diagnostics:
|
||||
irony.diagnostics();
|
||||
break;
|
||||
|
||||
case Command::Exit:
|
||||
return 0;
|
||||
|
||||
case Command::GetCompileOptions:
|
||||
irony.getCompileOptions(c->dir, c->file);
|
||||
break;
|
||||
|
||||
case Command::GetType:
|
||||
irony.getType(c->line, c->column);
|
||||
break;
|
||||
|
||||
case Command::Parse:
|
||||
irony.parse(c->file, c->flags, c->cxUnsavedFiles);
|
||||
break;
|
||||
|
||||
case Command::SetDebug:
|
||||
irony.setDebug(c->opt);
|
||||
break;
|
||||
|
||||
case Command::Unknown:
|
||||
assert(0 && "unreacheable code...reached!");
|
||||
break;
|
||||
}
|
||||
|
||||
std::cout << "\n;;EOT\n" << std::flush;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* \file
|
||||
* \brief Wrapper around Clang Indexing Public C Interface header.
|
||||
*
|
||||
* This file is distributed under the GNU General Public License. See
|
||||
* COPYING for details.
|
||||
*/
|
||||
|
||||
#ifndef IRONY_MODE_SERVER_SUPPORT_CINDEXVERSION_H_
|
||||
#define IRONY_MODE_SERVER_SUPPORT_CINDEXVERSION_H_
|
||||
|
||||
#include <clang-c/Index.h>
|
||||
|
||||
/// Use <tt>\#if CINDEX_VERSION_VERSION > 10047</tt> to test for
|
||||
/// CINDEX_VERSION_MAJOR = 1 and CINDEX_VERSION_MINOR = 47.
|
||||
#ifndef CINDEX_VERSION
|
||||
#define CINDEX_VERSION 0 // pre-clang 3.2 support
|
||||
#endif
|
||||
|
||||
#if CINDEX_VERSION >= 6
|
||||
#define HAS_BRIEF_COMMENTS_IN_COMPLETION 1
|
||||
#else
|
||||
#define HAS_BRIEF_COMMENTS_IN_COMPLETION 0
|
||||
#endif
|
||||
|
||||
#if CINDEX_VERSION >= 6
|
||||
#define HAS_COMPILATION_DATABASE 1
|
||||
#include <clang-c/CXCompilationDatabase.h>
|
||||
#else
|
||||
#define HAS_COMPILATION_DATABASE 0
|
||||
#endif
|
||||
|
||||
#endif /* !IRONY_MODE_SERVER_SUPPORT_CINDEXVERSION_H_ */
|
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* \file
|
||||
* \author Guillaume Papin <guillaume.papin@epitech.eu>
|
||||
*
|
||||
* This file is distributed under the GNU General Public License. See
|
||||
* COPYING for details.
|
||||
*/
|
||||
|
||||
#include "CommandLineParser.h"
|
||||
|
||||
namespace {
|
||||
|
||||
/// \brief A parser for escaped strings of command line arguments.
|
||||
///
|
||||
/// Assumes \-escaping for quoted arguments (see the documentation of
|
||||
/// unescapeCommandLine(...)).
|
||||
class CommandLineArgumentParser {
|
||||
public:
|
||||
CommandLineArgumentParser(const std::string &CommandLine)
|
||||
: Input(CommandLine), Position(Input.begin() - 1) {
|
||||
}
|
||||
|
||||
std::vector<std::string> parse() {
|
||||
bool HasMoreInput = true;
|
||||
while (HasMoreInput && nextNonWhitespace()) {
|
||||
std::string Argument;
|
||||
HasMoreInput = parseStringInto(Argument);
|
||||
CommandLine.push_back(Argument);
|
||||
}
|
||||
return CommandLine;
|
||||
}
|
||||
|
||||
private:
|
||||
// All private methods return true if there is more input available.
|
||||
|
||||
bool parseStringInto(std::string &String) {
|
||||
do {
|
||||
if (*Position == '"') {
|
||||
if (!parseDoubleQuotedStringInto(String))
|
||||
return false;
|
||||
} else if (*Position == '\'') {
|
||||
if (!parseSingleQuotedStringInto(String))
|
||||
return false;
|
||||
} else {
|
||||
if (!parseFreeStringInto(String))
|
||||
return false;
|
||||
}
|
||||
} while (*Position != ' ');
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parseDoubleQuotedStringInto(std::string &String) {
|
||||
if (!next())
|
||||
return false;
|
||||
while (*Position != '"') {
|
||||
if (!skipEscapeCharacter())
|
||||
return false;
|
||||
String.push_back(*Position);
|
||||
if (!next())
|
||||
return false;
|
||||
}
|
||||
return next();
|
||||
}
|
||||
|
||||
bool parseSingleQuotedStringInto(std::string &String) {
|
||||
if (!next())
|
||||
return false;
|
||||
while (*Position != '\'') {
|
||||
String.push_back(*Position);
|
||||
if (!next())
|
||||
return false;
|
||||
}
|
||||
return next();
|
||||
}
|
||||
|
||||
bool parseFreeStringInto(std::string &String) {
|
||||
do {
|
||||
if (!skipEscapeCharacter())
|
||||
return false;
|
||||
String.push_back(*Position);
|
||||
if (!next())
|
||||
return false;
|
||||
} while (*Position != ' ' && *Position != '"' && *Position != '\'');
|
||||
return true;
|
||||
}
|
||||
|
||||
bool skipEscapeCharacter() {
|
||||
if (*Position == '\\') {
|
||||
return next();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool nextNonWhitespace() {
|
||||
do {
|
||||
if (!next())
|
||||
return false;
|
||||
} while (*Position == ' ');
|
||||
return true;
|
||||
}
|
||||
|
||||
bool next() {
|
||||
++Position;
|
||||
return Position != Input.end();
|
||||
}
|
||||
|
||||
private:
|
||||
const std::string Input;
|
||||
std::string::const_iterator Position;
|
||||
std::vector<std::string> CommandLine;
|
||||
};
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
std::vector<std::string>
|
||||
unescapeCommandLine(const std::string &EscapedCommandLine) {
|
||||
CommandLineArgumentParser parser(EscapedCommandLine);
|
||||
return parser.parse();
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* \file
|
||||
* \brief Facility to parse a command line into a string array.
|
||||
*
|
||||
* \note Please note that the code borrowed from the Clang,
|
||||
* lib/Tooling/JSONCompilationDatabase.cpp.
|
||||
*
|
||||
* This file is distributed under the GNU General Public License. See
|
||||
* COPYING for details.
|
||||
*/
|
||||
|
||||
#ifndef IRONY_MODE_SERVER_SUPPORT_COMMAND_LINE_PARSER_H_
|
||||
#define IRONY_MODE_SERVER_SUPPORT_COMMAND_LINE_PARSER_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
std::vector<std::string>
|
||||
unescapeCommandLine(const std::string &EscapedCommandLine);
|
||||
|
||||
#endif // IRONY_MODE_SERVER_SUPPORT_COMMAND_LINE_PARSER_H_
|
@@ -0,0 +1,34 @@
|
||||
/**-*-C++-*-
|
||||
* \file
|
||||
* \author Guillaume Papin <guillaume.papin@epitech.eu>
|
||||
*
|
||||
* \brief NonCopyable class like in Boost.
|
||||
*
|
||||
* \see http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Non-copyable_Mixin
|
||||
*
|
||||
* This file is distributed under the GNU General Public License. See
|
||||
* COPYING for details.
|
||||
*/
|
||||
|
||||
#ifndef IRONY_MODE_SERVER_SUPPORT_NONCOPYABLE_H_
|
||||
#define IRONY_MODE_SERVER_SUPPORT_NONCOPYABLE_H_
|
||||
|
||||
namespace util {
|
||||
|
||||
class NonCopyable {
|
||||
protected:
|
||||
NonCopyable() {
|
||||
}
|
||||
|
||||
// Protected non-virtual destructor
|
||||
~NonCopyable() {
|
||||
}
|
||||
|
||||
private:
|
||||
NonCopyable(const NonCopyable &);
|
||||
NonCopyable &operator=(const NonCopyable &);
|
||||
};
|
||||
|
||||
} // ! namespace util
|
||||
|
||||
#endif /* !IRONY_MODE_SERVER_SUPPORT_NONCOPYABLE_H_ */
|
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* \file
|
||||
* \author Guillaume Papin <guillaume.papin@epitech.eu>
|
||||
*
|
||||
* This file is distributed under the GNU General Public License. See
|
||||
* COPYING for details.
|
||||
*/
|
||||
|
||||
#include "TemporaryFile.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
|
||||
static std::string getTemporaryFileDirectory() {
|
||||
const char *temporaryDirEnvVars[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR"};
|
||||
|
||||
for (const char *envVar : temporaryDirEnvVars) {
|
||||
if (const char *dir = std::getenv(envVar))
|
||||
return dir;
|
||||
}
|
||||
|
||||
return "/tmp";
|
||||
}
|
||||
|
||||
TemporaryFile::TemporaryFile(const std::string &prefix,
|
||||
const std::string &suffix)
|
||||
: pathOrPattern_(prefix + "-%%%%%%" + suffix) {
|
||||
}
|
||||
|
||||
TemporaryFile::~TemporaryFile() {
|
||||
if (openedFile_) {
|
||||
openedFile_.reset();
|
||||
std::remove(pathOrPattern_.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
const std::string &TemporaryFile::getPath() {
|
||||
if (!openedFile_) {
|
||||
openedFile_.reset(new std::fstream);
|
||||
|
||||
std::random_device rd;
|
||||
std::default_random_engine e(rd());
|
||||
std::uniform_int_distribution<int> dist(0, 15);
|
||||
std::string pattern = pathOrPattern_;
|
||||
std::string tmpDir = getTemporaryFileDirectory() + "/";
|
||||
int i = 0;
|
||||
|
||||
do {
|
||||
// exiting is better than infinite loop
|
||||
if (++i > TemporaryFile::MAX_ATTEMPS) {
|
||||
std::cerr << "error: couldn't create temporary file, please check your "
|
||||
"temporary file directory (" << tmpDir << ")\n";
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// make the filename based on the pattern
|
||||
std::transform(pattern.begin(),
|
||||
pattern.end(),
|
||||
pathOrPattern_.begin(),
|
||||
[&e, &dist](char ch) {
|
||||
return ch == '%' ? "0123456789abcdef"[dist(e)] : ch;
|
||||
});
|
||||
// create the file
|
||||
openedFile_->open(tmpDir + pathOrPattern_, std::ios_base::out);
|
||||
} while (!openedFile_->is_open());
|
||||
pathOrPattern_ = tmpDir + pathOrPattern_;
|
||||
}
|
||||
|
||||
return pathOrPattern_;
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
/**-*-C++-*-
|
||||
* \file
|
||||
* \author Guillaume Papin <guillaume.papin@epitech.eu>
|
||||
*
|
||||
* \brief Not the best piece of code out there.
|
||||
*
|
||||
* This file is distributed under the GNU General Public License. See
|
||||
* COPYING for details.
|
||||
*/
|
||||
|
||||
#ifndef IRONY_MODE_SERVER_SUPPORT_TEMPORARY_FILE_H_
|
||||
#define IRONY_MODE_SERVER_SUPPORT_TEMPORARY_FILE_H_
|
||||
|
||||
#include <iosfwd>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
class TemporaryFile {
|
||||
enum {
|
||||
// if we can't create the temp file, exits.
|
||||
MAX_ATTEMPS = 25
|
||||
};
|
||||
|
||||
public:
|
||||
TemporaryFile(const std::string &prefix, const std::string &suffix = "");
|
||||
~TemporaryFile();
|
||||
|
||||
/// Returns the path of this temporary filename
|
||||
const std::string &getPath();
|
||||
|
||||
private:
|
||||
std::string pathOrPattern_;
|
||||
std::unique_ptr<std::fstream> openedFile_;
|
||||
};
|
||||
|
||||
#endif // IRONY_MODE_SERVER_SUPPORT_TEMPORARY_FILE_H_
|
@@ -0,0 +1,39 @@
|
||||
/**-*-C++-*-
|
||||
* \file
|
||||
* \author Guillaume Papin <guillaume.papin@epitech.eu>
|
||||
*
|
||||
* \brief \c arraysize() and \c ARRAYSIZE_UNSAFE() definitions.
|
||||
*
|
||||
* This file is distributed under the GNU General Public License. See
|
||||
* COPYING for details.
|
||||
*/
|
||||
|
||||
#ifndef IRONY_MODE_SERVER_SUPPORT_ARRAYSIZE_H_
|
||||
#define IRONY_MODE_SERVER_SUPPORT_ARRAYSIZE_H_
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
char (&ArraySizeHelper(T (&array)[N]))[N];
|
||||
|
||||
/// \brief Convenience macro to get the size of an array.
|
||||
///
|
||||
/// \note Found in an article about the Chromium project, see
|
||||
/// http://software.intel.com/en-us/articles/pvs-studio-vs-chromium and
|
||||
/// http://codesearch.google.com/codesearch/p?hl=en#OAMlx_jo-ck/src/base/basictypes.h&q=arraysize&exact_package=chromium
|
||||
///
|
||||
#define arraysize(array) (sizeof(ArraySizeHelper(array)))
|
||||
|
||||
/// \brief \c arraysize() 'unsafe' version to use when the \c arraysize() macro
|
||||
/// can't be used.
|
||||
///
|
||||
/// The \c arraysize() macro can't be used on an anonymous structure array for
|
||||
/// example.
|
||||
///
|
||||
/// \note Be careful to check that the array passed is not just a pointer.
|
||||
///
|
||||
#define ARRAYSIZE_UNSAFE(a) \
|
||||
((sizeof(a) / sizeof(*(a))) / \
|
||||
static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
|
||||
|
||||
#endif /* !IRONY_MODE_SERVER_SUPPORT_ARRAYSIZE_H_ */
|
@@ -0,0 +1,52 @@
|
||||
/**-*-C++-*-
|
||||
* \file
|
||||
* \brief Dumb implementation of something that might look like C++14
|
||||
* std::quoted.
|
||||
*
|
||||
* This file is distributed under the GNU General Public License. See
|
||||
* COPYING for details.
|
||||
*/
|
||||
|
||||
#ifndef IRONY_MODE_SERVER_SUPPORT_IOMANIP_QUOTED_H_
|
||||
#define IRONY_MODE_SERVER_SUPPORT_IOMANIP_QUOTED_H_
|
||||
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
|
||||
namespace support {
|
||||
namespace detail {
|
||||
|
||||
struct quoted_string_proxy {
|
||||
quoted_string_proxy(const std::string &s) : s(s) {
|
||||
}
|
||||
|
||||
std::string s;
|
||||
};
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, const quoted_string_proxy &q) {
|
||||
const std::string &s = q.s;
|
||||
|
||||
os << '"';
|
||||
if (s.find_first_of("\"\\") == std::string::npos) {
|
||||
os << s;
|
||||
} else {
|
||||
for (auto ch : s) {
|
||||
if (ch == '\\' || ch == '"')
|
||||
os << '\\';
|
||||
|
||||
os << ch;
|
||||
}
|
||||
}
|
||||
os << '"';
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
detail::quoted_string_proxy quoted(const std::string &s) {
|
||||
return detail::quoted_string_proxy(s);
|
||||
}
|
||||
|
||||
} // namespace support
|
||||
|
||||
#endif // IRONY_MODE_SERVER_SUPPORT_IOMANIP_QUOTED_H_
|
Reference in New Issue
Block a user