1. For targets, when invoking ninja to build a target.
2. For targets, when doing a "query" command.
3. For command names.
4. For the subcommands of the "targets" command.
Also change CmdTargets() to call LookupNode() instead of GetNode() --
since the result was checked for NULL, that's probably what was intended
here originally.
#include <vector>
-#include "string_piece.h"
-
int EditDistance(const StringPiece& s1,
const StringPiece& s2,
bool allow_replacements,
#ifndef NINJA_EDIT_DISTANCE_H_
#define NINJA_EDIT_DISTANCE_H_
-struct StringPiece;
+#include "string_piece.h"
int EditDistance(const StringPiece& s1,
const StringPiece& s2,
#include "edit_distance.h"
-#include "string_piece.h"
#include "test.h"
TEST(EditDistanceTest, TestEmpty) {
#include "build.h"
#include "build_log.h"
#include "clean.h"
+#include "edit_distance.h"
#include "graph.h"
#include "graphviz.h"
#include "parsers.h"
targets->push_back(node);
} else {
*err = "unknown target '" + path + "'";
+
+ Node* suggestion = state->SpellcheckNode(path);
+ if (suggestion) {
+ *err += ", did you mean '" + suggestion->file_->path_ + "'?";
+ }
return false;
}
}
return 1;
}
for (int i = 0; i < argc; ++i) {
- Node* node = state->GetNode(argv[i]);
+ Node* node = state->LookupNode(argv[i]);
if (node) {
printf("%s:\n", argv[i]);
if (node->in_edge_) {
}
}
} else {
- printf("%s unknown\n", argv[i]);
+ Node* suggestion = state->SpellcheckNode(argv[i]);
+ if (suggestion) {
+ printf("%s unknown, did you mean %s?\n",
+ argv[i], suggestion->file_->path_.c_str());
+ } else {
+ printf("%s unknown\n", argv[i]);
+ }
return 1;
}
}
} else if (mode == "all") {
return CmdTargetsList(state);
} else {
- Error("unknown target tool mode '%s'", mode.c_str());
+ const char* suggestion =
+ SpellcheckString(mode, "rule", "depth", "all", NULL);
+ if (suggestion) {
+ Error("unknown target tool mode '%s', did you mean '%s'?",
+ mode.c_str(), suggestion);
+ } else {
+ Error("unknown target tool mode '%s'", mode.c_str());
+ }
return 1;
}
}
// the tool, i.e. "clean".
if (tool == "clean")
return CmdClean(&state, argc+1, argv-1, config);
- Error("unknown tool '%s'", tool.c_str());
+
+ const char* suggestion = SpellcheckString(tool,
+ "graph", "query", "browse", "targets", "rules", "commands", NULL);
+ if (suggestion) {
+ Error("unknown tool '%s', did you mean '%s'?", tool.c_str(), suggestion);
+ } else {
+ Error("unknown tool '%s'", tool.c_str());
+ }
}
BuildLog build_log;
#include <stdio.h>
+#include "edit_distance.h"
#include "graph.h"
FileStat* StatCache::GetFile(const std::string& path) {
return file;
}
+FileStat* StatCache::SpellcheckFile(const std::string& path) {
+ const bool kAllowReplacements = true;
+ const int kMaxValidEditDistance = 3;
+
+ int min_distance = kMaxValidEditDistance + 1;
+ FileStat* result = NULL;
+ for (Paths::iterator i = paths_.begin(); i != paths_.end(); ++i) {
+ int distance = EditDistance(
+ i->first, path, kAllowReplacements, kMaxValidEditDistance);
+ if (distance < min_distance && i->second->node_) {
+ min_distance = distance;
+ result = i->second;
+ }
+ }
+ return result;
+}
+
void StatCache::Dump() {
for (Paths::iterator i = paths_.begin(); i != paths_.end(); ++i) {
FileStat* file = i->second;
/// Mapping of path -> FileStat.
struct StatCache {
FileStat* GetFile(const std::string& path);
+ FileStat* SpellcheckFile(const std::string& path);
/// Dump the mapping to stdout (useful for debugging).
void Dump();
return file->node_;
}
+Node* State::SpellcheckNode(const string& path) {
+ FileStat* file = stat_cache_.SpellcheckFile(path);
+ if (!file || !file->node_)
+ return NULL;
+ return file->node_;
+}
+
void State::AddIn(Edge* edge, const string& path) {
Node* node = GetNode(path);
edge->inputs_.push_back(node);
Edge* AddEdge(const Rule* rule);
Node* GetNode(const string& path);
Node* LookupNode(const string& path);
+ Node* SpellcheckNode(const string& path);
void AddIn(Edge* edge, const string& path);
void AddOut(Edge* edge, const string& path);
bool AddDefault(const string& path, string* error);
#include <direct.h> // _mkdir
#endif
+#include "edit_distance.h"
+
void Fatal(const char* msg, ...) {
va_list ap;
fprintf(stderr, "ninja: FATAL: ");
return ((int64_t)now.tv_sec * 1000) + (now.tv_usec / 1000);
#endif
}
+
+const char* SpellcheckString(const string& text, ...) {
+ const bool kAllowReplacements = true;
+ const int kMaxValidEditDistance = 3;
+
+ va_list ap;
+ va_start(ap, text);
+ const char* correct_spelling;
+
+ int min_distance = kMaxValidEditDistance + 1;
+ const char* result = NULL;
+ while ((correct_spelling = va_arg(ap, const char*))) {
+ int distance = EditDistance(
+ correct_spelling, text, kAllowReplacements, kMaxValidEditDistance);
+ if (distance < min_distance) {
+ min_distance = distance;
+ result = correct_spelling;
+ }
+ }
+
+ va_end(ap);
+ return result;
+}
/// time.
int64_t GetTimeMillis();
+/// Given a misspelled string and a NULL-terminatd list of correct spellings,
+/// returns the closest match or NULL if there is no close enough match.
+const char* SpellcheckString(const string& text, ...);
+
#ifdef _WIN32
#define snprintf _snprintf
#endif