LINUX.ORG.RU

Проблема с compose-region в Emacs

 compose region,


0

1

Захотелось автоматически заменять defvar на ∃, defun на ∃ƒ, lambda на λ и т.д. Я так понял этим занимается функция compose-region, однако она не работает если исходный регион заменяется несколькими символами как в случае с ∃ƒ. А именно оно выводитвсе символы в одно место т.е. (compose-region 1 5 «hello») по идее должно заменить первые пять символов в буфере на hello, но работает как будто удаляет первые пять символов и потом все буквы «hello» выводит в одну позицию точки один поверх другого...

В идеале так же было бы здорово заменять (not (foo)) на ¬(foo), но я не придумал как... :(

Кто-нибудь делал что-то подобное?

google pretty-lambdada.el
или, например так:

(font-lock-add-keywords 'lisp-mode
    '(("(\\(lambda\\)\\>" (0 (prog1 ()
                               (compose-region (match-beginning 1)
                                               (match-end 1)
                                               ?λ))))))

Bad_ptr ★★★★★
()
Ответ на: комментарий от Bad_ptr

Да, я видел этот код. Это работает для lambda, но попробуйте с defun и ∃ƒ. Он будет выводить оба символа ∃ƒ в одно место один поверх другого. Именно в этом месте я и наткнулся на проблему с compose-region...

arte-at-marte
() автор топика
Ответ на: комментарий от arte-at-marte

а. Ну я так не прбовал, но если действительно так, то решение влоб какое приходит — попробуй тогда выводить со смещением, например заменяешь символы с 1 по 5. тогда заменяй с 1 по 4 на ∃ а с 4 по 5 на ƒ.

Bad_ptr ★★★★★
()
Ответ на: комментарий от Bad_ptr

compose region имеет очень удобное свойство - если удалить или заменить один из символов в lambda, то вместо λ отобразится то, что получилось т.е. если поставить точку после λ и нажать backspace, то получится lambd. А в случае «со смещением» выходит черти что...

arte-at-marte
() автор топика

Захотелось автоматически заменять

Заменять ни каких действиях? Если переводить весь регион, то есть функция translate-region. Но все эти замены не тривиальны. Вот рабочий код для трансформации строки, а не региона. Правда, там всё единообразно - везде нужно строить таблицу перевода.

(setq map '((?я . ?i)
            ([?т ?ы] . [?y ?o ?u])))
(setq table (make-translation-table-from-alist map))
(define-translation-table 'botva table)
(define-coding-system 'own-encrypt
  "my encrypt coding system."
  :mnemonic ?e
  :coding-type 'utf-8
  :charset-list '(unicode)
  :mime-charset '(unicode)
  :ascii-compatible-p t
  :encode-translation-table 'botva)
(setq enc-str (encode-coding-string "я am, ты are" 'own-encrypt))

anonymous
()
Ответ на: комментарий от power

если ∃ƒ заменяет defun, то должны исчезнуть оба символа ∃ƒ и появиться defu.

arte-at-marte
() автор топика
Ответ на: комментарий от anonymous

а как это должно выглядеть, когда надо заменить defun на ∃ƒ? Кроме того нужна не фактическая замена... compose-region ничего не заменяет, в коде выше он только отображает λ вместо lambda. Но реально в файле после сохранения будет lambda... Это самое правильное поведение. иначе невозможно будет работать друг с другом, если у каждого будут свои обозначения...

arte-at-marte
() автор топика
Ответ на: комментарий от arte-at-marte

я пишу

(setq map '(([?d ?e ?f ?u ?n] . [?∃ ?ƒ])))
(setq table (make-translation-table-from-alist map))
(define-translation-table 'botva table)

(define-coding-system 'own-encrypt
  "my encrypt coding system."
  :mnemonic ?e
  :coding-type 'utf-8
  :charset-list '(unicode)
  :mime-charset '(unicode)
  :encode-translation-table 'botva)

(setq enc-str (encode-coding-string "defun" 'own-encrypt))

в ответ оно мне выводит «âˆƒÆ’»

arte-at-marte
() автор топика
Ответ на: комментарий от arte-at-marte

впрочем, наверно, не важно т.к. это я так понимаю трансляция т.е. именно замена...

arte-at-marte
() автор топика
Ответ на: комментарий от arte-at-marte

Так вы же не объясняете, что вам нужно. Если только представление, то я бы глянул на код «cyril-util.el». Там можно вводить кириллицу так, чтобы она отображалась в виде транслитерации

M-x standard-display-cyrillic-translit RET russian
Насколько помню, это завязано на расширение quail. Но если вам нужно именно compose-region и ничего другого, то приведите _код_, который не работает - может придумается, что заменить.

anonymous
()
Ответ на: комментарий от arte-at-marte

у вас кодировка не utf-8. В принципе, можно и независимо от кодировки писать, наводите на нужный символ M-x describe-char и далее заменяете символы на хэш-коды типа #x65.

anonymous
()
Ответ на: комментарий от anonymous

Спасибо, я посмотрю. привожу код:

(compose-region 1 5 "hello")
у меня все пять букв «hello» отображаются в одном месте поверх друг друга.

arte-at-marte
() автор топика
Ответ на: комментарий от arte-at-marte

Судя по всему, compose-region вам не подходит, она выполняет компоновку, т.е. представляет регион одним символом.

anonymous
()

может ∃ƒ сделать одним символом добавив в шрифт, хотя топорно и баян

proofit404
()
Ответ на: комментарий от anonymous

Нашел только такой вариант. К сожалению, переводит только _один_символ_ в последовательность. Хотя, с помощью различных циклов и подобных фенечек народ как-то добивается и перевода последовательности символов.

(setq buffer-display-table (make-display-table))
(aset buffer-display-table ?я [?т ?ы])

anonymous
()
Ответ на: комментарий от anonymous

Ладно, походу моя версия все равно лучше! :-D Поэтому на всякий случай опубликую ее. Я тупо заменяю compose-region своей функцией следующего вида:

(defun hack/compose-region (start end str)
  "Замена для compose-region т.к. текущая версия некорректно работает в X Window System.
А именно, compose-region может заменять регион только на одиночный символ. Если использовать строку, то все символы отобразятся в одной точке и получится каша. hack/compose-region - это хак! В случае, если str - строка более чем из одного символа, то для X Window System вместо compose-region она просто вызывает put-text-property с 'display..."
  (if (or (not (eq window-system 'x))
	 (characterp str)
	 (eq (length str) 1))
      (compose-region start end str)
    (put-text-property start end 'display str)))

Кстати да, забыл совсем... ключевая фраза: «некорректно работает в X Window System»... В консоли все Ok... Так что все таки это баг...

arte-at-marte
() автор топика
Ответ на: комментарий от arte-at-marte

Т.е. это представление нескольких символов в виде одного, не более? Вот перевод строки в строку - проблема. А решение, схожее с твоим, можно подсмотреть в org mode, там заменяют отображение entities в виде одного символа (pretty entities).

anonymous
()
Ответ на: комментарий от anonymous

Нет и несколько вместо нескольких тоже подставляет. Попробуйте (hack/compose-region 1 5 «hello»)

Ведет себя не так разумно, как compose-region (поудаляйте символе backspace'ом, чтобы понять чем именно), но, похоже, это лучший из вариантов.

arte-at-marte
() автор топика
Ответ на: комментарий от arte-at-marte

Ах да, проверил, здорово! Спасибо!

anonymous
()
Ответ на: комментарий от arte-at-marte

такое поведение приведёт в скором времени к вообще невалидному коду, который отображается нормально в соответствии с твоими заменами

proofit404
()
Ответ на: комментарий от arte-at-marte

Полностью написанное слово hello пропадает только после удаления всех 5ти первых символов. Если удалить 1 из них, то твоя подстановка останется нетронутой, хотя по логике теперь должно отображаться то, что осталось от первноначальной строки.
Например, если заменить lambda на λ и нажать backspace - λ останется, хотя код уже невалидный из-за «lambd» - надо, чтобы сразу вернул отображение к этой строке.
Также хотел сделать с той темой put-text-property по тому же принцыпу - чтобы установленный цвет возвращался к дефолту при редактировании региона, а не растягивал его.

proofit404
()
Ответ на: комментарий от proofit404

или удалять всю подстроку lambda из буфера при удалении любого символа из последовательности, обозначенной λ, раз уж перемещаемся мы как через один символ

proofit404
()
Ответ на: комментарий от proofit404

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

или удалять всю подстроку lambda

Тогда надо учитывать не только удаление, но и изменение. При том и сделаные вручную и автоматически скриптами. Я было хотел модифицировать код compose-region, но она уходит корнями в какую то адскую смесь Си с Лиспом...

arte-at-marte
() автор топика
Ответ на: комментарий от arte-at-marte

она уходит корнями в какую то адскую смесь Си с Лиспом...

Как и половина всех базовых функций для имакса.
Может есть возможность подобную технику на оверлэях сделать - при модификации любого отрезка удалять оверлэи, в которые входит (point). А над этими оверлэями определить свое действие, например твой хак.
P.S. тема самому интересна, т.к. хочу свой умный фолдинг кода, а действие там простое - add-to-invisibility-spec.

proofit404
()

Если честно, то не могу полностью погрузиться в твою проблему. Может, оверлеи использовать? Ты можешь создать оверлей с текстом, покрывающий конкретный регион. А потом при помощи overley-put в этот оверлей, в его свойство modification-hooks положить список функций с четырьмя аргументами (см. док-цию), которые вызываются, если ты пытаешься изменить текст внутри оверлея.

Скопируй то, что ниже в *scratch*, в самое начало буфера напиши для теста defun, потом последовательно eval на эти конструкции и попробуй потом удалить оверлей - увидишь именно то поведение, которое хочешь.

(defun ov-rem (a b c d)
  (remove-overlays))

(setq ov (make-overlay 1 6))
(overlay-put ov 'display "∃ƒ")
(overlay-put ov 'modification-hooks '(ov-rem))

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

Остается открытым вопрос замены defun в оверлей на лету.

Zubok ★★★★★
()
Последнее исправление: Zubok (всего исправлений: 2)
Ответ на: комментарий от Zubok

Спасибо.

Вот полноценная замена compose-region:

(defun pretty-compose (beg end text)
  (let ((ov (make-overlay beg end)))
    (overlay-put ov 'display text)
    (overlay-put ov 'modification-hooks
		 (list (lambda (ov &rest unused)
			 (delete-overlay ov))))))
А еще я написал вот такой код для удобства замены:
(defun list-if-not (e)
  "Оборачивает аргумент в список, если только он уже не является списокм."
  (if (listp e)
      e
    (list e)))

(defun apply-or-eval (form &rest args)
  "Выполняет form с аргументами args, если это функция, иначе вычисляет ее, игнорируя аргументы."
  (if (and (symbolp form)  (fboundp form))
      (apply form args)
    (eval form)))

(defun pretty-compose (beg end text)
  (let ((ov (make-overlay beg end)))
    (overlay-put ov 'display text)
    (overlay-put ov 'modification-hooks
		 (list (lambda (ov &rest unused)
			 (delete-overlay ov))))))

(defun pretty-composes-with (fn r-map mode+ )
  "r-map - список вида (compose regex) или ((compose1 regex1)...)
mode* - nil, символ или список обозначающий режим[ы]
fn    - функция трех аргументов (begin end text). 

compose может быть текстом, переменной, содержащей текст, или функцией, возвращающей текст.

Если в буфере находится текст, соответствующий regexp, то вызывается ф-ция fn. Первыми двумя аргументами передаестя позиция в буффере началa и конца соответствия, а третьим - текст соответствующего compose."
(if (not mode+)
    (setq mode+ '(nil)))
(dolist (mode (list-if-not mode+))
  (dolist (entry (if (listp (first r-map))
		     r-map
		   (list r-map)))
    (font-lock-add-keywords
     mode `((,(second entry)
	     (0 (progn (,fn (match-beginning 1)
			    (match-end 1)
			    (apply-or-eval ,(first entry)))
		       nil))))))))

(defun pretty-composes (r-map &optional  mode*)
"r-map - список вида (compose regex) или ((compose1 regex1)...)
mode* - nil, символ или список обозначающий режим[ы]

compose может быть текстом, переменной, содержащей текст, или функцией, возвращающей текст. Этот текст будет отображаться вместо всех подстрок, соответствующих regex в режимах mode+ или в текущем буфере, если в качестве mode+ передан nil.. В случае, если передана fns"
(pretty-composes-with 'pretty-compose r-map mode*))

(provide 'pretty-composes)
Теперь замену на лету можно производить вот так:
(require 'pretty-composes)

(pretty-composes 
 `(("λ" "(\\(lambda\\) ")
   ("∅" "\\(\\<nil\\)")
   ("∃"  "(\\(defvar\\) ") 
   ("∃ƒ" "(\\(defun\\) ")
   ("∀"  "(\\(mapcar\\) "))
 '(emacs-lisp-mode
   lisp-interaction-mode))
Ну, или вот так:
(require 'pretty-composes)

(define-minor-mode pretty-lisps-mode
  "Minor-mode для замены некоторых имен в Common Lisp и Emacs Lisp."
  :global nil
  (pretty-composes 
   `(("λ" "(\\(lambda\\) ")
     ("∅" "\\(\\<nil\\)")
     ("∃"  "(\\(defvar\\) ") 
     ("∃ƒ" "(\\(defun\\) ")
     ("∀"  "(\\(mapcar\\) "))))

(add-hook 'lisp-interaction-mode-hook 'pretty-lisps-mode)
PS: Я в Emacs Lisp не очень хорошо разбираюсь. Было бы здорово прочитать о пробелах в собственных знаниях и недочетах моего кода.

arte-at-marte
() автор топика
Ответ на: комментарий от arte-at-marte

А ты глянь код, который выше. Оно сосвем маленький. Там даже без modification-hooks. И тоже все очень просто.

Zubok ★★★★★
()
Ответ на: комментарий от Zubok

Так я смотрел. У меня оно работает странно. Попробуйте в этом lambda-mod'е набрать lambda, а потом перевести курсор на начало слова и удалить первый символ...

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

Мой код вроде бы тоже не сильно большой и сложный, особенно если убрать строки документации. Кроме того он более общий.

arte-at-marte
() автор топика
Ответ на: комментарий от arte-at-marte

Так я смотрел. У меня оно работает странно. Попробуйте в этом lambda-mod'е набрать lambda, а потом перевести курсор на начало слова и удалить первый символ...

Если я набираю lambda, то оно моментально преобразуется в λ, я не могу перейти уже на начало слова. Попробовал встать курсором на λ, даю delete, остается ambda. А разве по-другому должно быть?

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

Набрал lambda самым последним словом в буфере так, чтобы дальше курсор не шел. Даю backspace. Остается lambd. Все верно.

Впрочем, баг там нашел. Надо подряд написать lambdalambdalambda. Я не разбирался (по-прежнему не могу), но мне кажется, что там слишком упрощенный регексп «lambda» используется.

Zubok ★★★★★
()
Ответ на: комментарий от Zubok

А разве по-другому должно быть?

Да нет, все верно. У меня оно работает примерно так, но то, что нужно почему то появляется не сразу после удаления буквы, а только со следующим действием.

Впрочем, баг там нашел.

Хм... У меня оно точно так же бажит, если использевать слишком простой регексп... Какое то странное поведение...

arte-at-marte
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.