Merge branch 'vim' into feat/emb-common-lisp
[vim_extended.git] / src / if_ecl.c
blob026019c473f440a504df8827496623920d563513
1 /* vi:ts=8:sts=4:sw=4:iskeyword+=-
3 * VIM - Vi IMproved by Bram Moolenaar
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9 /*
10 * ECL (Embeddable Common-Lisp) extension by Jim Bailey.
12 * Provides the "ecl" ex command for evalutating lisp forms,
13 * and a "VIM" package for querying/updating vim from lisp.
15 * The ecl command can take an argument, e.g. :ecl (print 42)
16 * or a range to evalutate forms from a buffer, e.g. :%ecl
18 * The "VIM" package is written and documented in lisp, see the
19 * very first few static constants defined in this file for details.
21 * TODO: Help files need to be written so everything is documented from
22 * vim (the documentation is available from CL at least).
24 * TODO: ECL has undocumented threading capabilities, if threads are
25 * used by someone who knows how, there are threading issues with
26 * vim (which is single threaded). This appears to be unresolved in
27 * other interfaces as well.
30 #include "vim.h"
31 #undef CAR
32 #include "ecl/ecl.h"
33 #include "ecl/ecl-inl.h"
34 #include <signal.h>
36 static const char *g_vim_package_definition =
37 "(defpackage :vim \n"
38 " (:use cl) \n"
39 " (:export #:msg \n"
40 " #:execute \n"
41 " #:expr \n"
42 " ;; a cons of (start-line . end-line) set when :ecl is called \n"
43 " #:range \n"
44 " \n"
45 " #:window \n"
46 " #:windows \n"
47 " #:current-window \n"
48 " #:window-width \n"
49 " #:window-height \n"
50 " #:window-column \n"
51 " #:window-cursor \n"
52 " #:window-buffer \n"
53 " \n"
54 " #:buffer \n"
55 " #:buffers \n"
56 " #:current-buffer \n"
57 " #:buffer-line-count \n"
58 " #:buffer-lines \n"
59 " #:buffer-name \n"
60 " #:append-line-to-buffer \n"
61 " #:append-to-buffer \n"
62 " #:get-buffer-by-name \n"
63 " \n"
64 " #:get-line \n"
65 " #:replace-lines \n"
66 " ))";
68 /*
69 * This core form must be kept minimal, as it is run without
70 * any error output, making debugging very difficult.
72 static const char *g_vim_package_source_core =
73 "(progn \n"
74 " (defun msg (str &optional (start 0) (end (length str))) \n"
75 " \"writes a message line to the screen, it must not contain newlines\" \n"
76 " (check-type str string) \n"
77 " (check-type start fixnum) \n"
78 " (check-type end fixnum) \n"
79 " (msg-int str start end)) \n"
80 " \n"
81 " ;; *standard-output* and *error-output* must be redirected to use \n"
82 " ;; vim's msg(). This is done using ECL's gray streams. \n"
83 " (defclass msg-stream (gray::fundamental-character-output-stream) \n"
84 " ((buffer :initform \"\"))) \n"
85 " \n"
86 " (defmethod gray:stream-write-char ((strm msg-stream) char) \n"
87 " (with-slots (buffer) strm \n"
88 " (cond \n"
89 " ((char= char #\\newline) \n"
90 " (msg buffer) \n"
91 " (setf buffer \"\")) \n"
92 " (t \n"
93 " (setf buffer (concatenate 'string buffer (string char))))))) \n"
94 " \n"
95 " (cl:setq *standard-output* (make-instance 'msg-stream)) \n"
96 " (cl:setq *error-output* *standard-output*) \n"
97 " \n"
98 " ;; Using msg() does not work very well unless executing an ex cmd. \n"
99 " ;; At all other times it is best to send output to a special ecl buffer. \n"
100 " (defun buffer-append-char (buffer char) \n"
101 " (let ((tail (buffer-line-count buffer))) \n"
102 " (if (char= char #\\newline) \n"
103 " (replace-lines (list \"\") \n"
104 " :start tail :end tail \n"
105 " :buffer buffer) \n"
106 " (replace-lines (list (concatenate 'string \n"
107 " (get-line (1- tail) buffer) \n"
108 " (string char))) \n"
109 " :start (1- tail) \n"
110 " :end tail \n"
111 " :buffer buffer)))) \n"
112 " \n"
113 " (defclass vim-buf-stream (gray::fundamental-character-output-stream) \n"
114 " ((buffer :accessor buffer :initform nil))) \n"
115 " \n"
116 " (defmethod gray:stream-line-column ((strm msg-stream)) \n"
117 " (with-slots (buffer) strm \n"
118 " (length buffer))) \n"
119 " \n"
120 " (defmethod gray:stream-write-char ((strm vim-buf-stream) char) \n"
121 " (unless (and (buffer strm) \n"
122 " (find (buffer strm) (buffers) :test 'equal)) \n"
123 " (execute \"new\") \n"
124 " (setf (buffer strm) (current-buffer))) \n"
125 " (buffer-append-char (buffer strm) char)) \n"
126 " \n"
127 " (defvar *vim-buf-stream* (make-instance 'vim-buf-stream)) \n"
128 " \n"
129 " (defun safe-eval (form from-ex) \n"
130 " \"evaluates a form, reporting any errors\" \n"
131 " (handler-case \n"
132 " (if from-ex \n"
133 " (progn \n"
134 " (when (stringp form) \n"
135 " (cl:setq form (read-from-string form))) \n"
136 " (eval form) \n"
137 " (fresh-line *standard-output*)) \n"
138 " (let ((*standard-output* *vim-buf-stream*) \n"
139 " (*error-output* *vim-buf-stream*)) \n"
140 " (eval form))) \n"
141 " (error (cnd) \n"
142 " (format t \"ERROR: ~a~%\" cnd)))) \n"
143 " )";
146 * Global packages and symbols.
148 static cl_object g_vim_package;
149 static cl_object g_window_symbol;
150 static cl_object g_buffer_symbol;
152 static cl_object intern_vim(const char *name)
154 return cl_intern(2, make_base_string_copy(name), g_vim_package);
157 static char *string_to_line(cl_object string)
159 return vim_strnsave(string->base_string.self, string->base_string.fillp);
163 * Copied from if_python.c, thanks.
165 static void fix_cursor(int lo, int hi, int extra)
167 if (curwin->w_cursor.lnum >= lo)
169 /* Adjust the cursor position if it's in/after the changed
170 * lines. */
171 if (curwin->w_cursor.lnum >= hi)
173 curwin->w_cursor.lnum += extra;
174 check_cursor_col();
176 else if (extra < 0)
178 curwin->w_cursor.lnum = lo;
179 check_cursor();
181 changed_cline_bef_curs();
183 invalidate_botline();
186 static char *zero_terminate(cl_object string)
188 int length = string->base_string.fillp;
189 char *buf = alloc(length + 1);
190 memcpy(buf, string->base_string.self, length);
191 buf[length] = 0;
192 return buf;
196 * vim callbacks
199 static cl_object cl_vim_msg_int(cl_narg narg, cl_object string, cl_object start, cl_object end)
201 int start_pos = fix(start);
202 int end_pos = fix(end);
203 int length = end_pos - start_pos;
204 char *buf;
206 if (length < 0)
207 return Cnil;
209 buf = alloc(length + 1);
210 memcpy(buf, string->base_string.self + start_pos, length);
211 buf[length] = 0;
212 msg(buf);
213 vim_free(buf);
215 return Ct;
218 static cl_object cl_vim_execute_int(cl_object cmd)
220 char *buf = zero_terminate( cmd );
221 do_cmdline_cmd(buf);
222 vim_free(buf);
224 return Ct;
227 static cl_object vim_type_to_cl_object(typval_T *tv)
229 cl_object cl_result = Cnil;
230 switch (tv->v_type) {
231 case VAR_LIST:
233 /* NOTE: Traverse the list back to front -- don't have to
234 * NREVERSE at the end that way. */
235 listitem_T *item = tv->vval.v_list->lv_last;
236 while (item) {
237 cl_result = CONS( vim_type_to_cl_object( &item->li_tv ),
238 cl_result );
239 item = item->li_prev;
242 break;
243 case VAR_STRING:
244 if (tv->vval.v_string != NULL)
245 cl_result = make_base_string_copy(tv->vval.v_string);
246 break;
247 case VAR_NUMBER:
249 long num = (long)tv->vval.v_number;
250 if (num < MOST_NEGATIVE_FIXNUM
251 || num > MOST_POSITIVE_FIXNUM)
253 /* Make a BIGNUM */
254 char num_buf[NUMBUFLEN];
255 sprintf((char *)num_buf, "%ld", (long)num);
256 cl_result = c_string_to_object(num_buf);
257 } else
258 cl_result = MAKE_FIXNUM(num);
260 break;
261 case VAR_UNKNOWN:
262 EMSG2(_(e_intern2), "vim_type_to_cl_object(VAR_UNKNOWN)");
263 break;
264 case VAR_DICT:
265 EMSG2(_(e_intern2), "vim_type_to_cl_object(VAR_DICT)");
266 break;
267 case VAR_FUNC:
268 EMSG2(_(e_intern2), "vim_type_to_cl_object(VAR_FUNC)");
269 break;
270 default:
271 EMSG2(_(e_intern2), "vim_type_to_cl_object()");
272 break;
274 return cl_result;
277 static cl_object cl_vim_expr_int(cl_object cmd)
279 typval_T *tv;
281 char *buf = zero_terminate(cmd);
282 cl_object cl_result = Cnil;
284 tv = eval_expr(buf, NULL);
285 if (tv != NULL) {
286 cl_result = vim_type_to_cl_object(tv);
287 clear_tv(tv);
290 vim_free(buf);
292 return cl_result;
295 static cl_object cl_vim_kill_int (cl_object pid, cl_object sig)
297 #ifndef FEAT_GUI_W32
298 int fixed_pid = fix(pid);
299 int fixed_sig = fix(sig);
300 if (kill (fixed_pid, fixed_sig) == 0)
301 return Ct;
302 else
303 return Cnil;
304 #endif
308 * windows
311 static cl_object cl_vim_windows_int()
313 cl_object result = Cnil;
314 win_T *vwin = firstwin;
316 while (vwin)
318 result = CONS(ecl_make_foreign_data(g_window_symbol,
319 sizeof(win_T *),
320 vwin),
321 result);
322 vwin = W_NEXT(vwin);
325 return cl_nreverse(result);
328 static cl_object cl_vim_current_window_int()
330 return ecl_make_foreign_data(g_window_symbol,
331 sizeof(win_T *),
332 curwin);
335 static cl_object cl_vim_window_width_int(cl_object win_)
337 win_T *win = ((win_T *)ecl_foreign_data_pointer_safe(win_));
339 return MAKE_FIXNUM(win->w_width);
342 static cl_object cl_vim_window_height_int(cl_object win_)
344 win_T *win = ((win_T *)ecl_foreign_data_pointer_safe(win_));
346 return MAKE_FIXNUM(win->w_height);
349 static cl_object cl_vim_window_column_int(cl_object win_)
351 win_T *win = ((win_T *)ecl_foreign_data_pointer_safe(win_));
353 return MAKE_FIXNUM(W_WINCOL(win));
356 static cl_object cl_vim_window_cursor_int(cl_object win_)
358 win_T *win = ((win_T *)ecl_foreign_data_pointer_safe(win_));
360 return CONS(MAKE_FIXNUM(win->w_cursor.lnum - 1),
361 MAKE_FIXNUM(win->w_cursor.col));
364 static cl_object cl_vim_window_buffer_int(cl_object win_)
366 win_T *win = ((win_T *)ecl_foreign_data_pointer_safe(win_));
368 return ecl_make_foreign_data(g_buffer_symbol,
369 sizeof(buf_T *),
370 win->w_buffer);
374 * buffers
377 static cl_object cl_vim_buffers_int()
379 cl_object result = Cnil;
380 buf_T *vbuf = firstbuf;
382 while (vbuf)
384 result = CONS(ecl_make_foreign_data(g_buffer_symbol,
385 sizeof(buf_T *),
386 vbuf),
387 result);
388 vbuf = vbuf->b_next;
391 return cl_nreverse(result);
394 static cl_object cl_vim_current_buffer_int()
396 return ecl_make_foreign_data(g_buffer_symbol,
397 sizeof(buf_T *),
398 curbuf);
401 static cl_object cl_vim_buffer_line_count_int(cl_object buf_)
403 buf_T *buf = ((buf_T *)ecl_foreign_data_pointer_safe(buf_));
405 return MAKE_FIXNUM(buf->b_ml.ml_line_count);
409 start_ and end_ and fixnums in the range 0..(num_lines-1)
412 static cl_object cl_vim_buffer_lines_int(cl_object buf_, cl_object start_, cl_object end_)
414 buf_T *buf = ((buf_T *)ecl_foreign_data_pointer_safe(buf_));
415 int start = fix(start_) + 1;
416 int end = fix(end_);
417 cl_object result = Cnil;
419 while (end >= start)
421 result = CONS(make_base_string_copy(ml_get_buf(buf, end--, FALSE)),
422 result);
425 return result;
428 static cl_object cl_vim_buffer_name_int (cl_object buf_)
430 buf_T *buf = ((buf_T *)ecl_foreign_data_pointer_safe(buf_));
431 if (buf->b_fname == NULL)
432 return Cnil;
433 else
434 return make_base_string_copy (buf->b_fname);
437 static cl_object cl_vim_append_string_int (cl_object buf, cl_object string)
439 buf_T *savebuf = curbuf;
440 int start_line;
441 curbuf = ((buf_T *)ecl_foreign_data_pointer_safe(buf));
442 start_line = curbuf->b_ml.ml_line_count;
444 if (string != Cnil)
446 char_u *line = string_to_line (string);
447 ml_append_string (start_line, line, -1);
448 //ml_append_string (curbuf->b_ml.ml_line_count-1, string->base_string.self, string->base_string.fillp);
450 changed_lines(start_line, 0, curbuf->b_ml.ml_line_count, 1);
452 /* restore and return */
453 curbuf = savebuf;
454 return Ct;
457 static cl_object cl_vim_append_lines_int (cl_object buf, cl_object lines)
459 buf_T *savebuf = curbuf;
460 int start_line, first_line;
461 curbuf = ((buf_T *)ecl_foreign_data_pointer_safe(buf));
462 start_line = curbuf->b_ml.ml_line_count;
463 first_line = 1;
464 if (start_line == 0) start_line = 1;
466 while (lines != Cnil) {
467 char_u *line = string_to_line (cl_car (lines));
468 if (first_line)
470 ml_append_string (start_line, line, -1);
471 first_line = 0;
473 else
474 ml_append (start_line, line, 0, FALSE);
475 vim_free (line);
476 lines = cl_cdr (lines);
478 changed_lines(start_line, 0, start_line, (long)(curbuf->b_ml.ml_line_count - start_line));
480 /* restore and return */
481 curbuf = savebuf;
482 return Ct;
485 static cl_object cl_vim_append_char_int (cl_object buf, cl_object ecl_char)
487 static char string[2] = {0};
488 buf_T *savebuf = curbuf;
489 int start_line, lines_changed;
490 curbuf = ((buf_T *)ecl_foreign_data_pointer_safe(buf));
491 start_line = curbuf->b_ml.ml_line_count;
492 lines_changed = 0;
494 if (ecl_char != Cnil)
496 string[0] = CHAR_CODE (ecl_char);
497 if (string[0] == '\n')
499 ml_append (start_line, "", 0, FALSE);
500 lines_changed = 1;
502 else
503 ml_append_string (start_line, string, -1);
504 //ml_append_string (curbuf->b_ml.ml_line_count-1, string->base_string.self, string->base_string.fillp);
506 changed_lines (start_line, 0, start_line, lines_changed);
508 /* restore and return */
509 curbuf = savebuf;
510 return Ct;
513 start1_ and end1_ are fixnums in the range 0..(num_lines-1)
516 static cl_object cl_vim_replace_lines_int(cl_object buf, cl_object lines, cl_object start1_, cl_object end1_, cl_object start2_, cl_object end2_)
518 linenr_T start1 = (linenr_T)fix(start1_) + 1;
519 linenr_T end1 = (linenr_T)fix(end1_) + 1;
520 int start2 = fix(start2_);
521 int new_len = fix(end2_);
522 int old_len = end1 - start1;
523 int max_len;
524 int i;
525 buf_T *savebuf = curbuf;
527 curbuf = ((buf_T *)ecl_foreign_data_pointer_safe(buf));
529 if (start2 > 0)
531 /* take off the head of the list */
532 lines = cl_nthcdr(start2_, lines);
533 new_len -= start2;
536 max_len = new_len > old_len ? new_len : old_len;
538 /* save undo information
539 * Need to restrict the length to the buffer size or
540 * the u_save fails
542 if (start1 + max_len > curbuf->b_ml.ml_line_count + 1)
543 u_save(start1 - 1, curbuf->b_ml.ml_line_count + 1);
544 else
545 u_save(start1 - 1, start1 + max_len);
547 /* delete excess lines */
548 for (i = 0; i < old_len - new_len; ++i)
550 ml_delete(start1, FALSE);
553 /* replace existing lines */
554 for (i = 0; i < old_len && i < new_len; ++i)
556 ml_replace(start1 + i, string_to_line(cl_car(lines)), FALSE);
557 lines = cl_cdr(lines);
560 /* add new lines (must be freed) */
561 while (i < new_len)
563 char_u *line = string_to_line(cl_car(lines));
564 ml_append(start1 + i - 1, line, 0, FALSE);
565 vim_free(line);
566 lines = cl_cdr(lines);
567 ++i;
570 /* adjust marks */
571 mark_adjust(start1, end1 - 1,
572 (long)MAXLNUM, (long)(new_len - old_len));
573 changed_lines(start1, 0, end1, (long)(new_len - old_len));
575 /* fix cursor */
576 if (curbuf == savebuf)
577 fix_cursor(start1, end1, (new_len - old_len));
579 /* restore and return */
580 curbuf = savebuf;
581 return Ct;
585 * helpers
588 static cl_object eval_string(const char *form)
590 return cl_eval(c_string_to_object(form));
593 static cl_object safe_eval_form(cl_object form, int from_ex)
595 /* uses vim::safe-eval to trap errors */
596 static cl_object safe_eval = 0;
598 if (safe_eval == 0)
600 /* get the safe eval and quote symbols */
601 safe_eval = intern_vim("SAFE-EVAL");
604 /* this is (vim::safe-eval form from_ex) */
605 return si_eval_with_env(1, cl_list(3,
606 safe_eval,
607 form,
608 from_ex == TRUE ? Ct : Cnil));
611 static cl_object safe_eval_string(const char *string, int from_ex)
613 return safe_eval_form(make_base_string_copy(string), from_ex);
616 static void
617 RunEclCommand(exarg_T *eap, const char *cmd)
619 static int have_inited = 0;
620 static cl_object range;
622 if (!have_inited)
624 static char *argv[] = {"ecl", 0};
625 have_inited = 1;
626 cl_boot(1, argv);
628 /* create the vim package */
629 g_vim_package = eval_string(g_vim_package_definition);
631 /* add the lisp->c functions */
632 cl_def_c_function_va(intern_vim("MSG-INT"), cl_vim_msg_int);
633 cl_def_c_function(intern_vim("EXECUTE-INT"), cl_vim_execute_int, 1);
634 cl_def_c_function(intern_vim("EXPR-INT"), cl_vim_expr_int, 1);
635 cl_def_c_function(intern_vim("KILL-INT"), cl_vim_kill_int, 2);
637 g_window_symbol = intern_vim("WINDOW");
639 cl_def_c_function(intern_vim("WINDOWS-INT"),
640 cl_vim_windows_int, 0);
641 cl_def_c_function(intern_vim("CURRENT-WINDOW-INT"),
642 cl_vim_current_window_int, 0);
643 cl_def_c_function(intern_vim("WINDOW-WIDTH-INT"),
644 cl_vim_window_width_int, 1);
645 cl_def_c_function(intern_vim("WINDOW-HEIGHT-INT"),
646 cl_vim_window_height_int, 1);
647 cl_def_c_function(intern_vim("WINDOW-COLUMN-INT"),
648 cl_vim_window_column_int, 1);
649 cl_def_c_function(intern_vim("WINDOW-CURSOR-INT"),
650 cl_vim_window_cursor_int, 1);
651 cl_def_c_function(intern_vim("WINDOW-BUFFER-INT"),
652 cl_vim_window_buffer_int, 1);
655 g_buffer_symbol = intern_vim("BUFFER");
657 cl_def_c_function(intern_vim("BUFFERS-INT"),
658 cl_vim_buffers_int, 0);
659 cl_def_c_function(intern_vim("CURRENT-BUFFER-INT"),
660 cl_vim_current_buffer_int, 0);
661 cl_def_c_function(intern_vim("BUFFER-LINE-COUNT-INT"),
662 cl_vim_buffer_line_count_int, 1);
663 cl_def_c_function(intern_vim("BUFFER-LINES-INT"),
664 cl_vim_buffer_lines_int, 3);
665 cl_def_c_function(intern_vim("BUFFER-NAME-INT"),
666 cl_vim_buffer_name_int, 1);
669 cl_def_c_function(intern_vim("REPLACE-LINES-INT"),
670 cl_vim_replace_lines_int, 6);
671 cl_def_c_function(intern_vim("APPEND-LINES-INT"),
672 cl_vim_append_lines_int, 2);
673 cl_def_c_function(intern_vim("APPEND-STRING-INT"),
674 cl_vim_append_string_int, 2);
675 cl_def_c_function(intern_vim("APPEND-CHAR-INT"),
676 cl_vim_append_char_int, 2);
678 /* Eval the vim package source,
679 * the minimal core sets up the "safe" version. */
680 eval_string("(in-package :vim)");
681 eval_string(g_vim_package_source_core);
683 /* return to cl-user */
684 eval_string("(in-package :cl-user)");
687 * Load the lisp source that is provided with the runtime,
688 * this is responsible for defining the public :vim interface.
690 safe_eval_string("(cl:load (cl:format nil \"~a/if_ecl\" (vim::expr-int \"$VIMRUNTIME\")))", TRUE);
692 /* get needed symbols */
693 range = intern_vim("RANGE");
696 /* Store the range. */
697 cl_set(range, cl_cons(MAKE_FIXNUM(eap->line1),
698 MAKE_FIXNUM(eap->line2)));
700 /* Run the string, this isn't a REPL loop so the result is discarded. */
701 if (cmd[0] == '\0')
702 safe_eval_string("(vim:eval-range)", TRUE);
703 else
704 safe_eval_string(cmd, TRUE);
708 * ":ecl" command
710 void
711 ex_ecl(exarg_T *eap)
713 char_u *script;
715 script = script_get(eap, eap->arg);
716 if (!eap->skip)
718 if (script == NULL)
719 RunEclCommand(eap, (char *)eap->arg);
720 else
721 RunEclCommand(eap, (char *)script);
724 vim_free(script);
727 void
728 ecl_end()