completing-read.el 45.2 KB
Newer Older
David Byers's avatar
David Byers committed
1
;;;;; -*-coding: iso-8859-1;-*-
Linus Tolke's avatar
Linus Tolke committed
2
3
;;;;;
;;;;; $Id$
4
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
Linus Tolke's avatar
Linus Tolke committed
5
;;;;;
6
;;;;; This file is part of the LysKOM Emacs LISP client.
Linus Tolke's avatar
Linus Tolke committed
7
8
9
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
10
;;;;; the Free Software Foundation; either version 2, or (at your option) 
Linus Tolke's avatar
Linus Tolke committed
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
Per Cederqvist's avatar
.    
Per Cederqvist committed
26
27
28
29
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: completing-read.el
30
;;;; Author: David Byers
Per Cederqvist's avatar
.    
Per Cederqvist committed
31
32
33
34
35
;;;;
;;;; This file implements functions for reading a conference name
;;;; or a person name with completion and other help.
;;;;

36
(setq lyskom-clientversion-long 
37
38
39
      (concat
       lyskom-clientversion-long
       "$Id$\n"))
40

41
(defvar lyskom-name-hist nil)
Per Cederqvist's avatar
.    
Per Cederqvist committed
42

43

44
45
46
47
;;; ============================================================
;;;
;;; Name lookup caches
;;;
48

David Byers's avatar
David Byers committed
49
50
51
52
53
54
(defvar lyskom-completing-who-info-cache nil
  "Temporary cache of who-info data")

(defvar lyskom-completing-lookup-name-cache nil
  "Temporary cache of server queries")

55
(defvar lyskom-completing-use-dynamic-info nil)
David Byers's avatar
David Byers committed
56
57
58
59
60
61
62
63
64
65
66

(defun lyskom-completing-clear-cache ()
  (setq lyskom-completing-who-info-cache nil)
  (setq lyskom-completing-lookup-name-cache nil))

(defun lyskom-completing-who-is-on ()
  "Get information about who is on, first checking the cache. Returns what 
\(blocking-do 'who-is-on\) would, but as a list, not a vector"
  (if lyskom-completing-who-info-cache
      lyskom-completing-who-info-cache
    (setq lyskom-completing-who-info-cache
67
          (listify-vector
David Byers's avatar
David Byers committed
68
	   (if (lyskom-have-feature dynamic-session-info)
69
70
	       (blocking-do 'who-is-on-dynamic t t 0)
	     (blocking-do 'who-is-on))))))
David Byers's avatar
David Byers committed
71

72
(defun lyskom-completing-cache-completion (string data)
73
  (let* ((downs (lyskom-unicase string))
74
75
76
77
78
79
80
81
         (tmp (assoc downs lyskom-completing-lookup-name-cache)))
    (if (null tmp)
        (setq lyskom-completing-lookup-name-cache
              (cons (cons downs data) lyskom-completing-lookup-name-cache)))
    string))

(defun lyskom-completing-lookup-z-name (string want-conf want-pers)
  "Look up STRING as a name. Same as \(blocking-do 'lookup-z-name ...\)
David Byers's avatar
David Byers committed
82
but first checks a cache."
83
84
85
86
87
88
89
90
91
92
93
94
95
  (if (and (eq 0 want-conf)
           (eq 0 want-pers))
      nil
    (let* ((downs (lyskom-unicase string))
           (tmp (assoc downs lyskom-completing-lookup-name-cache)))
      (if tmp
          (cdr tmp)
        (progn
          (setq tmp (blocking-do 'lookup-z-name string want-pers want-conf))
          (setq lyskom-completing-lookup-name-cache
                (cons (cons downs tmp)
                      lyskom-completing-lookup-name-cache))
          tmp)))))
David Byers's avatar
David Byers committed
96

97
98
99
100
101
;;; ============================================================
;;;
;;; Keymaps
;;;

David Byers's avatar
David Byers committed
102

103
104
105
106
107
108
109
110
(defvar lyskom-minibuffer-local-completion-map
  (let ((map (copy-keymap minibuffer-local-completion-map)))
    (define-key map " " nil)
    map)
  "Keymap used for reading LysKOM names.")

(defvar lyskom-minibuffer-local-must-match-map
  (let ((map (copy-keymap minibuffer-local-must-match-map)))
David Byers's avatar
X    
David Byers committed
111
    (lyskom-xemacs-or-gnu 
112
     (lyskom-set-keymap-parent map lyskom-minibuffer-local-completion-map)
David Byers's avatar
X    
David Byers committed
113
     (define-key map " " nil))
114
115
116
    map)
  "Keymap used for reading LysKOM names.")

117
118


119
120
121
122
123
(defsubst lyskom-completing-match-string-regexp (string)
  (concat "^"
          (replace-in-string (regexp-quote (lyskom-unicase (lyskom-completing-strip-name string)))
                             "\\s-+" "\\\\S-*\\\\s-+")
          "\\s-*"))
124

125
126
127
128
(defsubst lyskom-completing-match-string (string name)
  "Return non-nil if STRING matches NAME using LysKOM completion rules."
  (string-match (lyskom-completing-match-string-regexp string)
                (lyskom-completing-strip-name (lyskom-unicase name))))
129
130


David Byers's avatar
David Byers committed
131
(defun lyskom-read-conf-no (prompt type &optional empty initial mustmatch)
132
133
134
135
  "Read a conference name from the minibuffer with completion and
return its number or zero if nothing was matched.

See lyskom-read-conf for a description of the parameters."
136
137
138
  (let ((conf-z-info (lyskom-read-conf prompt type empty initial mustmatch)))
    (cond ((null conf-z-info) 0)
          ((stringp conf-z-info) 0)
139
	  ((lyskom-conf-stat-p conf-z-info) (conf-stat->conf-no conf-z-info))
David Byers's avatar
David Byers committed
140
	  ((lyskom-uconf-stat-p conf-z-info) (uconf-stat->conf-no conf-z-info))
141
          (t (conf-z-info->conf-no conf-z-info)))))
142
143
144
145
146
147

(defun lyskom-read-conf-stat (prompt type &optional empty initial mustmatch)
  "Read a conference name from the minibuffer with completion and
return its conf-stat or nil if nothing was matched.

See lyskom-read-conf for a description of the parameters."
148
149
150
  (let ((conf-z-info (lyskom-read-conf prompt type empty initial mustmatch)))
    (cond ((null conf-z-info) nil)
          ((stringp conf-z-info) nil)
151
	  ((lyskom-conf-stat-p conf-z-info) conf-z-info)
David Byers's avatar
David Byers committed
152
153
          ((lyskom-uconf-stat-p conf-z-info) 
           (blocking-do 'get-conf-stat (uconf-stat->conf-no conf-z-info)))
154
155
          (t (blocking-do 'get-conf-stat 
                          (conf-z-info->conf-no conf-z-info))))))
156

David Byers's avatar
David Byers committed
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
(defun lyskom-read-uconf-stat (prompt type &optional empty initial mustmatch)
  "Read a conference name from the minibuffer with completion and
return its conf-stat or nil if nothing was matched.

See lyskom-read-conf for a description of the parameters."
  (let ((conf-z-info (lyskom-read-conf prompt type empty initial mustmatch)))
    (cond ((null conf-z-info) nil)
          ((stringp conf-z-info) nil)
	  ((lyskom-uconf-stat-p conf-z-info) conf-z-info)
	  ((lyskom-conf-stat-p conf-z-info)
           (blocking-do 'get-uconf-stat 
                        (conf-stat->conf-no conf-z-info)))
          (t (blocking-do 'get-uconf-stat 
                          (conf-z-info->conf-no conf-z-info))))))

172
173
174
175
176
(defun lyskom-read-conf-name (prompt type &optional empty initial mustmatch)
  "Read a conference name from the minibuffer with completion and
return its name.

See lyskom-read-conf for a description of the parameters."
177
178
179
  (let ((conf-z-info (lyskom-read-conf prompt type empty initial mustmatch)))
    (cond ((null conf-z-info) "")
          ((stringp conf-z-info) conf-z-info)
David Kågedal's avatar
David Kågedal committed
180
	  ((lyskom-conf-stat-p conf-z-info) (conf-stat->name conf-z-info))
David Byers's avatar
David Byers committed
181
	  ((lyskom-uconf-stat-p conf-z-info) (uconf-stat->name conf-z-info))
David Kågedal's avatar
David Kågedal committed
182
	  (t (conf-z-info->name conf-z-info)))))
183

184

185
186
187
188
;;; ================================================================
;;; Code to guess defaults for initial input
;;;

189
(defun lyskom-default-conference-at-point (&rest args)
190
191
192
193
194
  (let* ((pos (or lyskom-command-point (point)))
         (type (and pos (get-text-property pos 'lyskom-button-type))))
    (and (memq type '(conf pers))
         (list (get-text-property pos 'lyskom-button-arg)))))

195
(defun lyskom-default-conference-current (&rest args)
196
197
  (list lyskom-current-conf))

198
(defun lyskom-default-conference-self (&rest args)
199
200
  (list lyskom-pers-no))

201
(defun lyskom-default-conference-last-author (&rest args)
202
203
204
205
  (and (lyskom-get-last-read-text)
       (list (text-stat->author
              (blocking-do 'get-text-stat (lyskom-get-last-read-text))))))

206
207
208
209
210
(defun lyskom-default-conference-author-of-text-at-point (&rest args)
  (let ((text (lyskom-text-at-point)))
    (and text 
	 (list (text-stat->author (blocking-do 'get-text-stat text))))))

211
212
213
214
(defun lyskom-default-conference-restriction (predicate &rest args)
  (and (assq 'restrict predicate)
       (cdr (assq 'restrict predicate))))

215
216
217
(defun lyskom-default-conference-empty (&rest args)
  nil)

218
219

(defun lyskom-default-conference-saved (sym &rest args)
220
221
222
223
224
  (save-excursion
    (when lyskom-buffer (set-buffer lyskom-buffer))
    (and (cdr (assq (car sym) lyskom-read-conf-saved-inputs))
         (list (conf-z-info->conf-no 
                (cdr (assq (car sym) lyskom-read-conf-saved-inputs)))))))
225
226
227
228
229
230
231

(defun lyskom-default-conference-not-self (uc &rest args)
  (not (eq (uconf-stat->conf-no uc) lyskom-pers-no)))

(defun lyskom-default-conference-not-current (uc &rest args)
  (not (eq (uconf-stat->conf-no uc) lyskom-current-conf)))

232

233
234
235
236
237
238
239
240
241

(defun lyskom-get-initial-conf-strategy (prompt)
  (when (listp prompt) (setq prompt (car prompt)))
  (let* ((spec-1 (cdr (assq (or lyskom-current-command this-command)
                            lyskom-default-conference-strategy)))
         (default-spec 
           (cdr (assq t (assq t lyskom-default-conference-strategy))))
         (prompt-spec (cdr (assq prompt spec-1)))
         (cmd-spec (cdr (assq t spec-1))))
242
    (lyskom-debug-forms
243
244
245
246
247
248
249
250
251
     (unless spec-1 
       (save-excursion
         (when lyskom-buffer (set-buffer lyskom-buffer))
         (lyskom-format-insert-before-prompt 
          "%[%#2@%#1s%]\n"
          (format "Warning: no strategy for %S/%S"
                  (or lyskom-current-command this-command)
                  prompt)
          `(face ,kom-warning-face)))))
252
253
254
255
256
    (list (or (assq 'default prompt-spec)
              (assq 'default cmd-spec)
              (assq 'default default-spec))
          (or (assq 'filter prompt-spec)
              (assq 'filter cmd-spec)
257
258
259
260
              (assq 'filter default-spec))
          (or (assq 'save prompt-spec)
              (assq 'save cmd-spec)
              (assq 'save default-spec)))))
261
262

(defun lyskom-read-conf-guess-initial (prompt predicate)
263
  "Return a guess for the initial value for lyskom-read-conf."
264
  (let* ((strategy (lyskom-get-initial-conf-strategy prompt))
265
266
267
268
         (default (cdr (assq 'default strategy)))
         (filter (cdr (assq 'filter strategy))))

    (uconf-stat->name
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
     (car
      (filter-list 
       (lambda (uconf-stat)
         (and uconf-stat
              (not (memq nil
                         (mapcar (lambda (fn)
                                   (funcall fn uconf-stat))
                                 filter)))
              (lyskom-read-conf-internal-verify-type
               (uconf-stat->conf-no uconf-stat)
               (uconf-stat->conf-type uconf-stat)
               predicate nil nil)))
       (mapcar (lambda (conf-no)
                 (blocking-do 'get-uconf-stat conf-no)) 
               (apply 'append 
                      (delq nil (mapcar (lambda (fn)
                                          (if (listp fn)
286
287
                                              (funcall (car fn) (cdr fn) predicate)
                                            (funcall fn predicate)))
288
289
290
291
292
293
                                        default)))))))))



(defun lyskom-read-conf-save-input (prompt input)
  "Save INPUT as input for the current completing read command."
294
295
296
297
298
299
300
301
  (save-excursion
    (when lyskom-buffer (set-buffer lyskom-buffer))
    (lyskom-traverse sym (cdr (assq 'save (lyskom-get-initial-conf-strategy prompt)))
      (if (assq sym lyskom-read-conf-saved-inputs)
          (setcdr (assq sym lyskom-read-conf-saved-inputs) input)
        (setq lyskom-read-conf-saved-inputs 
              (cons (cons sym input) lyskom-read-conf-saved-inputs))
        ))))
302

303
304
305
(defun lyskom-read-conf (prompt type &optional empty initial mustmatch)
  "Completing read a conference or person from the minibuffer. 

306
307
308
309
PROMPT is the prompt. It can be a string, symbol or list. If it is a symbol,
       lyskom-get-string will be used to get the string. If it is a list,
       it is used as the argument list to lyskom-format to create the
       prompt. You should use symbols and lists only.
310
TYPE   is the type of conferences to return. It is a list of one or
311
312
313
314
315
316
317
318
319
320
321
       more of the following:

       all     Return any conference.
       conf    Return conferences (not letterboxes).
       pers    Return persons (letterboxes).
       login   Return persons who are also logged-in.
       membership Return only conferences and letterboxes lyskom-pers-no
               is a member of.
       none    Return names that do not match anything in the database.
       (restrict c1 c2 ...) Restrict matching to conference numbers c1, 
               c2 etc. The implementation is inefficient for long lists.
322

323
Optional arguments
324

325
EMPTY     allow nothing to be entered.
326

David Byers's avatar
David Byers committed
327
INITIAL   initial contents of the minibuffer. If an integer, use the
328
329
330
          name of that conference. This is normally computed automatically,
          so pass nil whenever possible.

331
332
333
MUSTMATCH if non-nil, the user must enter a valid name.

The return value may be one of
334
A conf-z-info: The conf-z-info associated with the name entered,
335
336
nil:         Nothing was entered, or
A string:    A name that matched nothing in the database."
337

338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
  ;; We bind lyskom-current-command here so that changes to
  ;; this-command will not confuse our guesses for the initial value
  ;; (particularly at the end of this function, where we sometimes
  ;; save the value the user entered).

  (let ((lyskom-current-command (or lyskom-current-command this-command)))
    (lyskom-completing-clear-cache)
    (setq initial
          (cond ((integerp initial)
                 (uconf-stat->name (blocking-do 'get-uconf-stat initial)))
                ((stringp initial) initial)
                ((lyskom-conf-stat-p initial)
                 (conf-stat->name initial))
                ((lyskom-uconf-stat-p initial)
                 (uconf-stat->name initial))
                ((lyskom-conf-z-info-p initial)
                 (conf-z-info->name initial))
                ((consp initial) initial)
                ((lyskom-read-conf-guess-initial prompt type))
                (t nil)))

    (let* ((completion-ignore-case t)
           (minibuffer-local-completion-map 
            lyskom-minibuffer-local-completion-map)
           (minibuffer-local-must-match-map 
            lyskom-minibuffer-local-must-match-map)
           (read-string nil)
           (old-prompt prompt)
           (result nil)
           (keep-going t))

      (setq prompt (cond ((stringp prompt) prompt)
                         ((symbolp prompt) (lyskom-get-string prompt))
                         ((listp prompt) (apply 'lyskom-format prompt))
                         (t (lyskom-get-string 'conf-prompt))))

      (while keep-going
        (setq read-string (lyskom-completing-read prompt
                                                  'lyskom-read-conf-internal
                                                  type
                                                  mustmatch
                                                  (if (listp initial)
                                                      initial
                                                    (cons initial 0))
                                                  'lyskom-name-hist))
        (setq result
              (cond ((null read-string) nil)
                    ((string= "" read-string) nil)
                    (t (lyskom-lookup-conf-by-name read-string type))))
        (setq keep-going (and (not empty)
                              (null result))))
      (lyskom-read-conf-save-input old-prompt result)
      result)))
391
392


393
394
395
(defun lyskom-read-conf-get-logins ()
  "Used internally by lyskom-read-conf-internal to get a list of
persons who are logged on."
David Byers's avatar
David Byers committed
396
397
398
399
  (mapcar (if (lyskom-have-feature dynamic-session-info)
              (function (lambda (el) (dynamic-session-info->person el)))
            (function (lambda (el) (who-info->pers-no el))))
          (lyskom-completing-who-is-on)))
400
401


402
403
404
405
406
(defun lyskom-read-conf-expand-specials (string
                                         predicate
                                         login-list
                                         x-list
                                         &optional return-cs)
407
408
409
  "Used internally by lyskom-read-conf-internal to expand person and
conference number specifications to something useful."
  (cond ((string-match (lyskom-get-string 'person-or-conf-no-regexp) string)
410
         (let* ((no (lyskom-string-to-number (match-string 1 string)))
David Byers's avatar
David Byers committed
411
                (cs (blocking-do 'get-uconf-stat no)))
412
413
           (if (and cs
                    (lyskom-read-conf-internal-verify-type
David Byers's avatar
David Byers committed
414
415
                     (uconf-stat->conf-no cs)
                     (uconf-stat->conf-type cs)
416
417
418
                     predicate 
                     login-list
                     x-list))
419
420
421
422
               (if return-cs
                   cs
                 (list string)))))
        ((string-match (lyskom-get-string 'session-no-regexp) string)
423
         (let* ((no (lyskom-string-to-number (match-string 1 string)))
424
425
                (si (blocking-do 'get-session-info no))
                (cs (and si
David Byers's avatar
David Byers committed
426
                         (blocking-do 'get-uconf-stat
427
428
429
                                      (session-info->pers-no si)))))
           (if (and cs
                    (lyskom-read-conf-internal-verify-type
David Byers's avatar
David Byers committed
430
431
                     (uconf-stat->conf-no cs)
                     (uconf-stat->conf-type cs)
432
433
434
435
436
437
                     predicate 
                     login-list
                     x-list))
               (if return-cs
                   cs
                 (list string)))))))
438
439
440
441

(defun lyskom-read-conf-lookup-specials (string predicate login-list x-list)
  "Used internally by lyskom-read-conf-internal to look up conf-stats
from person and conference number specifications."
David Byers's avatar
David Byers committed
442
443
444
445
446
447
448
449
  (let ((cs (lyskom-read-conf-expand-specials string
                                              predicate
                                              login-list
                                              x-list
                                              t)))
    (lyskom-create-conf-z-info (uconf-stat->name cs)
                               (uconf-stat->conf-type cs)
                               (uconf-stat->conf-no cs))))
450
451

(defun lyskom-lookup-conf-by-name (string predicate)
452
  "Return the conf-z-info associated with STRING that also satisfies
453
454
PREDICATE or nil if no name matches. See lyskom-read-conf-internal for
a documentation of PREDICATE."
David Byers's avatar
X    
David Byers committed
455
456
457
  (if (string= string "")
      nil
    (lyskom-read-conf-internal string predicate 'lyskom-lookup)))
458
459
460
461
462
463


(defun lyskom-read-conf-internal (string predicate all)
  "Complete the name STRING according to PREDICATE and ALL.

STRING is a string to complete.
464
465
PREDICATE is a list of name types to return. See lyskom-read-conf for
details.
466
467
468
469
ALL is set by try-completion and all-completions. See the Emacs lisp
manual for a description. Special value 'lyskom-lookup makes the
function work as a name-to-conf-stat translator."

470
471
472
473
474
475
476
477
  ;;
  ;;  Catch some degenerate cases that can cause...problems. This
  ;;  won't solve all the...problems, but should speed things up a
  ;;  little bit.
  ;;

  (cond 
   ((and (null all)
478
         (string-match "^\\s-*$" string)) "")
479
   ((and (eq all 'lyskom-lookup)
David Byers's avatar
David Byers committed
480
         (string-match "^\\s-*$" string)) nil)
481
   ((and (eq all 'lambda)
David Byers's avatar
David Byers committed
482
         (string-match "^\\s-*$" string)) nil)
483
484
485
486
   (t

    (let* ((login-list (and (memq 'login predicate)
                            (lyskom-read-conf-get-logins)))
487
           (x-list (lyskom-completing-lookup-z-name string 
488
                                                    (if (or (memq 'all predicate)
David Byers's avatar
David Byers committed
489
                                                            (memq 'membership predicate)
490
491
492
                                                            (memq 'conf predicate)
                                                            (memq 'none predicate)) 1 0)
                                                    (if (or (memq 'all predicate)
David Byers's avatar
David Byers committed
493
                                                            (memq 'membership predicate)
494
495
496
497
498
499
500
501
502
503
504
                                                            (memq 'pers predicate)
                                                            (memq 'none predicate)
                                                            (memq 'login predicate)) 1 0)))
           (r-list (when (assq 'restrict predicate)
                     (let ((result (make-collector)))
                       (lyskom-traverse conf-no (cdr (assq 'restrict predicate))
                         (initiate-get-uconf-stat 'main 'collector-push 
                                                  conf-no result))
                       (lyskom-wait-queue 'main)
                       (delq nil
                             (mapcar (lambda (conf-stat)
505
                                       (when (lyskom-completing-match-string string (uconf-stat->name conf-stat))
506
                                         (lyskom-create-conf-z-info
507
508
509
                                          (uconf-stat->name conf-stat)
                                          (uconf-stat->conf-type conf-stat)
                                          (uconf-stat->conf-no conf-stat))))
510
                               (collector->value result))))))
511
           (candidate-list 
512
513
514
            (append r-list
                   (if x-list
                       (conf-z-info-list->conf-z-infos x-list))))
515
           (result-list nil))
516
517

      ;;
518
519
520
      ;;  login-list now contains a list of logins, IF the predicate
      ;;  includes 'login
      ;;
521
      ;;  candidate-list contains a list of conf-z-infos
522
      ;;
523
      ;;  Now set result-list to the conf-z-infos that fulfill the
524
      ;;  predicate, fetching the conf-stats asynchronously.
525
526
      ;;

527
528
529
530
531
532
533
      (lyskom-traverse el candidate-list
        (if (lyskom-read-conf-internal-verify-type (conf-z-info->conf-no el)
                                                   (conf-z-info->conf-type el)
                                                   predicate
                                                   login-list
                                                   candidate-list)
            (setq result-list (cons el result-list))))
534
      
535

536
      ;;
537
      ;;  Now the matching conf-z-infos are in result-list
538
      ;;
539

540
541
      (cond 
       ((eq all 'lyskom-lookup)
542
        (let ((names (mapcar 'conf-z-info->name 
543
544
                             result-list))
              (specials (lyskom-read-conf-expand-specials string
545
546
                                                          predicate
                                                          login-list
547
                                                          candidate-list)))
548

David Byers's avatar
David Byers committed
549
          (cond ((and kom-complete-numbers-before-names specials)
550
551
552
553
554
                 (lyskom-read-conf-lookup-specials string
                                                   predicate
                                                   login-list
                                                   candidate-list))
                ((= (length result-list) 1)
555
                 (car result-list))
David Byers's avatar
David Byers committed
556

557
558
559
560
561
562
563
564
565
                ((and (> (length result-list) 1)
                      (lyskom-completing-member string names))
                 (elt result-list
                      (- (length result-list)
                         (length (lyskom-completing-member string names)))))

                (specials (lyskom-read-conf-lookup-specials string
                                                            predicate
                                                            login-list
566
                                                            candidate-list))
567
568
                ((string-match (lyskom-get-string 'person-or-conf-no-regexp)
                               string) nil)
569
570
                ((string-match (lyskom-get-string 'session-no-regexp)
                               string) nil)
571
                ((lyskom-read-conf-internal-verify-type nil
572
573
574
                                                        nil
                                                        predicate
                                                        login-list
575
                                                        candidate-list)
576
577
578
579
580
581
582
583
584
585
586
587
                 string))))
     
       ;;
       ;;  Check for exact match. We have an exact match in the server
       ;;  when there was a single match OR when there was no match, and
       ;;  no match is valid according to predicate
       ;;

       ((eq all 'lambda)
        (let ((specials (lyskom-read-conf-expand-specials string
                                                          predicate
                                                          login-list
588
                                                          candidate-list)))
589
          (cond ((= (length result-list) 1) t)
David Byers's avatar
David Byers committed
590
591
592
593
594
                ((and (> (length result-list) 1)
                      (let ((names (mapcar 'conf-z-info->name
                                           result-list)))
                        (and (lyskom-completing-member string names)
                             t))))
595
596
597
598
599
                (result-list nil)
                ((= (length specials) 1) t)
                (specials nil)
                ((string-match (lyskom-get-string 'person-or-conf-no-regexp)
                               string) nil)
600
601
                ((string-match (lyskom-get-string 'session-no-regexp)
                               string) nil)
David Byers's avatar
David Byers committed
602

603
604
605
606
                (t (lyskom-read-conf-internal-verify-type nil
                                                          nil
                                                          predicate
                                                          login-list
607
                                                          candidate-list)))))
608
609


610
611
612
613
614
615
616
617
       ;;
       ;;  Called from all-completions. Return a list of all possible
       ;;  completions, in this case all names in the result list plus,
       ;;  if the input string is a person or conf number specification,
       ;;  the input string, PROVIDED, the requested conference matches
       ;;  the predicate. If there were no matches, return the input
       ;;  string if no matches satisfies the predicate.
       ;;
618
          
619
       (all
620
        (let ((names (mapcar 'conf-z-info->name result-list))
621
622
623
              (specials (lyskom-read-conf-expand-specials string
                                                          predicate
                                                          login-list
624
                                                          candidate-list)))
625
626
627
628
          (cond (specials (append specials names))
                (names names)
                ((string-match (lyskom-get-string 'person-or-conf-no-regexp)
                               string) nil)
629
630
                ((string-match (lyskom-get-string 'session-no-regexp)
                               string) nil)
631
632
                ((lyskom-read-conf-internal-verify-type nil
                                                        nil
633
634
                                                        predicate
                                                        login-list
635
                                                        candidate-list)
636
637
638
639
640
641
642
643
644
645
646
647
648
649
                 (list string))
                (t nil))))

       ;;
       ;;  Called from try-completion, and there were no matches. Try to
       ;;  expand the input string as a person or conf number
       ;;  specification or return something sensible if the predicate
       ;;  is satisfied by no matches.
       ;;

       ((null result-list)
        (let ((specials (lyskom-read-conf-expand-specials string
                                                          predicate
                                                          login-list
650
                                                          candidate-list)))
651
          (cond (specials (car specials))
652
653
                ((string-match (lyskom-get-string 'person-or-conf-no-regexp)
                               string) nil)
654
655
                ((string-match (lyskom-get-string 'session-no-regexp)
                               string) nil)
656
657
                ((lyskom-read-conf-internal-verify-type nil
                                                        nil
658
659
                                                        predicate
                                                        login-list
660
                                                        candidate-list)
661
                 t)
662
663
664
665
666
667
668
669
670
671
                (t nil))))

       ;;
       ;;  Called from try-completion, and there were matches in the
       ;;  server. Return t if the string is an exact match to any
       ;;  string returned from the server. Otherwise, expand the string
       ;;  as far as possible and return that
       ;;

       (t
672
        (let ((name-list (mapcar 'conf-z-info->name result-list))
673
674
675
              (specials (lyskom-read-conf-expand-specials string
                                                          predicate
                                                          login-list
David Kågedal's avatar
David Kågedal committed
676
                                                          candidate-list)))
677
          (if specials (setq name-list (nconc specials name-list)))
678

David Byers's avatar
David Byers committed
679
680
          (cond ((lyskom-completing-member string name-list) 
                 (or (and (= (length name-list) 1) t) string)) ; Exact match
681
682
683
                ((= (length name-list) 1) (car name-list))
                ((string-match (lyskom-get-string 'person-or-conf-no-regexp)
                               string) nil)
684
685
                ((string-match (lyskom-get-string 'session-no-regexp)
                               string) nil)
686
687
                (t (or (lyskom-completing-cache-completion
                        (lyskom-complete-string name-list)
688
689
690
691
                        (if r-list
                            (lyskom-create-conf-z-info-list
                             (apply 'vector candidate-list))
                          x-list))
692
693
694
695
696
697
                       (and (lyskom-read-conf-internal-verify-type 
                             nil
                             nil
                             predicate
                             login-list
                             candidate-list)
698
                            (list string))))))))))))
699
700
        

David Byers's avatar
David Byers committed
701
702
703
704
(defun lyskom-completing-member (string list)
  (let ((string (lyskom-unicase (lyskom-completing-strip-name string)))
        (result nil))
    (while (and list (not result))
705
      (if (lyskom-string= string (lyskom-unicase 
David Byers's avatar
David Byers committed
706
707
708
709
                           (lyskom-completing-strip-name (car list))))
          (setq result list)
        (setq list (cdr list))))
    result))
710

711

712
713
(defun lyskom-completing-strip-name (string)
  "Strip parens and crap from a name."
David Byers's avatar
David Byers committed
714
715
716
717
  (while (string-match "([^()]*)" string)
    (setq string (replace-match " " t t string)))
  (while (string-match "\\s-\\s-+" string)
    (setq string (replace-match " " t t string)))
718
719
  (while (string-match "([^()]*$" string)
    (setq string (substring string 0 (match-beginning 0))))
720
  (if (string-match "^\\s-*\\(.*\\S-\\)\\s-*$" string)
David Byers's avatar
David Byers committed
721
722
      (match-string 1 string)
    string))
723

724
725
726
727
728
729

(defun lyskom-read-conf-internal-verify-type (conf-no
                                              conf-type
                                              predicate
                                              logins
                                              x-list)
730
731
  (or (memq conf-no (cdr (assq 'restrict predicate)))
      (and (memq 'all predicate)
732
733
           conf-no)
      (and (memq 'conf predicate)
734
           conf-type
735
736
           (not (conf-type->letterbox conf-type)))
      (and (memq 'pers predicate) 
737
           conf-type
738
739
           (conf-type->letterbox conf-type))
      (and (memq 'login predicate)
David Byers's avatar
David Byers committed
740
           conf-no
741
           (memq conf-no logins))
David Byers's avatar
David Byers committed
742
743
744
      (and (memq 'membership predicate)
           conf-no
           (lyskom-get-membership conf-no t))
745
746
      (and (memq 'none predicate) 
           (and (null conf-no)
747
                (null x-list)))))
748

David Byers's avatar
David Byers committed
749
750
751
752
753
754
;;; FOR DEBUGGING (DON'T DELETE)     /byers
;;;
;;; (defun lyskom-complete-show-data-list (state data)
;;;   (save-excursion
;;;     (pop-to-buffer (get-buffer-create "*kom*-complete"))
;;;     (erase-buffer)
755
;;;     (lyskom-set-buffer-multibyte nil)
David Byers's avatar
David Byers committed
756
757
758
759
760
761
762
763
764
765
;;;    (while data
;;;       (insert
;;;        (format "%s\n" (substring (aref (car data) 2)
;;;                                  (aref (car data) 0)
;;;                                  (aref (car data) 1))))
;;;       (setq data (cdr data)))
;;;     (insert (format "%S %S: %S" (symbol-value current-state)
;;;                     (elt state 0)
;;;                     (elt state 1)))
;;;     (sit-for 5)))
766

767

768
769
770
771
772
773
774
775
776
777
778
(defun lyskom-complete-string (string-list)
  "Find the longest common prefix of all strings in STRING-LIST according to
the LysKOM rules of string matching."
  (let ((main-state 'start-of-string)
        (tmp-state nil)
        (current-state 'main-state)
        (main-accumulator nil)
        (tmp-accumulator nil)
        (current-accumulator 'main-accumulator)
        (done nil)
        (paren-depth 0)
779
780
        (have-here nil)
        (last-event-worth-noting nil)
781
782
783
784
785
        (data-list (lyskom-complete-string-munge-input string-list))
        (next-char-state (vector nil nil)))

    (while (not done)
      (lyskom-complete-string-next-char next-char-state data-list)
786
;      (lyskom-complete-show-data-list next-char-state data-list)
787
788
789
790
791
792
793
794
      (cond

       ;;
       ;; Case one, a match of two non-special characters.
       ;; Accumulate one character and advance the lists
       ;;

       ((eq (aref next-char-state 0) 'match)
David Byers's avatar
David Byers committed
795
796
        (if (eq (aref next-char-state 1) ?\ )
            (progn
797
              (cond ((memq (symbol-value current-state)
798
			     '(start-of-word start-of-string))
David Byers's avatar
David Byers committed
799
800
801
802
803
804
805
806
807
                     nil)
                    ((eq last-event-worth-noting 'mismatch)
                     (lyskom-complete-string-accumulate current-accumulator
                                                        'SPC))
                    (t
                     (lyskom-complete-string-accumulate current-accumulator
                                                        ?\ )))
              (set current-state 'start-of-word)
              (lyskom-complete-string-advance data-list))
808
          (progn
David Byers's avatar
David Byers committed
809
            (set current-state 'in-a-word)
810
            (lyskom-complete-string-accumulate current-accumulator
David Byers's avatar
David Byers committed
811
812
813
                                               (aref next-char-state 1))
            (lyskom-complete-string-advance data-list)))
        (setq last-event-worth-noting 'match))
814
815
816
817
818
819
820
821
822
       
       ;;
       ;; Case two, a match of two open-paren expressions Increase
       ;; paren depth and accumulate a character. First set
       ;; current-accumulator to the temporary if paren-depth is zero
       ;; to start with.
       ;;

       ((eq (aref next-char-state 0) 'open-paren-match)
823
        (setq last-event-worth-noting 'match)
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
        (if (zerop paren-depth)
            (progn
              (setq current-accumulator 'tmp-accumulator)
              (setq current-state 'tmp-state)
              (setq tmp-state main-state)
              (setq tmp-accumulator nil)))
        (setq paren-depth (1+ paren-depth))
        (lyskom-complete-string-accumulate current-accumulator
                                    (aref next-char-state 1))
        (lyskom-complete-string-advance data-list))

       ;;
       ;; Case three, a match of two close-paren expressions
       ;; Accumulate a character. If paren-depth is postitive,
       ;; decrease it. If it ends up zero, add the temporary
       ;; accumulator to the main accumulator and set the current
       ;; accumulator to the main accumulator.
       ;;

       ((eq (aref next-char-state 0) 'close-paren-match)
844
        (setq last-event-worth-noting 'match)
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
        (lyskom-complete-string-accumulate current-accumulator
                                    (aref next-char-state 1))
        (if (> paren-depth 0)
            (progn
              (setq paren-depth (1- paren-depth))
              (if (zerop paren-depth)
                  (progn
                    (setq main-accumulator
                          (nconc tmp-accumulator main-accumulator))
                    (setq main-state tmp-state)
                    (setq current-state 'main-state)
                    (setq current-accumulator 'main-accumulator)))))
        (lyskom-complete-string-advance data-list))

       ;;
       ;; Case two, a mismatch of any kind in a paren expression
       ;;

       ((and (> paren-depth 0)
864
             (memq (aref next-char-state 0)
865
		     '(mismatch space-mismatch open-paren-mismatch)))
866
        (setq last-event-worth-noting 'mismatch)
867
868
869
870
871
872
873
874
875
876
877
878
879
        (setq tmp-accumulator nil)
        (setq tmp-state nil)
        (setq current-state 'main-state)
        (setq current-accumulator 'main-accumulator)
        (lyskom-complete-string-close-parens data-list paren-depth)
        (setq paren-depth 0))

       ;;
       ;; Case two and a half or so, a space mismatch. This is ignored
       ;; if we're still at the start of the string
       ;;
       
       ((and (eq (aref next-char-state 0) 'space-mismatch)
880
             (memq (symbol-value current-state)
881
		     '(start-of-string start-of-word)))
David Byers's avatar
David Byers committed
882
        (setq last-event-worth-noting nil)
883
884
885
886
887
888
        (lyskom-complete-string-skip-whitespace data-list))

       ;;
       ;; Case three, a mismatch of regular characters outside a paren
       ;; Advance to the end of the current word
       ;;
889

890
       ((and (memq (aref next-char-state 0) '(mismatch space-mismatch))
891
             (zerop paren-depth))
David Byers's avatar
David Byers committed
892
        (setq last-event-worth-noting 'mismatch)
893
        (if (memq (symbol-value current-state)
894
		    '(start-of-word start-of-string))
895
896
            (setq done t)
          (progn
897
898
899
900
901
            (if (not have-here)
                (progn
                  (lyskom-complete-string-accumulate current-accumulator 
                                                     'HERE)
                  (setq have-here t)))
902
903
            (lyskom-complete-string-advance-to-end-of-word data-list)
            (set current-state 'in-a-word))))
904

905
906
907
       ;;
       ;; Case four, a mistmatch where one character is an open-paren
       ;;
908

909
       ((eq (aref next-char-state 0) 'open-paren-mismatch)
910
        (setq last-event-worth-noting 'mismatch)
911
        (lyskom-complete-string-skip-parens data-list))
912
913


914
915
916
       ;;
       ;; Case five, eof
       ;;
917

918
919
       ((eq (aref next-char-state 0) 'eof)
        (setq done t))
920

921
922
923
924
925
926
927
928
929
930
931
       ;;
       ;; Case six, can't happen
       ;;

       (t (error "This can't happen: %S" next-char-state))))

    ;;
    ;; Build the result by reversing the result list and making a
    ;; string out of it.
    ;;

David Byers's avatar
David Byers committed
932
933
    (if (eq (car main-accumulator) 'SPC)
        (setq main-accumulator (cdr main-accumulator)))
934
935
936

    (setq main-accumulator (nreverse main-accumulator))

David Byers's avatar
David Byers committed
937
938
939
    (if (memq 'HERE main-accumulator)
        (let ((backup (length (memq 'HERE main-accumulator))))
          (if lyskom-experimental-features
940
941
              (setq unread-command-events
                    (append (cons ? (make-list (1- backup) 2))
David Byers's avatar
David Byers committed
942
943
                            unread-command-events)))
          (setq main-accumulator (delq 'HERE main-accumulator))))
944
945
946
    
    (concat (mapcar (lambda (el) (if (eq el 'SPC) ?\  el))
		    main-accumulator))))
947

948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964

(defun lyskom-complete-string-accumulate (accumulator char)
  (set accumulator (cons char (symbol-value accumulator))))

(defun lyskom-complete-string-munge-input (string-list)
  (mapcar (function
           (lambda (x)
             (vector 0 (length x) x)))
          string-list))

;;;
;;; Advance one regular character or multiple whitespaces
;;;

(defun lyskom-complete-string-advance (data-list)
  (lyskom-traverse 
   el data-list
965
   (string-match "\\([ \t]+\\|[^ \t]\\|$\\)"
966
967
968
969
970
971
972
                 (aref el 2)
                 (aref el 0))
   (aset el 0 (match-end 0))))

(defun lyskom-complete-string-skip-whitespace (data-list)
  (lyskom-traverse
   el data-list
973
   (string-match "[ \t]*" (aref el 2) (aref el 0))
974
975
976
977
978
979
980
981
982
   (aset el 0 (match-end 0))))

;;;
;;; Advance to the end of the current word
;;;

(defun lyskom-complete-string-advance-to-end-of-word (data-list)
  (lyskom-traverse
   el data-list
983
   (aset el 0 (string-match "\\([ \t]\\|$\\)" 
984
985
986
987
988
989
                            (aref el 2)
                            (aref el 0)))))

;;;
;;; Unwind a number of parens
;;;
990

991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
(defun lyskom-complete-string-skip-parens (data-list)
  (lyskom-traverse
   el data-list
   (if (eq ?\( (aref (aref el 2) (aref el 0)))
       (progn
         (aset el 0 (1+ (aref el 0)))
         (lyskom-complete-string-close-parens-2 el 1)))))

(defun lyskom-complete-string-close-parens (data-list depth)
  (lyskom-traverse
   el data-list
   (lyskom-complete-string-close-parens-2 el depth)))

(defun lyskom-complete-string-close-parens-2 (el depth)
David Byers's avatar
David Byers committed
1005
  (let ((string (aref el 2))
David Byers's avatar
David Byers committed
1006
        (pos (aref el 0)))
1007
    (while (> depth 0)
David Byers's avatar
David Byers committed
1008
1009
      (cond ((>= pos (length string)) 
             (setq depth 0))
David Byers's avatar
David Byers committed
1010
            ((eq (aref string pos) ?\))
David Byers's avatar
David Byers committed
1011
             (setq depth (1- depth)))
David Byers's avatar
David Byers committed
1012
            ((eq (aref string pos) ?\))
David Byers's avatar
David Byers committed
1013
1014
1015
1016
             (setq depth (1+ depth))))
      (setq pos (1+ pos)))
    (aset el 0 pos)))

1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030

;;;
;;; Check what's happenin' next
;;;

(defun lyskom-complete-string-next-char (state data-list)
  (let ((eofp nil)
        (open-paren-p nil)
        (close-paren-p nil)
        (matchp t)
        (spacep nil)
        (char nil)
        (xchar nil))

1031
    (lyskom-save-excursion
David Byers's avatar
X    
David Byers committed
1032
     (set-buffer lyskom-buffer)
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
     (mapcar
      (function 
       (lambda (x)
         (cond ((>= (aref x 0) (aref x 1))
                (setq eofp t)
                (setq matchp nil))
               ((eq (aref (aref x 2) (aref x 0)) ?\()
                (setq open-paren-p t))
               ((eq (aref (aref x 2) (aref x 0)) ?\))
                (setq close-paren-p t))
               ((eq (aref (aref x 2) (aref x 0)) ?\ )
                (setq spacep t)))

         (setq matchp (and matchp
                           (if (null char)
                               (progn
                                 (setq xchar (aref (aref x 2)
                                                   (aref x 0)))
                                 (setq char (lyskom-unicase-char xchar)))
                             (eq char (lyskom-unicase-char
                                       (aref (aref x 2)
                                             (aref x 0)))))))))
      data-list))
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072

    (aset state 1 xchar)
    (cond (eofp (aset state 0 'eof))
          ((and matchp open-paren-p)
           (aset state 0 'open-paren-match))
          ((and matchp close-paren-p)
           (aset state 0 'close-paren-match))
          (matchp
           (aset state 0 'match))
          (spacep
           (aset state 0 'space-mismatch))
          (open-paren-p
           (aset state 0 'open-paren-mismatch))
          (t
           (aset state 0 'mismatch))))
  state)

1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085







;;; ============================================================
;;;
;;; Session reading
;;;
;;;

Per Cederqvist's avatar
.    
Per Cederqvist committed
1086
1087


1088
(defun lyskom-read-session-no (prompt &optional empty initial only-one)
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
  (let ((possible-matches
         (lyskom-session-from-conf
          (lyskom-read-conf-no prompt
                               (if kom-permissive-completion
                                   '(pers)
                                 '(login))
                               empty
                               initial
                               t))))
    (if (and (> (length possible-matches) 1)
             only-one)
        (lyskom-read-session-resolve-ambiguity possible-matches)
      possible-matches)))


(defun lyskom-session-from-conf (conf-no)
  (let ((who-list (lyskom-completing-who-is-on))
        (sessions nil))
David Byers's avatar
David Byers committed
1107
    (if (lyskom-have-feature dynamic-session-info)
1108
	(while who-list
David Byers's avatar
David Byers committed
1109
	  (if (eq (dynamic-session-info->person (car who-list)) conf-no)
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
	      (setq sessions (cons (dynamic-session-info->session
				    (car who-list))
				   sessions)))
	  (setq who-list (cdr who-list)))
      (while who-list
	(if (eq (who-info->pers-no (car who-list))
		conf-no)
	    (setq sessions (cons (who-info->connection (car who-list))
				 sessions)))
	(setq who-list (cdr who-list))))
1120
1121
    (cond ((and (null sessions) kom-permissive-completion) (list (- conf-no)))
          (t sessions))))
1122
1123
1124
1125


(defun lyskom-read-session-resolve-ambiguity (sessions)
  (lyskom-insert "\n")
David Kågedal's avatar
David Kågedal committed
1126
1127
  (let* ((s-width (1+ (apply 'max (mapcar (function
					   (lambda (x)
1128
					     (lyskom-string-width (int-to-string x))))
David Kågedal's avatar
David Kågedal committed
1129
1130
1131
					  sessions))))
	 (format-string-s (lyskom-info-line-format-string s-width "s" "s"))
	 (format-string-p (lyskom-info-line-format-string s-width "P" "M")))
1132
    (lyskom-format-insert format-string-s
David Kågedal's avatar
David Kågedal committed
1133
			  ""
1134
1135
1136
			  (lyskom-get-string 'lyskom-name)
			  (lyskom-get-string 'is-in-conf))
    (lyskom-format-insert format-string-s
David Kågedal's avatar
David Kågedal committed
1137
			  ""
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
			  (lyskom-get-string 'from-machine)
			  (lyskom-get-string 'is-doing))
    (lyskom-insert
     (concat (make-string (- (lyskom-window-width) 2) ?-)
	     "\n"))
    (let ((result nil)
	  (who-info
	   (mapcar (function
		    (lambda (el)
		      (let* ((info (blocking-do 'get-session-info el))
			     (confconfstat
David Byers's avatar
David Byers committed
1149
			      (blocking-do 'get-uconf-stat
1150
1151
1152
					   (session-info->working-conf info))))
			(lyskom-format-insert
			 format-string-p
David Kågedal's avatar
David Kågedal committed
1153
1154
1155
1156
1157
1158
			 (format "%d%s"
				 (session-info->connection info)
				 (if (eq (session-info->connection info)
					 lyskom-session-no)
				     "*" " "))
			 (session-info->pers-no info)
1159
1160
			 (or confconfstat
                             (lyskom-get-string 'not-present-anywhere)))
1161
1162
			(lyskom-format-insert
			 format-string-p
David Kågedal's avatar
David Kågedal committed
1163
			 ""
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
			 (lyskom-return-username info)
			 (concat "("
				 (session-info->doing info)
				 ")"))
			(cons (number-to-string
			       (session-info->connection info))
			      info))))
		   (sort sessions '<))))
      (lyskom-insert (concat (make-string (- (lyskom-window-width) 2) ?-)
			     "\n"))
1174
      (lyskom-insert (lyskom-format 'total-users-sans-date (length who-info)))
1175
1176
      (lyskom-scroll)
      (while (string= ""
1177
                      (setq result (lyskom-completing-read
1178
				    (lyskom-get-string 'resolve-session)
David Byers's avatar
David Byers committed
1179
1180
				    (lyskom-maybe-frob-completion-table 
				     who-info)
1181
1182
1183
				    nil
				    t
				    (car (car who-info))
1184
				    nil))))
1185
      (list (session-info->connection (cdr (assoc result who-info)))))))
1186
1187
1188