std::vector<CompileCommand> JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const { llvm::SmallString<128> NativeFilePath; llvm::sys::path::native(FilePath, NativeFilePath); std::vector<StringRef> PossibleMatches; std::string Error; llvm::raw_string_ostream ES(Error); StringRef Match = MatchTrie.findEquivalent(NativeFilePath.str(), ES); if (Match.empty()) { if (Error.empty()) Error = "No match found."; llvm::outs() << Error << "\n"; return std::vector<CompileCommand>(); } llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator CommandsRefI = IndexByFile.find(Match); if (CommandsRefI == IndexByFile.end()) return std::vector<CompileCommand>(); const std::vector<CompileCommandRef> &CommandsRef = CommandsRefI->getValue(); std::vector<CompileCommand> Commands; for (int I = 0, E = CommandsRef.size(); I != E; ++I) { llvm::SmallString<8> DirectoryStorage; llvm::SmallString<1024> CommandStorage; Commands.push_back(CompileCommand( // FIXME: Escape correctly: CommandsRef[I].first->getValue(DirectoryStorage), unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage)))); } return Commands; }
std::vector<std::string> nextCommand() { std::string line; if (std::getline(std::cin, line)) { return unescapeCommandLine(line); } return std::vector<std::string>(1, "exit"); }
std::vector<CompileCommand> JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const { llvm::SmallString<128> NativeFilePath; llvm::sys::path::native(FilePath, NativeFilePath); llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator CommandsRefI = IndexByFile.find(NativeFilePath); if (CommandsRefI == IndexByFile.end()) return std::vector<CompileCommand>(); const std::vector<CompileCommandRef> &CommandsRef = CommandsRefI->getValue(); std::vector<CompileCommand> Commands; for (int I = 0, E = CommandsRef.size(); I != E; ++I) { llvm::SmallString<8> DirectoryStorage; llvm::SmallString<1024> CommandStorage; Commands.push_back(CompileCommand( // FIXME: Escape correctly: CommandsRef[I].first->getValue(DirectoryStorage), unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage)))); } return Commands; }
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_; }