2012-04-23 3 views
4

나는 comint-mode에 기반하여 파생 된 모드를 쓰고있다. 이 모드는 명령 행 프로그램 (GRASS gis)에 대한 인터페이스이며, 프로그램에 대한 comint 모드 완료가 작동합니다. completion-at-point-functions을 통해 프로그램 인수에 대한 지원을 추가하려고합니다. 장난감의 예 :Emacs completion-at-point-functions

(setq my-commands 
     '(("ls" 
     ("my-completion-1") 
     ("my-completion-2")) 
     ("mv" 
     ("my-completion-3") 
     ("my-completion-4")))) 


(defun my-completion-at-point() 
    (interactive) 
    (let ((pt (point)) ;; collect point 
     start end) 

    (save-excursion ;; collect the program name 
     (comint-bol) 
     (re-search-forward "\\(\\S +\\)\\s ?")) 
    (if (and (>= pt (match-beginning 1)) 
      (<= pt (match-end 1))) 
     () ;; if we're still entering the command, pass completion on to 
     ;; comint-completion-at-point by returning nil 

     (let ((command (match-string-no-properties 1))) 
     (when (member* command my-commands :test 'string= :key 'car) 
      ;; If the command is one of my-commands, use the associated completions 
      (goto-char pt) 
      (re-search-backward "\\S *") 
      (setq start (point)) 
      (re-search-forward "\\S *") 
      (setq end (point)) 
      (list start end (cdr (assoc command my-commands)) :exclusive 'no)))))) 

(push 'my-completion-at-point completion-at-point-functions) 

거의 작동합니다. 프로그램 이름이 정상적으로 완성됩니다. 그러나 명령 행에서 ls을 입력하면 my-completion-이 삽입되고 두 가지 옵션이 제공되지 않습니다. 탭을 다시 누르면 다시 my-completion-이 삽입되어 이제 ls my-completion-mycompletion-이됩니다.

실제 코드에는 여러 줄의 명령이 있는지 확인하는 데 몇 줄이 포함되지만 완료 코드는 변경되지 않습니다. 이 버전의 코드에서는 프로그램 이름 중 하나가 시작되는 줄에 탭이 있습니다. my-commands 명령을 완료하는 데 사용할 수있는 인수 목록이 표시되지만 버퍼에 아무 것도 삽입되지 않으며 목록에 없습니다. 인수의 처음 몇 글자를 입력하여 좁혀지지 않습니다.

설명서를 끝내 보았지만 completion-at-point 함수를 작성하는 정확한 방법을 알 수 없습니다. 내가 누락 된 아이디어가 있습니까?

나는 간단히 'pcomplete'을 보았지만 실제로는 '문서'를 이해하지 못했으며 어떤 진전도 이루지 못했습니다.

답변

7

문제는 startend을 발견하는 것과 같아서 포인트의 인수 경계를 반환하는 것 같습니다. 나는 세부 사항을 확실히 알기 위해 충분히 오래 디버깅을하지 않았지만 대화식으로 함수를 호출하면 startend에 대해 동일한 값을 반환한다는 것을 알 수 있습니다. 이는 완료 UI가 지점에서 인수를 사용하여 전달한 완료 테이블에서 선택하라는 사실을 알고 있어야합니다. completion-at-point-functions의 요소로 추가 할 때

(when (member* command my-commands :test 'string= :key 'car) 
    ;; If the command is one of my-commands, use the associated completions 
    (goto-char pt) 
    (let ((start 
     (save-excursion 
      (skip-syntax-backward "^ ") 
      (point)))) 

    (list start pt (cdr (assoc command my-commands)) :exclusive 'no))))))) 

이 예상되는 결과를 제공합니다 : 다음 함수의 마지막 부분을 변경

한 수정 될 것으로 보인다.

여기 regexp 검색 대신 skip-syntax-backward을 사용했습니다. 이런 종류의 일에 대해서는 약간 더 관용적 인 Elisp입니다. 그것은 단지 구문 클래스 "공백"에없는 것들에 대해 포인트를 뒤로 이동시키는 것입니다. skip-syntax 함수는 point 값이 아닌 move distance를 리턴하므로 save-excursion이 끝날 때 point에 대한 호출을 추가해야합니다.

이와 같은 함수에서 regexp 검색을 사용하는 경우 일치하지 않는 경우 사용자에게 오류를 전달하지 않도록 t을 네 번째 인수 인 noerror에 전달하는 것이 좋습니다. 이것은 반환 값이 nil인지 직접 확인해야한다는 것을 의미합니다.

마지막으로, 당신은 add-hook을 사용할 수 있습니다 대신 완성 기능을 추가하는 push의 다음과 같이

(add-hook 'completion-at-point-functions 'my-completion-at-point nil t) 

이 두 가지 유용한 일을 : 그것은 당신의 기능을 추가하기 전에 후크에 이미 여부를 확인, 그리고 네 번째 인수 인 localt을 전달하면 함수는 버퍼 로컬 완료 지점 후크 값에만 추가됩니다. Tab 키를 누를 때 다른 모든 Emacs 버퍼에서 이러한 완료를 사용하고 싶지 않기 때문에 거의 확실합니다.

+0

하나 이상의 표결이 있다면 그 표를 얻을 수 있습니다! 귀하의 답변에 감사드립니다. 문서를 읽으면서, backward-rege 검색은 skip-syntax-backward와 같은 위치에 포인트를 두어야하지만, 분명히 그렇지 않습니다. 나는 또한 add-hook을 사용하여 시작했지만, 함수 이름을 반대로하고 잘못 인용했다. 이제 모든 것이 완벽하게 작동합니다. 다시 한 번 감사드립니다! – Tyler

+0

다행이었습니다. 더 생각해 보면,'re-search-backward' 문제는 거꾸로 검색 할 때 regexp 연산자의 탐욕과 관련이 있다고 생각합니다. 물론, "\\ S *"'는 빈 문자열과 일치하지만,'*'를'+'로 바꾸어도 여전히 공백이 아닌 문자 하나를 되돌리고 멈 춥니 다. –