OLD | NEW |
---|---|
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <algorithm> | 5 #include <algorithm> |
6 #include <iostream> | 6 #include <iostream> |
7 | 7 |
8 #include "base/command_line.h" | 8 #include "base/command_line.h" |
9 #include "tools/gn/args.h" | 9 #include "tools/gn/args.h" |
10 #include "tools/gn/commands.h" | 10 #include "tools/gn/commands.h" |
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
136 bool PrintHelpOnSwitch(const std::string& what) { | 136 bool PrintHelpOnSwitch(const std::string& what) { |
137 const switches::SwitchInfoMap& all = switches::GetSwitches(); | 137 const switches::SwitchInfoMap& all = switches::GetSwitches(); |
138 switches::SwitchInfoMap::const_iterator found = | 138 switches::SwitchInfoMap::const_iterator found = |
139 all.find(base::StringPiece(what)); | 139 all.find(base::StringPiece(what)); |
140 if (found == all.end()) | 140 if (found == all.end()) |
141 return false; | 141 return false; |
142 PrintLongHelp(found->second.long_help); | 142 PrintLongHelp(found->second.long_help); |
143 return true; | 143 return true; |
144 } | 144 } |
145 | 145 |
146 size_t EditDistance(const base::StringPiece& s1, | |
brettw
2016/02/16 20:48:28
This is exciting, I've been wanting to add spellch
| |
147 const base::StringPiece& s2, | |
148 size_t max_edit_distance) { | |
149 // The algorithm implemented below is the "classic" | |
150 // dynamic-programming algorithm for computing the Levenshtein | |
151 // distance, which is described here: | |
152 // | |
153 // http://en.wikipedia.org/wiki/Levenshtein_distance | |
154 // | |
155 // Although the algorithm is typically described using an m x n | |
156 // array, only one row plus one element are used at a time, so this | |
157 // implementation just keeps one vector for the row. To update one entry, | |
158 // only the entries to the left, top, and top-left are needed. The left | |
159 // entry is in row[x-1], the top entry is what's in row[x] from the last | |
160 // iteration, and the top-left entry is stored in previous. | |
161 size_t m = s1.size(); | |
162 size_t n = s2.size(); | |
163 | |
164 std::vector<size_t> row(n + 1); | |
165 for (size_t i = 1; i <= n; ++i) | |
166 row[i] = i; | |
167 | |
168 for (size_t y = 1; y <= m; ++y) { | |
169 row[0] = y; | |
170 size_t best_this_row = row[0]; | |
171 | |
172 size_t previous = y - 1; | |
173 for (size_t x = 1; x <= n; ++x) { | |
174 size_t old_row = row[x]; | |
175 row[x] = std::min(previous + (s1[y - 1] == s2[x - 1] ? 0u : 1u), | |
176 std::min(row[x - 1], row[x]) + 1u); | |
177 previous = old_row; | |
178 best_this_row = std::min(best_this_row, row[x]); | |
179 } | |
180 | |
181 if (max_edit_distance && best_this_row > max_edit_distance) | |
182 return max_edit_distance + 1; | |
183 } | |
184 | |
185 return row[n]; | |
186 } | |
187 | |
188 base::StringPiece SpellcheckString( | |
189 const std::string& text, | |
190 const std::vector<base::StringPiece>& words) { | |
191 const size_t kMaxValidEditDistance = 3u; | |
192 | |
193 size_t min_distance = kMaxValidEditDistance + 1u; | |
194 base::StringPiece result; | |
195 for (base::StringPiece word : words) { | |
196 size_t distance = EditDistance(word, text, kMaxValidEditDistance); | |
197 if (distance < min_distance) { | |
198 min_distance = distance; | |
199 result = word; | |
200 } | |
201 } | |
202 return result; | |
203 } | |
204 | |
146 } // namespace | 205 } // namespace |
147 | 206 |
148 const char kHelp[] = "help"; | 207 const char kHelp[] = "help"; |
149 const char kHelp_HelpShort[] = | 208 const char kHelp_HelpShort[] = |
150 "help: Does what you think."; | 209 "help: Does what you think."; |
151 const char kHelp_Help[] = | 210 const char kHelp_Help[] = |
152 "gn help <anything>\n" | 211 "gn help <anything>\n" |
153 " Yo dawg, I heard you like help on your help so I put help on the help\n" | 212 " Yo dawg, I heard you like help on your help so I put help on the help\n" |
154 " in the help.\n"; | 213 " in the help.\n"; |
155 | 214 |
(...skipping 13 matching lines...) Expand all Loading... | |
169 // Switch help needs to be done separately. The CommandLine will strip the | 228 // Switch help needs to be done separately. The CommandLine will strip the |
170 // switch separators so --args will come out as "args" which is then | 229 // switch separators so --args will come out as "args" which is then |
171 // ambiguous with the variable named "args". | 230 // ambiguous with the variable named "args". |
172 if (!PrintHelpOnSwitch(switches.begin()->first)) | 231 if (!PrintHelpOnSwitch(switches.begin()->first)) |
173 PrintToplevelHelp(); | 232 PrintToplevelHelp(); |
174 return 0; | 233 return 0; |
175 } else { | 234 } else { |
176 what = args[0]; | 235 what = args[0]; |
177 } | 236 } |
178 | 237 |
238 std::vector<base::StringPiece> all_help_topics; | |
239 | |
179 // Check commands. | 240 // Check commands. |
180 const commands::CommandInfoMap& command_map = commands::GetCommands(); | 241 const commands::CommandInfoMap& command_map = commands::GetCommands(); |
181 commands::CommandInfoMap::const_iterator found_command = | 242 auto found_command = command_map.find(what); |
182 command_map.find(what); | |
183 if (found_command != command_map.end()) { | 243 if (found_command != command_map.end()) { |
184 PrintLongHelp(found_command->second.help); | 244 PrintLongHelp(found_command->second.help); |
185 return 0; | 245 return 0; |
186 } | 246 } |
247 for (const auto& entry : command_map) | |
248 all_help_topics.push_back(entry.first); | |
187 | 249 |
188 // Check functions. | 250 // Check functions. |
189 const functions::FunctionInfoMap& function_map = functions::GetFunctions(); | 251 const functions::FunctionInfoMap& function_map = functions::GetFunctions(); |
190 functions::FunctionInfoMap::const_iterator found_function = | 252 auto found_function = function_map.find(what); |
191 function_map.find(what); | |
192 if (found_function != function_map.end()) { | 253 if (found_function != function_map.end()) { |
193 PrintLongHelp(found_function->second.help); | 254 PrintLongHelp(found_function->second.help); |
194 return 0; | 255 return 0; |
195 } | 256 } |
257 for (const auto& entry : function_map) | |
258 all_help_topics.push_back(entry.first); | |
196 | 259 |
197 // Builtin variables. | 260 // Builtin variables. |
198 const variables::VariableInfoMap& builtin_vars = | 261 const variables::VariableInfoMap& builtin_vars = |
199 variables::GetBuiltinVariables(); | 262 variables::GetBuiltinVariables(); |
200 variables::VariableInfoMap::const_iterator found_builtin_var = | 263 auto found_builtin_var = builtin_vars.find(what); |
201 builtin_vars.find(what); | |
202 if (found_builtin_var != builtin_vars.end()) { | 264 if (found_builtin_var != builtin_vars.end()) { |
203 PrintLongHelp(found_builtin_var->second.help); | 265 PrintLongHelp(found_builtin_var->second.help); |
204 return 0; | 266 return 0; |
205 } | 267 } |
268 for (const auto& entry : builtin_vars) | |
269 all_help_topics.push_back(entry.first); | |
206 | 270 |
207 // Target variables. | 271 // Target variables. |
208 const variables::VariableInfoMap& target_vars = | 272 const variables::VariableInfoMap& target_vars = |
209 variables::GetTargetVariables(); | 273 variables::GetTargetVariables(); |
210 variables::VariableInfoMap::const_iterator found_target_var = | 274 auto found_target_var = target_vars.find(what); |
211 target_vars.find(what); | |
212 if (found_target_var != target_vars.end()) { | 275 if (found_target_var != target_vars.end()) { |
213 PrintLongHelp(found_target_var->second.help); | 276 PrintLongHelp(found_target_var->second.help); |
214 return 0; | 277 return 0; |
215 } | 278 } |
279 for (const auto& entry : target_vars) | |
280 all_help_topics.push_back(entry.first); | |
216 | 281 |
217 // Random other topics. | 282 // Random other topics. |
218 if (what == "all") { | 283 std::map<std::string, std::function<void()>> random_topics; |
219 PrintAllHelp(); | 284 random_topics["all"] = PrintAllHelp; |
285 random_topics["buildargs"] = [=]() { PrintLongHelp(kBuildArgs_Help); }; | |
286 random_topics["dotfile"] = [=]() { PrintLongHelp(kDotfile_Help); }; | |
287 random_topics["grammar"] = [=]() { PrintLongHelp(kGrammar_Help); }; | |
288 random_topics["input_conversion"] = [=]() { | |
289 PrintLongHelp(kInputConversion_Help); | |
290 }; | |
291 random_topics["label_pattern"] = [=]() { PrintLongHelp(kLabelPattern_Help); }; | |
292 random_topics["runtime_deps"] = [=]() { PrintLongHelp(kRuntimeDeps_Help); }; | |
293 random_topics["source_expansion"] = [=]() { | |
294 PrintLongHelp(kSourceExpansion_Help); | |
295 }; | |
296 random_topics["switches"] = PrintSwitchHelp; | |
297 auto found_random_topic = random_topics.find(what); | |
298 if (found_random_topic != random_topics.end()) { | |
299 found_random_topic->second(); | |
220 return 0; | 300 return 0; |
221 } | 301 } |
222 if (what == "buildargs") { | 302 for (const auto& entry : random_topics) |
223 PrintLongHelp(kBuildArgs_Help); | 303 all_help_topics.push_back(entry.first); |
224 return 0; | |
225 } | |
226 if (what == "dotfile") { | |
227 PrintLongHelp(kDotfile_Help); | |
228 return 0; | |
229 } | |
230 if (what == "grammar") { | |
231 PrintLongHelp(kGrammar_Help); | |
232 return 0; | |
233 } | |
234 if (what == "input_conversion") { | |
235 PrintLongHelp(kInputConversion_Help); | |
236 return 0; | |
237 } | |
238 if (what == "label_pattern") { | |
239 PrintLongHelp(kLabelPattern_Help); | |
240 return 0; | |
241 } | |
242 if (what == "runtime_deps") { | |
243 PrintLongHelp(kRuntimeDeps_Help); | |
244 return 0; | |
245 } | |
246 if (what == "source_expansion") { | |
247 PrintLongHelp(kSourceExpansion_Help); | |
248 return 0; | |
249 } | |
250 if (what == "switches") { | |
251 PrintSwitchHelp(); | |
252 return 0; | |
253 } | |
254 | 304 |
255 // No help on this. | 305 // No help on this. |
256 Err(Location(), "No help on \"" + what + "\".").PrintToStdout(); | 306 Err(Location(), "No help on \"" + what + "\".").PrintToStdout(); |
257 RunHelp(std::vector<std::string>()); | 307 base::StringPiece suggestion = SpellcheckString(what, all_help_topics); |
308 if (suggestion.empty()) { | |
309 OutputString("Run `gn help` for a list of available topics.\n", | |
310 DECORATION_NONE); | |
311 } else { | |
312 OutputString("Did you mean `gn help " + suggestion.as_string() + "`?\n", | |
313 DECORATION_NONE); | |
314 } | |
258 return 1; | 315 return 1; |
259 } | 316 } |
260 | 317 |
261 } // namespace commands | 318 } // namespace commands |
OLD | NEW |