Python + readline + автозаполнение (вкладка): почему тире и вопросительные знаки рассматриваются как разделители слов? - PullRequest
5 голосов
/ 22 сентября 2011

Привет кодерам и смелым пользователям GNU-readline,
Несколько месяцев назад я начал использовать модуль readline Python (2.7.1) для подобного оболочке приложения, которое я написал. Приложение не имеет ничего общего с файлами и файловыми системами - это индивидуальное решение для проприетарного программного обеспечения для управления.

Вчера я обнаружил, что определенный текст вызывает непредвиденное поведение автозаполнения, и не нашел способа решить эту проблему в документации . Я отчаянно прошу вашей помощи здесь. Я начну с примера и последую за фрагментом кода, который воспроизводит нежелательное поведение.

Предоставление значений для автозаполнения:

aaa0   aaa1   aaa2   bbb_0  bbb_1  bbb_2
ccc-0  ccc-1  ccc-2  ddd?0  ddd?1  ddd?2

... тогда неожиданное поведение выглядит следующим образом (каждое действие сопровождается результирующим выводом, а знак «|» представляет курсор):

  1. Введите 'b'.
    Input> b|
  2. Нажмите TAB (который в моей конфигурации связан с действием автозавершения).
    Input> bbb_|
  3. Нажмите TAB еще раз. Ваш текст останется прежним, но вы получите следующую подсказку:
    bbb_0 bbb_1 bbb_2<br> Input> bbb_|
  4. Введите '0' и нажмите клавишу TAB.
    bbb_0 bbb_1 bbb_2<br> Input> bbb_0 |
    Обратите внимание на пробел между символом «0» и курсором (приведенный ниже фрагмент кода должен это объяснить).
    Пока все хорошо, и попытка того же с 'a' приведет к аналогичному выводу, только без подчеркивания (aaa0, aaa1, aaa2).
  5. Начните сначала и введите 'c'.
    Input> c
  6. Нажмите клавишу TAB.
    Input> ccc-
  7. Снова нажмите TAB.
    aaa0 aaa1 aaa2 bbb_0 bbb_1 bbb_2 ccc-0 ccc-1 ccc-2 ddd?0 ddd?1 ddd?2<br> Input> ccc-|
    Это первая половина моей проблемы. Отображаются все значения, а не только значения, начинающиеся с 'ccc -'.
  8. Введите '0' и нажмите клавишу TAB.
    aaa0 aaa1 aaa2 bbb_0 bbb_1 bbb_2 ccc-0 ccc-1 ccc-2 ddd?0 ddd?1 ddd?2<br> Input> ccc-0|
    Вот вторая половина моей проблемы, видите ли, между символом «0» и курсором нет пробела (опять же, фрагмент ниже должен объяснить, почему должен быть пробел). Фактически, нажатие клавиши TAB не изменяет текст и не отображает подсказки, и последующие нажатия клавиши TAB ведут себя так же.

На практике то, что происходит на шаге 7, является недоразумением . Readline «ошибается» символом тире «-» для разделителя слов (и то же самое касается символа вопросительного знака «?», Если вы пытаетесь автоматически завершить «ddd?»; Другие распространенные разделители слов, например: пробел, табуляция, '='). Итак, поскольку текущий буфер строки заканчивается разделителем слов, тогда пришло время для нового слова, верно? Следовательно, на шаге 7 (вот где мы находимся) все значения отображаются после нажатия клавиши TAB.

На шаге 8, когда строка выглядит так: "Input> ccc-0|", нажатие клавиши TAB не дает никакого эффекта, потому что тире, будучи разделителем слов, разделяет строку на два слова: 'ccc' и '0'. Таким образом, слово, которое должно быть завершено, равно «0», но, увы, ни одно из возможных значений не начинается с «0», так что никакого эффекта.

Теперь, к сожалению, здесь нет правильного или неправильного. Например, в моем приложении знак равенства '=' на самом деле является разделителем слов, а тире '-' - нет. Полагаю, это должно быть вопросом конфигурации, но я не нашел способа настроить, какие символы разделяют слова. Вот с чем мне нужна помощь.

Я человек своего слова, поэтому вот фрагмент кода, который я обещал:

import readline
values = ['aaa0',  'aaa1',  'aaa2',  'bbb_0', 'bbb_1', 'bbb_2',
          'ccc-0', 'ccc-1', 'ccc-2', 'ddd?0', 'ddd?1', 'ddd?2']
def complete(text, state):
    matches = [v for v in values if v.startswith(text)]
    if len(matches) == 1 and matches[0] == text:
        # Add space if the current text is the same as the only match
        return "{} ".format(matches[0]) if state == 0 else None
    if state >= len(matches):
        return None
    return matches[state]
readline.set_completer(complete)
for line in ("tab: complete", "set show-all-if-unmodified on"):
    readline.parse_and_bind(line)
raw_input("Input> ")

Мальчики и девочки, пожалуйста - помогите! Я обещаю быть очень благодарным и даже вернуть одолжение. )

Большое спасибо заранее, Амнон Г

1 Ответ

5 голосов
/ 25 сентября 2011

Просто глядя на вывод dir(readline), функции get_completer_delims() и set_completer_delims() выглядят так, как будто они могут быть полезны.Фактически, документация для модуля readline включает в себя:

set_completer_delims(...)
    set_completer_delims(string) -> None
    set the readline word delimiters for tab-completion

Я думаю, это точно описывает то, что вы хотите.Это на Python 2.6.7;если вы запускаете что-то раньше, возможно, эта функция недоступна.

...