diff --git a/discord/ext/commands/view.py b/discord/ext/commands/view.py index acde0b75b..65f96e18f 100644 --- a/discord/ext/commands/view.py +++ b/discord/ext/commands/view.py @@ -108,13 +108,42 @@ class StringView: # Parser +# map from opening quotes to closing quotes +_quotes = { + '"': '"', + "'": "'", + "«": "»", + "‘": "’", + "‚": "‛", + "“": "”", + "„": "‟", + "‹": "›", + "⹂": "⹂", + "「": "」", + "『": "』", + "〝": "〞", + "﹁": "﹂", + "﹃": "﹄", + """: """, + "'": "'", + "「": "」", + "«": "»", + "‹": "›", + "《": "》", + "〈": "〉", +} +_all_quotes = set(_quotes.keys()) | set(_quotes.values()) + def quoted_word(view): current = view.current if current is None: return None - is_quoted = current == '"' + close_quote = _quotes.get(current) + is_quoted = bool(close_quote) + if is_quoted: + open_quote = current result = [] if is_quoted else [current] while not view.eof: @@ -122,7 +151,7 @@ def quoted_word(view): if not current: if is_quoted: # unexpected EOF - raise BadArgument('Expected closing "') + raise BadArgument('Expected closing {}.'.format(close_quote)) return ''.join(result) # currently we accept strings in the format of "hello world" @@ -133,32 +162,32 @@ def quoted_word(view): # string ends with \ and no character after it if is_quoted: # if we're quoted then we're expecting a closing quote - raise BadArgument('Expected closing "') + raise BadArgument('Expected closing {}.'.format(close_quote)) # if we aren't then we just let it through return ''.join(result) - if next_char == '"': + if next_char in ((open_quote, close_quote) if is_quoted else _all_quotes): # escaped quote - result.append('"') + result.append(next_char) else: # different escape character, ignore it view.undo() result.append(current) continue + if not is_quoted and current in _all_quotes: + # we aren't quoted + raise BadArgument('Unexpected quote mark in non-quoted string') + # closing quote - if current == '"': + if is_quoted and current == close_quote: next_char = view.get() valid_eof = not next_char or next_char.isspace() - if is_quoted: - if not valid_eof: - raise BadArgument('Expected space after closing quotation') + if not valid_eof: + raise BadArgument('Expected space after closing quotation') - # we're quoted so it's okay - return ''.join(result) - else: - # we aren't quoted - raise BadArgument('Unexpected quote mark in non-quoted string') + # we're quoted so it's okay + return ''.join(result) if current.isspace() and not is_quoted: # end of word found