# # # patch "selectors.cc" # from [1bc409d04ba133812a530ec4aa12bc8d0761cd54] # to [87546db6679852f6f0bae526ca9a53c05185c8d6] # ============================================================ --- selectors.cc 1bc409d04ba133812a530ec4aa12bc8d0761cd54 +++ selectors.cc 87546db6679852f6f0bae526ca9a53c05185c8d6 @@ -713,7 +713,8 @@ shared_ptr selector::create(op { // just parentheses for grouping E(lparen_pos == 3 && items[items.size() - 2].sel, origin::user, - F("selector '%s' is invalid, grouping parentheses contain something that doesn't look like an expr") % orig); + F("selector '%s' is invalid, grouping parentheses contain something that " + "doesn't look like an expr") % orig); shared_ptr to_add(new nested_selector(items[items.size() - 2].sel)); items.pop_back(); items.pop_back(); @@ -804,390 +805,7 @@ shared_ptr selector::create(op return items[0].sel; } -enum selector_type - { - sel_author, - sel_branch, - sel_head, - sel_any_head, - sel_date, - sel_tag, - sel_ident, - sel_cert, - sel_earlier, - sel_later, - sel_message, - sel_parent, - sel_update, - sel_base, - sel_unknown - }; -typedef vector > selector_list; - -static void -decode_selector(options const & opts, lua_hooks & lua, - project_t & project, - string const & orig_sel, - selector_type & type, - string & sel) -{ - sel = orig_sel; - - L(FL("decoding selector '%s'") % sel); - - string tmp; - if (sel.size() < 2 || sel[1] != ':') - { - if (!lua.hook_expand_selector(sel, tmp)) - { - L(FL("expansion of selector '%s' failed") % sel); - } - else - { - P(F("expanded selector '%s' -> '%s'") % sel % tmp); - sel = tmp; - } - } - - if (sel.size() >= 2 && sel[1] == ':') - { - switch (sel[0]) - { - case 'a': - type = sel_author; - break; - case 'b': - type = sel_branch; - break; - case 'h': - type = opts.ignore_suspend_certs ? sel_any_head : sel_head; - break; - case 'd': - type = sel_date; - break; - case 'i': - type = sel_ident; - break; - case 't': - type = sel_tag; - break; - case 'c': - type = sel_cert; - break; - case 'l': - type = sel_later; - break; - case 'e': - type = sel_earlier; - break; - case 'm': - type = sel_message; - break; - case 'p': - type = sel_parent; - break; - case 'u': - type = sel_update; - break; - case 'w': - type = sel_base; - break; - default: - E(false, origin::user, F("unknown selector type: %c") % sel[0]); - } - sel.erase(0,2); - - // validate certain selector values and provide defaults - switch (type) - { - case sel_date: - case sel_later: - case sel_earlier: - if (lua.hook_exists("expand_date")) - { - E(lua.hook_expand_date(sel, tmp), origin::user, - F("selector '%s' is not a valid date\n") % sel); - } - else - { - // if expand_date is not available, start with something - tmp = sel; - } - - // if we still have a too short datetime string, expand it with - // default values, but only if the type is earlier or later; - // for searching a specific date cert this makes no sense - // FIXME: this is highly speculative if expand_date wasn't called - // beforehand - tmp could be _anything_ but a partial date string - if (tmp.size()<8 && (sel_later==type || sel_earlier==type)) - tmp += "-01T00:00:00"; - else if (tmp.size()<11 && (sel_later==type || sel_earlier==type)) - tmp += "T00:00:00"; - E(tmp.size()==19 || sel_date==type, origin::user, - F("selector '%s' is not a valid date (%s)") % sel % tmp); - - if (sel != tmp) - { - P (F ("expanded date '%s' -> '%s'\n") % sel % tmp); - sel = tmp; - } - if (sel_date == type && sel.size() < 19) - sel = string("*") + sel + "*"; // to be GLOBbed later - break; - - case sel_branch: - case sel_head: - case sel_any_head: - if (sel.empty()) - { - i18n_format msg = sel_branch == type - ? F("the empty branch selector b: refers to " - "the current branch") - : F("the empty head selector h: refers to " - "the head of the current branch"); - workspace::require_workspace(msg); - sel = opts.branch(); - } - break; - - case sel_cert: - E(!sel.empty(), origin::user, - F("the cert selector c: may not be empty")); - break; - - case sel_parent: - if (sel.empty()) - { - workspace work(lua, F("the empty parent selector p: refers to " - "the base revision of the workspace")); - - parent_map parents; - set parent_ids; - - work.get_parent_rosters(project.db, parents); - - for (parent_map::const_iterator i = parents.begin(); - i != parents.end(); ++i) - { - parent_ids.insert(i->first); - } - - diagnose_ambiguous_expansion(opts, lua, project, "p:", parent_ids); - sel = encode_hexenc((* parent_ids.begin()).inner()(), - origin::internal); - } - break; - case sel_update: - E(sel.empty(), origin::user, - F("no value is allowed with the update selector u:")); - { - workspace work(lua, F("the update selector u: refers to the " - "revision before the last update in the " - "workspace")); - revision_id update_id; - work.get_update_id(update_id); - sel = encode_hexenc(update_id.inner()(), origin::internal); - } - break; - case sel_base: - E(sel.empty(), origin::user, - F("no value is allowed with the base revision selector w:")); - break; - - default: break; - } - } -} - -static void -parse_selector(options const & opts, lua_hooks & lua, - project_t & project, - string const & str, selector_list & sels) -{ - sels.clear(); - - // this rule should always be enabled, even if the user specifies - // --norc: if you provide a revision id, you get a revision id. - if (str.find_first_not_of(constants::legal_id_bytes) == string::npos - && str.size() == constants::idlen) - { - sels.push_back(make_pair(sel_ident, str)); - } - else - { - typedef boost::tokenizer > tokenizer; - boost::escaped_list_separator slash("\\", "/", ""); - tokenizer tokens(str, slash); - - vector selector_strings; - copy(tokens.begin(), tokens.end(), back_inserter(selector_strings)); - - for (vector::const_iterator i = selector_strings.begin(); - i != selector_strings.end(); ++i) - { - string sel; - selector_type type = sel_unknown; - - decode_selector(opts, lua, project, *i, type, sel); - sels.push_back(make_pair(type, sel)); - } - } -} - -static void -complete_one_selector(options const & opts, lua_hooks & lua, - project_t & project, - selector_type ty, string const & value, - set & completions) -{ - switch (ty) - { - case sel_ident: - project.db.complete(value, completions); - break; - - case sel_parent: - I(!value.empty()); - project.db.select_parent(value, completions); - break; - - case sel_update: - project.db.complete(value, completions); - break; - - case sel_author: - project.db.select_cert(author_cert_name(), value, completions); - break; - - case sel_tag: - project.db.select_cert(tag_cert_name(), value, completions); - break; - - case sel_branch: - I(!value.empty()); - project.db.select_cert(branch_cert_name(), value, completions); - break; - - case sel_unknown: - project.db.select_author_tag_or_branch(value, completions); - break; - - case sel_date: - project.db.select_date(value, "GLOB", completions); - break; - - case sel_earlier: - project.db.select_date(value, "<=", completions); - break; - - case sel_later: - project.db.select_date(value, ">", completions); - break; - - case sel_message: - { - set changelogs, comments; - project.db.select_cert(changelog_cert_name(), value, changelogs); - project.db.select_cert(comment_cert_name(), value, comments); - completions.insert(changelogs.begin(), changelogs.end()); - completions.insert(comments.begin(), comments.end()); - } - break; - - case sel_cert: - { - I(!value.empty()); - size_t spot = value.find("="); - - if (spot != (size_t)-1) - { - string certname; - string certvalue; - - certname = value.substr(0, spot); - spot++; - certvalue = value.substr(spot); - - project.db.select_cert(certname, certvalue, completions); - } - else - project.db.select_cert(value, completions); - } - break; - - case sel_head: - case sel_any_head: - { - // get branch names - set branch_names; - I(!value.empty()); - project.get_branch_list(globish(value, origin::user), branch_names); - - L(FL("found %d matching branches") % branch_names.size()); - - // for each branch name, get the branch heads - for (set::const_iterator bn = branch_names.begin(); - bn != branch_names.end(); bn++) - { - set branch_heads; - project.get_branch_heads(*bn, branch_heads, ty == sel_any_head); - completions.insert(branch_heads.begin(), branch_heads.end()); - L(FL("after get_branch_heads for %s, heads has %d entries") - % (*bn) % completions.size()); - } - } - break; - - case sel_base: - { - workspace work(lua, F("the selector w: returns the " - "base revision(s) of the workspace")); - parent_map parents; - work.get_parent_rosters(project.db, parents); - - for (parent_map::const_iterator i = parents.begin(); - i != parents.end(); ++i) - { - completions.insert(i->first); - } - } - break; - } -} - -static void -complete_selector(options const & opts, lua_hooks & lua, - project_t & project, - selector_list const & limit, - set & completions) -{ - if (limit.empty()) // all the ids in the database - { - project.db.complete("", completions); - return; - } - - selector_list::const_iterator i = limit.begin(); - complete_one_selector(opts, lua, project, i->first, i->second, completions); - i++; - - while (i != limit.end()) - { - set candidates; - set intersection; - complete_one_selector(opts, lua, project, i->first, i->second, candidates); - - intersection.clear(); - set_intersection(completions.begin(), completions.end(), - candidates.begin(), candidates.end(), - inserter(intersection, intersection.end())); - - completions = intersection; - i++; - } -} - void complete(options const & opts, lua_hooks & lua, project_t & project,