후크 기능을 한 번만 실행할 수있는 방법이 있습니까?

문맥

emacs 클라이언트 / 서버 구성after-make-frame-functions 에서 테마를 올바르게로드하기 위해 후크를 사용하고 있습니다 . 특히 이것은 이것을 만드는 데 사용하는 코드 스 니펫입니다 (이 SO answer 기반 ).

 (if (daemonp)
      (add-hook 'after-make-frame-functions
          (lambda (frame)
              (select-frame frame)
              (load-theme 'monokai t)
              ;; setup the smart-mode-line and its theme
              (sml/setup)))
      (progn (load-theme 'monokai t)
             (sml/setup)))

문제

새로운 emacsclient -c/t세션이 시작되면 후크는 새로운 프레임뿐만 아니라 기존의 모든 프레임 (다른 emacsclient 세션)에서 실행되며 매우 성가신 시각적 효과를냅니다 (테마는 모든 프레임에 다시로드됩니다) . 더 나쁜 것은 터미널 클라이언트가 이미 열린 상태에서 테마 색상이 완전히 엉망이되는 것입니다. 분명히 동일한 emacs 서버에 연결된 emacs 클라이언트에서만 발생합니다. 이 동작의 이유는 분명하며 후크가 서버에서 실행되고 모든 클라이언트가 영향을받습니다.

질문

이 기능을 한 번만 실행하거나 후크를 사용하지 않고 동일한 결과를 얻는 방법이 있습니까?


부분 솔루션

@Drew의 답변 덕분에 이제이 코드가 있습니다. 그러나 여전히 문제가 있습니다. 일단 터미널에서 클라이언트 세션을 시작하면 GUI가 테마를 올바르게로드하지 않으며 그 반대도 마찬가지입니다. 많은 테스트를 거친 후에는 어떤 emacsclient가 먼저 시작되는지에 따라 동작이 달라짐을 깨달았으며 다양한 것을 버린 후에는로드 된 색상 팔레트와 관련이 있다고 생각합니다. 테마를 수동으로 다시로드하면 모든 것이 제대로 작동하므로 초기 구성에서와 같이 후크가 함수를 호출 할 때이 동작이 나타나지 않는 이유입니다.

(defun emacsclient-setup-theme-function (frame)
  (progn
    (select-frame frame)
    (load-theme 'monokai t)
    ;; setup the smart-mode-line and its theme
    (sml/setup)
    (remove-hook 'after-make-frame-functions 'emacsclient-setup-theme-function)))

(if (daemonp)
    (add-hook 'after-make-frame-functions 'emacsclient-setup-theme-function)
    (progn (load-theme 'monokai t)
           (sml/setup)))

최종 솔루션

마지막으로 부분 솔루션에서 보이는 동작을 해결하는 완전히 작동하는 코드가 있습니다.이를 달성하기 위해 적절한 emacsclient가 처음 시작될 때 모드 (터미널 또는 GUI)로 함수를 한 번 실행 한 다음 후크에서 함수를 제거합니다. 더 이상 필요하지 않습니다. 이제 나는 행복하다! 🙂 다시 감사합니다 @Drew!

코드:

(setq myGraphicModeHash (make-hash-table :test 'equal :size 2))
(puthash "gui" t myGraphicModeHash)
(puthash "term" t myGraphicModeHash)

(defun emacsclient-setup-theme-function (frame)
  (let ((gui (gethash "gui" myGraphicModeHash))
        (ter (gethash "term" myGraphicModeHash)))
    (progn
      (select-frame frame)
      (when (or gui ter)
        (progn
          (load-theme 'monokai t)
          ;; setup the smart-mode-line and its theme
          (sml/setup)
          (sml/apply-theme 'dark)
          (if (display-graphic-p)
              (puthash "gui" nil myGraphicModeHash)
            (puthash "term" nil myGraphicModeHash))))
      (when (not (and gui ter))
        (remove-hook 'after-make-frame-functions 'emacsclient-setup-theme-function)))))

(if (daemonp)
    (add-hook 'after-make-frame-functions 'emacsclient-setup-theme-function)
  (progn (load-theme 'monokai t)
         (sml/setup)))



답변

나는 당신이 실제로 ” 한 번만 후크를 실행하는 “방법을 찾고 있지 않다고 추측하고있다 . 후크가 실행될 때마다 특정 기능을 한 번만 실행할 수있는 방법을 찾고 있다고 생각합니다.

기존 및 단순에 대한 답변 질문은 당신이 원하는 것을 한 번 작업을 수행 한 후, 후크에서 자신을 제거하는 함수입니다. 즉 add-hook, 후크가 실행될 때 함수가 실행되어야하고 함수가 수행 한 후에 함수 자체가 후크에서 제거되도록하는 컨텍스트에서 사용 하십시오.

내가 정말로 원하는 것을 정확하게 추측하고 있다면, ” 후크 기능을 한 번만 실행할 수있는 방법이 있습니까?

다음은 표준 라이브러리의 예입니다 facemenu.el.

(defun facemenu-set-self-insert-face (face)
  "Arrange for the next self-inserted char to have face `face'."
  (setq facemenu-self-insert-data (cons face this-command))
  (add-hook 'post-self-insert-hook 'facemenu-post-self-insert-function))

(defun facemenu-post-self-insert-function ()
  (when (and (car facemenu-self-insert-data)
             (eq last-command (cdr facemenu-self-insert-data)))
    (put-text-property (1- (point)) (point)
                       'face (car facemenu-self-insert-data))
    (setq facemenu-self-insert-data nil))
  (remove-hook 'post-self-insert-hook 'facemenu-post-self-insert-function))


답변

다음은 add-hook광범위하게 테스트되지 않은 대신 사용할 수있는 매크로입니다 .

(defmacro add-hook-run-once (hook function &optional append local)
  "Like add-hook, but remove the hook after it is called"
  (let ((sym (make-symbol "#once")))
    `(progn
       (defun ,sym ()
         (remove-hook ,hook ',sym ,local)
         (funcall ,function))
       (add-hook ,hook ',sym ,append ,local))))

참고 : make-symbol주어진 이름으로 언 인턴 심볼을 만듭니다. 나는 포함 #후크 변수를 보면서 경우에 당신이 그것을 건너, 플래그 이름 다소 특이한로 기호에.


답변

하이퍼 함수 gen-once를 사용하여 일반 함수를 한 번만 실행할 수있는 함수로 변환 할 수 있습니다.

(setq lexical-binding t)

(defun gen-once (fn)
  (let ((done nil))
    (lambda () (if (not done)
                   (progn (setq done t)
                          (funcall fn))))))
(setq test (gen-once (lambda () (message "runs"))))

;;;;---following is test----;;;;
((lambda () (funcall test))) ;; first time, message "runs"
((lambda () (funcall test))) ;; nil
((lambda () (funcall test))) ;; nil

그런 다음 사용 (add-hook 'xxx-mode-hook (gen-once your-function-need-to-run-once))


답변