parseCmd tokenizes the text following "lark-cli" into leading command words (the subcommand path, up to the first flag) and flag tokens. It stops at a shell separator (standalone or glued), an inline-code backtick, a comment, or a placeholder/prose word. ok=false filters out non-commands.
(after string)
| 91 | // shell separator (standalone or glued), an inline-code backtick, a comment, or |
| 92 | // a placeholder/prose word. ok=false filters out non-commands. |
| 93 | func parseCmd(after string) (words, flags []string, raw string, ok bool) { |
| 94 | // An inline code span ends at the next backtick; a command never spans one. |
| 95 | if i := strings.IndexByte(after, '`'); i >= 0 { |
| 96 | after = after[:i] |
| 97 | } |
| 98 | // Drop $(...) command substitutions so flags belonging to the inner command |
| 99 | // (e.g. `--data "$(jq -n --arg x ...)"`) are not mistaken for lark-cli flags. |
| 100 | after = stripCmdSubst(after) |
| 101 | |
| 102 | var kept []string |
| 103 | inFlags := false |
| 104 | for _, orig := range strings.Fields(after) { |
| 105 | tok := orig |
| 106 | if shellStops[tok] || strings.HasPrefix(tok, "#") { |
| 107 | break |
| 108 | } |
| 109 | // A shell separator glued to a token ends the command mid-token |
| 110 | // ("get;", "foo|next"): keep the part before it, handle it, then stop. |
| 111 | stop := false |
| 112 | if i := strings.IndexAny(tok, ";|"); i >= 0 { |
| 113 | tok, stop = tok[:i], true |
| 114 | } |
| 115 | switch { |
| 116 | case tok == "" || tok == "-": |
| 117 | // empty (after a glued separator) or a bare stdin marker — skip |
| 118 | case strings.HasPrefix(tok, "-"): |
| 119 | if f := normalizeFlag(tok); f != "" { |
| 120 | inFlags = true |
| 121 | flags = append(flags, f) |
| 122 | kept = append(kept, tok) |
| 123 | } |
| 124 | case inFlags: |
| 125 | // positional / flag value after the first flag — not a command word |
| 126 | kept = append(kept, tok) |
| 127 | default: |
| 128 | // Command-path word. ASCII placeholder markers (<x>, [x], {x|y}, |
| 129 | // +<verb>, ...) end the command — checked on the RAW token so the |
| 130 | // trailing-punct stripping below cannot erase a "..." ellipsis |
| 131 | // ("base +..." must stay a placeholder, not become "+"). |
| 132 | if strings.ContainsAny(tok, "<>[]{}|") || strings.Contains(tok, "...") { |
| 133 | stop = true |
| 134 | break |
| 135 | } |
| 136 | // Strip trailing sentence/CJK punctuation so "login." / "login," |
| 137 | // resolve to "login"; non-ASCII narration ends the command. |
| 138 | w := strings.TrimRight(tok, wordTrailPunct) |
| 139 | if w == "" || hasNonASCII(w) { |
| 140 | stop = true |
| 141 | break |
| 142 | } |
| 143 | words = append(words, w) |
| 144 | kept = append(kept, tok) |
| 145 | } |
| 146 | if stop { |
| 147 | break |
| 148 | } |
| 149 | } |
| 150 | if len(kept) > 0 { |
no test coverage detected