[comp.emacs] webster and functions on words

dsill@RELAY.NSWC.NAVY.MIL (03/22/89)

Yesterday I posted a version of webster that provided the current word
as the default word to be spelled, defined, or ended.  Also yesterday,
Dirk Grunwald posted a version of webster that provided webster-word
and webster-define to spell or define, respectively, the word to the
left of point.

There is an inconsistency in the way Emacs functions on words work.
Some, like ispell-word or webster-word, operate on a word in the
current buffer.  Others, like apropos or webster, read their argument
from the minibuffer, after prompting.  Some of the latter, like
unix-apropos and webster as I modified it, provide a default response
of the current word.

Personally, I think it's better to have one function that can operate
on either the current word or one read from the minibuffer than to
have two separate functions.  The former has the advantage of showing
the operator exactly what word will be operated on if the default is
taken.  The only thing that could be considered a drawback of this
approach is that it requires the operator to confirm his choice by
pressing return.

What's the consensus: two separate functions, one operating on the
current word and one reading from the minibuffer; or one function
reading from the minibuffer providing the current word as a default? 

A related problem is that these functions determine the current word
differently.  Most, like ispell-word, use the regular expression \\w
to find the complete word to the left of point.  Others, like
webster-word, use the partial word from point to the beginning of the
word to the left of point.

Perhaps the function ``interactive'' could be modified to include a
code, say "w", meaning "the complete word to the left of point".  But
that would only solve half the problem.  There would still need to be
some way to handle defaults.  How about a general default mechanism
added to ``interactive''?  Something like:
    (interactive "wISpell word (%s): ")
    (interactive "aDescribe function (default %s): ")
which would result in:
    ISpell word (prefrobnicate):
    Describe function (default interactive):

Comments?  Flames?  Implementations?

================
Above opinions are mine.

"The ultimate metric that I would like to propose for user
friendliness is quite simple: if this system were a person, how long
would it take before you punched it in the nose?"
					-- Tom Carey

news@bbn.COM (News system owner ID) (03/22/89)

> From: dsill@RELAY.NSWC.NAVY.MIL
> Newsgroups: comp.emacs
> Date: 21 Mar 89 16:37:34 GMT
> 
> Perhaps the function ``interactive'' could be modified to include a
> code, say "w", meaning "the complete word to the left of point".  But
> that would only solve half the problem.  There would still need to be
> some way to handle defaults.  How about a general default mechanism
> added to ``interactive''?  Something like:
>     (interactive "wISpell word (%s): ")
>     (interactive "aDescribe function (default %s): ")
> which would result in:
>     ISpell word (prefrobnicate):
>     Describe function (default interactive):
> 

Well, something like this is already there: look for example at the
way find-tag and find-tag-tag are implemented, and you'll see a more
complex interaction between finding some string from the buffer and
constructing a call to interactive with it (i changed my definition of
find-tag-tag because i didn't like the way it lost if point is just
inside a left paren). The crucial documentation from C-h d interactive
is this prose:

The argument of  interactive  is usually a string containing a code letter
 followed by a prompt...
If the argument is not a string, it is evaluated to get a list of
 arguments to pass to the function.

And so you can call arbitrary bits of code to supply defaults by
looking at the buffer in smart ways. I also wish there were a more
standard way to do this, but maybe that's hoping for too much
(sometimes you want completion, sometimes you want to obey syntactic
conventions of the language in the buffer, etc.).

........................................
Sean Boisen -- sboisen@bbn.com
BBN Systems and Technologies Corporation, Cambridge MA
Disclaimer: these opinions void where prohibited by lawyers.


Sean

grunwald@flute.cs.uiuc.edu (03/22/89)

personally, I agree with you -- functions should provide default arguments
instead of having two ways of doing things. My brain went to lunch, leaving
my fingers behind, ergo ``webster-word'' (actually, I had been using
spell-word and said `gee, I'd like to use webster for spellings too'...)

Here's a combined version:
;; Copyright (C) 1989 Free Software Foundation

;; This file is part of GNU Emacs.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY.  No author or distributor
;; accepts responsibility to anyone for the consequences of using it
;; or for whether it serves any particular purpose or works at all,
;; unless he says so in writing.  Refer to the GNU Emacs General Public
;; License for full details.

;; Everyone is granted permission to copy, modify and redistribute
;; GNU Emacs, but only under the conditions described in the
;; GNU Emacs General Public License.   A copy of this license is
;; supposed to have been given to you along with GNU Emacs so you
;; can know your rights and responsibilities.  It should be in a
;; file named COPYING.  Among other things, the copyright notice
;; and this notice must be preserved on all copies.
;;
;; Author Jason R. Glasgow (glasgow@cs.yale.edu)
;; Modified from telnet.el by William F. Schelter
;; But almost entirely different.
;;
;; Modified by Dirk Grunwald to maintain an open connection.
;;
;; 3/18/89 Ashwin Ram <Ram-Ashwin@yale.edu>
;; Added webster-mode.
;; Fixed documentation.
;;
;; 3/20/89 Dirk Grunwald <grunwald@flute.cs.uiuc.edu>
;; Merged Rams changes with new additions: smarter window placement,
;; correctly handles un-exposed webster windows, minor cleanups.
;; Also, ``webster-word'', akin to ``spell-word''.
;;
;; To use this, you might want to add this line to your .emacs file:
;;
;;  (autoload 'webster "webster" "look up a word in Webster's 7th edition" t)
;;
;; Then just hit M-x webster to look up a word.
;;

(defvar webster-host "128.197.2.40"
  "Host that is a webster server (Boston U). Try also 26.0.0.73, which is sri-nic")
(defvar webster-port "103"
  "The port to connect to. Either 103 or 2627")

(defvar webster-process nil
  "The current webster process")

(defvar webster-process-name "webster"
  "The current webster process")

(defvar webster-buffer nil
  "The current webster process")

(defvar webster-running nil
  "Used to determine when connection is established")

;;;
;;; Initial filter for ignoring information until successfully connected
;;;
(defun webster-initial-filter (proc string)
  (let
      (( this-buffer (current-buffer)))
    (set-buffer webster-buffer)
    (goto-char (point-max))
    (cond
     ((not (eq (process-status webster-process) 'run))
      (progn
	(setq webster-running t)
	(message "Webster died")))
     
     ((string-match "No such host" string)
      (progn
	(setq webster-running t)
	(kill-buffer (process-buffer proc))
	(error "No such host.")))
     
     ((string-match "]" string)
      (progn
	(setq webster-running t)
	(set-process-filter proc 'webster-filter))))
    (set-buffer this-buffer)))

(defun webster-filter (proc string)
  (let
      ((closed-message (string-match "Connection closed" string))
       (end-def-message (or (string-match "\200" string) (string-match "\0" string)))
       ( this-buffer (current-buffer)))

    (set-buffer webster-buffer)
    (cond

     ((not (eq (process-status webster-process) 'run))
      (message "Webster died"))

     (closed-message
	(message "Closing webster connection...")
	(kill-process proc)
	(replace-regexp "Process webster killed" "" nil)
	(goto-char 1)
	(message "Closing webster connection...Done."))
     
     ((string-match "SPELLING 0" string)
      (insert-string "...Word not found in webster\n"))
     
     ((string-match "SPELLING 1" string)
      (insert-string "...Spelled correctly\n"))

     (end-def-message
      (webster-filter proc (concat (substring string 0 (- end-def-message 1)) "\n\n"))
      (goto-char (point-max)))

     (t
      (goto-char (point-max))
      (let ((now (point)))
	(insert string)
	(delete-char-in-region now (point) ?\^m ?\ ))
      (if (process-mark proc)
	  (set-marker (process-mark proc) (point)))))

    ;;
    ;; if webster is visible, move the last line to the bottom of that window
    ;;
    (let ((here (selected-window)))
      (let ((webster-window (get-buffer-window webster-buffer)))
	(if (windowp webster-window)
	  (progn
	    (select-window webster-window)
	     (recenter -1)
	     (select-window here)))))

    (set-buffer this-buffer)))

;;;
;;; delete char1 and char2 if it precedes char1
;;; used to get rid of <space><return>
(defun delete-char-in-region (start end char1 char2)
  (goto-char start)
  (while (search-forward (char-to-string char1) end t)
    (backward-delete-char 1)
    (if (equal (char-after (- (point) 1)) char2)
	(backward-delete-char 1))))

;; Snatched from unix-apropos by Henry Kautz
(defun webster-current-word ()
   "Word cursor is over, as a string."
   (save-excursion
      (let (beg end)
	 (re-search-backward "\\w" nil 2)
	 (re-search-backward "\\b" nil 2)
	 (setq beg (point))
	 (re-search-forward "\\w*\\b" nil 2)
	 (setq end (point))
	 (buffer-substring beg end))))

(defun webster (arg)
"Look up a word in the Webster's dictionary.
Open a network login connection to a webster host if necessary.
Communication with host is recorded in a buffer *webster*."
  (interactive (list
		(read-string
		 (concat "Look up word in webster (" (webster-current-word) "): "))))
  (if (equal "" arg) (setq arg (webster-current-word)))
  (webster-send-request "DEFINE" arg))

(defun webster-endings (arg)
"Look up endings for a word in the Webster's dictionary.
Open a network login connection to a webster host if necessary.
Communication with host is recorded in a buffer *webster*."
  (interactive (list
		(read-string
		 (concat
		  "Find endings for word in webster (" (webster-current-word) "): "))))
  (webster-send-request "ENDINGS" arg))

(defun webster-spell (arg)
"Look spelling for a word in the Webster's dictionary.
Open a network login connection to a webster host if necessary.
Communication with host is recorded in a buffer *webster*."
  (interactive (list
		(read-string
		 (concat "Spell word in webster (" (webster-current-word) "): "))))
  (if (equal "" arg) (setq arg (webster-current-word)))
  (webster-send-request "SPELL" arg))

(defun webster-send-request (kind word)
  (require 'shell)
  (let
      ((webster-command (concat "open " webster-host " " webster-port "\n")))
    
    (if (or 
	 (not webster-buffer)
	 (not webster-process)
	 (not (eq (process-status webster-process) 'run)))
	(progn
	  (message
	   (concat "Attempting to connect to server " webster-host "..."))
	  (setq webster-buffer
		(make-shell webster-process-name "telnet"))
	  (let
	      ((this-buffer (current-buffer)))
	    (set-buffer webster-buffer)
	    (webster-mode)
	    (set-buffer this-buffer))

	  (setq webster-process (get-process webster-process-name))
	  (set-process-filter webster-process 'webster-initial-filter)
	  (process-send-string  webster-process webster-command)
	  (setq webster-running nil);
	  (while (not webster-running)	; wait for feedback
	    (accept-process-output))))	;
    (display-buffer webster-buffer nil)
    (process-send-string webster-process (concat kind " " word "\n"))))

(defun webster-quit ()
   "Close connection and quit webster-mode.
Buffer is not deleted."
   (interactive)
   (message "Closing connection to %s..." webster-host)
   (kill-process webster-process)
   (message "Closing connection to %s...done" webster-host)
   (bury-buffer))

(defun webster-mode ()
  "Major mode for interacting with on-line Webster's dictionary.
\\{webster-mode-map}
Use webster-mode-hook for customization."
  (interactive)
  (kill-all-local-variables)
  (setq major-mode 'webster-mode)
  (setq mode-name "Webster")
  (use-local-map webster-mode-map)
  (run-hooks 'webster-mode-hook))

(defvar webster-mode-map nil)
(if webster-mode-map
    nil
  (setq webster-mode-map (make-sparse-keymap))
  (define-key webster-mode-map "?" 'describe-mode)
  (define-key webster-mode-map "d" 'webster)
  (define-key webster-mode-map "e" 'webster-endings)
  (define-key webster-mode-map "q" 'webster-quit)
  (define-key webster-mode-map "s" 'webster-spell))
--
Dirk Grunwald
Univ. of Illinois
grunwald@flute.cs.uiuc.edu