Emacs: выберите несколько регионов и переключитесь на инверсию

Я знаю, что есть несколько разных пакетов для выбора нескольких регионов в gnu emacs. То, что я ищу, - это способ выбрать несколько регионов, работать с ними, а затем выбрать инверсию ранее отмеченных регионов, чтобы затем работать с ними.

В качестве примера предположим, что у меня есть следующие данные в буфере:

Line A
Line B
Line C
Line D
Line E
Line F

Я хочу сделать следующее:

  1. Отметьте линии A, C и E как несколько областей
  2. Примените elisp к тексту в этих отмеченных областях.
  3. Скажите emacs, чтобы он поменял местами отмеченные и неотмеченные регионы. Теперь линии B, D и F должны быть отмечены как несколько регионов.
  4. Применить какой-либо другой elisp к тексту в этих других регионах

Шаги 1, 2 и 4 тривиальны, и они просто зависят от выбора кода с маркировкой нескольких регионов, который я решу использовать.

Но как насчет шага 3? Есть ли какой-нибудь пакет для маркировки нескольких регионов, который позволяет мне переключиться на то, что было ранее помечено?


person HippoMan    schedule 06.02.2016    source источник


Ответы (3)


Ваш вопрос общий. Мой ответ, вероятно, подойдет некоторым из них, но, возможно, не всем.

  1. Библиотека zones.el (см. Zones) позволяет определять и управлять несколькими зонами (по сути, несколькими областями) текста в нескольких буферах. Вы можете делать с ними такие вещи:

    • Sort them.
    • Объединяйте (объединяйте) смежные или перекрывающиеся зоны (включая их сортировку).
    • Дополните их, если они были объединены.
    • Пересеките их.
    • Сужение буфера до зон в списке — см. Множественные сужения.
    • Выберите зоны в списке в качестве активного региона. Цикл между регионами.
    • Обыщите их (они автоматически объединяются первыми). Для этого вам понадобится библиотека isearch-prop.el (см. Isearch+).
    • Выделяйте и не выделяйте их. (Для этого вам понадобится библиотека Highlight (highlight.el) или библиотеку facemenu+.el (см. Facemenu+).
    • Добавьте активный регион в список зон.
    • Добавьте регион в список зон, а затем объедините (объедините) зоны.
    • Удалить зону из списка зон.
    • Клонируйте переменную зоны в другую, чтобы у клона были те же зоны.
    • Клонируйте переменную зоны, а затем объедините зоны клона.
    • Сделайте переменную списка зон постоянной в закладке. Используйте закладку, чтобы восстановить ее в следующем сеансе Emacs. Для этого вам понадобится библиотека Bookmark+.
  2. Библиотека Isearch+ (включая isearch-prop.el) позволяет выполнять поиск в обеих зонах согласно zones.el и зоны, определяемые свойствами текста или наложения (любыми свойствами), и упрощает применение свойств к зонам текста.

    • You can also search the complement of a set of zones, which I think is what your question is really about. The areas not being searched can optionally be dimmed during search.
    • Вы можете искать различные «ВЕЩИ» как зоны. Вещами может быть все, что вы можете определить синтаксически. Они включают в себя обычные «вещи» Emacs, и если вы используете библиотеку thingatpt+.el, то вы можете легко определить дополнительные вещи.
  3. Если вы используете Icicles, вы можете использовать поиск Icicles в наборе контекстов поиска< /strong> в буферах или файлах. Вы можете определить контексты поиска различными способами, в том числе с помощью регулярного выражения. Там же вы можете искать дополнение набора поисковых контекстов. И вы можете использовать постепенное сужение совпадающих контекстов поиска, и при этом вы также можете вычитать наборы совпадений, используя дополнение.


ОБНОВЛЕНО после ваших замечаний -

Очевидно, вы хотите дополнить список неосновных зон (я называю их "зонами"), каждая из которых представляет собой число в качестве идентификатора, за которым следуют два ограничения зоны. (Базовые зоны имеют только пределы, а не идентификатор, и функция zz-zones-complement возвращает список основных зон.)

Вот как я бы определил эту функцию дополнения:

(defun zz-complement-izones (izones &optional beg end)
  "Complement IZONES, which is a list like `zz-izones'.
Such a list is also returned, that is, zones that have identifiers."
  (zz-izones-from-zones
   (zz-zones-complement (zz-zone-union (zz-izone-limits izones nil t)))))

Это просто использует предопределенную функцию zz-zones-complement после удаления идентификаторов зон и объединения зон. Предопределенная функция zz-izones-from-zones дает идентификаторы зон, в результате чего получается список зон.

И вам нужна функция, которая сопоставляет функцию со списком зон, применяя ее к каждой зоне в списке — то, что делает ваша функция traverse-zones.

Вот версия (непроверенная) такой функции карты, которая ожидает зоны формы, которую вы используете, то есть изоны (зоны, которые имеют числовой идентификатор) и отображает на них 3-арную функцию. Это похоже на ваш traverse-zones (но см. комментарий):

(defun map-izones (function &optional izones)
  "Map 3-ary FUNCTION over IZONES.
FUNCTION is applied to the first three elements of each zone.
IZONES is a list like `zz-izones', that is, zones with identifiers."
  ;; Do you really want this?  It prohibits mapping over an empty list of zones.
  ;; (unless izones) (setq izones  zz-izones)
  (when (and (functionp function)  (zz-izones-p izones))
    (setq izones  (zz-unite-zones izones))
    (dolist (izone  izones) (funcall function (car izone) (cadr izone) (caddr izone)))))

А вот (непроверенная) версия, которая отображает бинарную функцию на список зон. Зоны в списке могут быть либо всеми базовыми зонами, либо всеми изозонами. Мне это кажется более полезным, так как идентификаторы обычно не очень полезны в таком контексте.

(defun map-zones (function &optional zones)
  "Map binary FUNCTION over ZONES, applying it to the limits of each zone.
ZONES can be a list of basic zones or a list like `zz-izones', that
is, zones that have identifiers."
  (when (functionp function)
    (when (zz-izones-p zones)
      (setq zones  (zz-izone-limits zones nil 'ONLY-THIS-BUFFER)))
    (setq zones  (zz-zone-union zones))
    (dolist (zone  zones) (funcall function (car zone) (cadr zone)))))

Надеюсь это поможет.

person Drew    schedule 06.02.2016
comment
Наши сообщения пересеклись — вы опубликовали свой ответ о zones.el, пока я печатал свой. ;-) - person Drew; 06.02.2016
comment
Спасибо за все это. Однако у меня есть еще один вопрос. Вы написали, что базовые зоны имеют только лимиты, а не идентификатор, а функция zz-zones-complement возвращает список базовых зон. Я это понимаю. Итак, какой аргумент я должен передать zz-zones-complement, если не саму структуру zz-izones? Другими словами, как мне получить список основных зон, учитывая текущую структуру zz-зон? Опять же, моя цель такова: я хочу использовать структуры для нормальных зон и дополнительных зон, которые являются изоморфными. - person HippoMan; 08.02.2016
comment
См. мой раздел ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ, который я только что добавил, ниже. - person HippoMan; 08.02.2016
comment
Вы получаете список основных зон из такой структуры, как zz-izones, используя функцию zz-izone-limits. Он просто удаляет идентификаторы, оставляя только пределы каждой зоны. - person Drew; 09.02.2016

Я только что обнаружил предварительный ответ. Я говорю «предварительно», потому что я еще не проводил углубленного тестирования. Однако пакет "zones.el" официально предлагает нужные мне функции.

Он создает список выбранных регионов, которые он называет «зонами». Он предлагает функцию с именем zz-zones-complement, которая вернет дополнение «объединенного» списка текущих зон, которое можно получить следующим образом: (zz-zones-complement (zz-zone-union zz-izones)).

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

person HippoMan    schedule 06.02.2016

Спасибо, Дрю.

Получается, что зоны ближе всего к тому, что мне нужно, хотя и сосульки, и поиск+ тоже полезны.

Что касается зон, то оказалось, что функция дополнения zz-зон, похоже, не возвращает правильную информацию.

Однако я написал свою собственную функцию, которую я могу использовать вместо нее.

Теперь я делаю это, чтобы запустить произвольный код lisp для дополнения всех зон, которые были ранее добавлены через zone.el...

(defun my-complement-zones (&optional zones)
  (unless zones
    (setq zones zz-izones))
  (zz-unite-zones 'zones)
  (let ((result ())
        (end (copy-marker (point-min)))
        (n 0)
        (a nil)
        (b nil))
    (dolist (item (reverse zones))
      (setq n (1+ n))
      (setq a (cadr item))
      (setq b (caddr item))
      (setq result (append (list (list n end a)) result))
      (setq end b))
    (when (< (marker-position end) (point-max))
      (setq result (append (list (list (1+ n) end (copy-marker (point-max)))) result)))
    result))

;; Each element has three values: an index followed by the start
;; and end markers for each region. To traverse this structure,
;; do the following ...
(dolist (region (my-complement-zones))
  (let ((idx (car region))
        (start (cadr region))
        (end (caddr region)))
    ;; At this point, "start" is a marker pointing to the
    ;; beginning of the given zone, and "end" is a marker pointing
    ;; its endpoint. I can use these for inputs to any region-aware 
    ;; elisp commands, or for any functions that I might want to
    ;; write which operate on each given region.
    ;;
    ;; ... etc. ...
  ))

... и вот функция, которую я написал, которая будет проходить по списку зон и применять лямбду к каждой зоне. Это работает одинаково, независимо от того, используем ли мы исходные zz-izones или их дополнение, созданное с помощью моей функции:

;; Helper function
(defun funcallable (func)
  (and func
       (or (functionp func)
           (and (symbolp func)
                (fboundp func)))))

;; Traverse a list of zones such as zz-izones, and apply a lambda
;; to each zone in the list. Works equivalently with the output of 
;; `my-complement-zones'.
(defun traverse-zones (func &optional zones)
  (when (funcallable func)
    (unless zones
      (setq zones zz-izones))
    (zz-unite-zones 'zones) ;; not sure if this is really necessary
    (dolist (zone zones)
      (let ((i (car zone))
            (s (cadr zone))
            (e (caddr zone)))
        (funcall func i s e)))))

Чтобы проиллюстрировать разницу в структуре между zz-izones и выводом zz-zones-complement, вот пример структуры zz-izones, которую я создал в буфере с именем «foo»:

((4 #<marker at 1202 in foo> #<marker at 1266 in foo>) (3 #<marker at 689 in foo> #<marker at 1132 in foo>) (2 #<marker at 506 in foo> #<marker at 530 in foo>) (1 #<marker at 3 in foo> #<marker at 446 in foo>))

Вот как (zz-zones-complement zz-izones) выглядит для этого самого списка zz-izones...

((1 4) (#<marker at 1202 in foo> 3) (#<marker at 689 in foo> 2) (#<marker at 506 in foo> 1) (#<marker at 3 in foo> 1266)

Обратите внимание, что каждая запись в zz-izones состоит из индекса и двух маркеров. Однако в своем дополнении каждая запись представляет собой либо два целых числа, либо маркер и целое число. Эти структуры не изоморфны.

ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ

Для (zz-zone-union (zz-izone-limits zz-izones nil t)) ...

((#<marker at 3 in foo> #<marker at 446 in foo>) (#<marker at 506 in foo> #<marker at 530 in foo>) (#<marker at 689 in foo> #<marker at 1132 in foo>) (#<marker at 1202 in foo> #<marker at 1266 in foo>)

Для (zz-zones-complement (zz-zone-union (zz-izone-limits zz-izones nil t)))

((1 #<marker at 3 in foo>) (#<marker at 446 in foo> #<marker at 506 in foo>) (#<marker at 530 in foo> #<marker at 689 in foo>) (#<marker at 1132 in foo> #<marker at 1202 in foo>) (#<marker at 1266 in foo> 1266))

Я думаю, я мог бы использовать это дополнение, если бы я преобразовал «1» в первой записи в (copy-marker (point-min)) и «1266» в последнем элементе в (copy-marker (point-max)) ... если только я не имею дело с конкретным случаем, когда не имеет значения, является ли Я имею дело с маркерами или точками.

Маркеры идеальны, потому что тогда я могу изменить буфер после создания дополнения, и мне не придется беспокоиться о числовом значении точки в структуре дополнения, которое больше не указывает на то, где оно изначально указывало.

person HippoMan    schedule 06.02.2016
comment
Вы не говорите, в чем проблема с zz-zones-complement. Рассмотрите возможность подачи отчета об ошибке или запроса на улучшение (загрузите файл еще раз, чтобы получить контактную информацию). Спасибо. - person Drew; 07.02.2016
comment
zz-zones-complement возвращает структуру, которая не является допустимой структурой zz-izones. Это возвращаемое значение нельзя использовать во многих местах, где работают стандартные zz-izones. Моя собственная структура (из моих дополнительных зон) действительно везде работает в местах zz-зон. Я обязательно сообщу об этом автору. - person HippoMan; 07.02.2016
comment
Спасибо. С нетерпением жду отчета. Я не вижу в этом проблемы, априори. См. isearch-prop.el, чтобы узнать, как вы можете использовать его с zz-izones или произвольными зонами. Переменная. Например, (let* ((zones (zz-zone-union (zz-izone-limits (symbol-value variable) nil 'ONLY-THIS-BUFFER))) (comp-zones (zz-zones-complement zones))) (dolist (zone (if isearchp-complement-domain-p comp-zones zones)) ...) - person Drew; 07.02.2016
comment
О... похоже, вы автор... по крайней мере, у вас такое же имя! Если это так, у вас уже есть мой обходной путь. Причина, по которой я хотел, чтобы дополнение zz-izones имело ту же структуру, что и сами zz-izones, заключается в том, что я хочу переключаться между оригиналом и дополнением, и я не хочу иметь дело с двумя структурами. иначе. Например, я написал функцию обхода, которую опубликую здесь, и она будет работать как с исходным, так и с моим сгенерированным дополнением. - person HippoMan; 08.02.2016
comment
... и я также опубликовал содержимое примера zz-izones и результат (zz-zones-complement zz-izones). Вы можете видеть, что эти два списка имеют разную структуру. - person HippoMan; 08.02.2016
comment
Как я уже сказал, если вы хотите обсудить это, пожалуйста, свяжитесь со мной по электронной почте. Я думаю, вы делаете вещи, которые на самом деле не нужны (начиная с вашего funcallable, который делает не больше, чем functionp). Смотрите также мой обновленный ответ, который соответствует вашему ответу. - person Drew; 08.02.2016
comment
К вашему сведению, я обновил zones.el, чтобы использовать маркеры для начальной и конечной точек. также. - person Drew; 09.02.2016
comment
Я явно добавил функции сопоставления в zones.el. - person Drew; 13.11.2018