Initial commit.

This commit is contained in:
2016-02-18 14:53:30 +01:00
commit 8e93ca7a95
2215 changed files with 341269 additions and 0 deletions

View 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)

View 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_;
}

View 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_

View 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

View 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
}

View 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_

View 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;
}
}
}

View 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_ */

View 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;
}

View File

@@ -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_ */

View File

@@ -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();
}

View File

@@ -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_

View File

@@ -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_ */

View File

@@ -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_;
}

View File

@@ -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_

View File

@@ -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_ */

View File

@@ -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_