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.
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.
33 #include "ecl/ecl-inl.h"
36 static const char *g_vim_package_definition
=
42 " ;; a cons of (start-line . end-line) set when :ecl is called \n"
47 " #:current-window \n"
56 " #:current-buffer \n"
57 " #:buffer-line-count \n"
60 " #:append-line-to-buffer \n"
61 " #:append-to-buffer \n"
62 " #:get-buffer-by-name \n"
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
=
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"
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"
86 " (defmethod gray:stream-write-char ((strm msg-stream) char) \n"
87 " (with-slots (buffer) strm \n"
89 " ((char= char #\\newline) \n"
91 " (setf buffer \"\")) \n"
93 " (setf buffer (concatenate 'string buffer (string char))))))) \n"
95 " (cl:setq *standard-output* (make-instance 'msg-stream)) \n"
96 " (cl:setq *error-output* *standard-output*) \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"
111 " :buffer buffer)))) \n"
113 " (defclass vim-buf-stream (gray::fundamental-character-output-stream) \n"
114 " ((buffer :accessor buffer :initform nil))) \n"
116 " (defmethod gray:stream-line-column ((strm msg-stream)) \n"
117 " (with-slots (buffer) strm \n"
118 " (length buffer))) \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"
127 " (defvar *vim-buf-stream* (make-instance 'vim-buf-stream)) \n"
129 " (defun safe-eval (form from-ex) \n"
130 " \"evaluates a form, reporting any errors\" \n"
134 " (when (stringp form) \n"
135 " (cl:setq form (read-from-string form))) \n"
137 " (fresh-line *standard-output*)) \n"
138 " (let ((*standard-output* *vim-buf-stream*) \n"
139 " (*error-output* *vim-buf-stream*)) \n"
142 " (format t \"ERROR: ~a~%\" cnd)))) \n"
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
171 if (curwin
->w_cursor
.lnum
>= hi
)
173 curwin
->w_cursor
.lnum
+= extra
;
178 curwin
->w_cursor
.lnum
= lo
;
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
);
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
;
209 buf
= alloc(length
+ 1);
210 memcpy(buf
, string
->base_string
.self
+ start_pos
, length
);
218 static cl_object
cl_vim_execute_int(cl_object cmd
)
220 char *buf
= zero_terminate( cmd
);
227 static cl_object
vim_type_to_cl_object(typval_T
*tv
)
229 cl_object cl_result
= Cnil
;
230 switch (tv
->v_type
) {
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
;
237 cl_result
= CONS( vim_type_to_cl_object( &item
->li_tv
),
239 item
= item
->li_prev
;
244 if (tv
->vval
.v_string
!= NULL
)
245 cl_result
= make_base_string_copy(tv
->vval
.v_string
);
249 long num
= (long)tv
->vval
.v_number
;
250 if (num
< MOST_NEGATIVE_FIXNUM
251 || num
> MOST_POSITIVE_FIXNUM
)
254 char num_buf
[NUMBUFLEN
];
255 sprintf((char *)num_buf
, "%ld", (long)num
);
256 cl_result
= c_string_to_object(num_buf
);
258 cl_result
= MAKE_FIXNUM(num
);
262 EMSG2(_(e_intern2
), "vim_type_to_cl_object(VAR_UNKNOWN)");
265 EMSG2(_(e_intern2
), "vim_type_to_cl_object(VAR_DICT)");
268 EMSG2(_(e_intern2
), "vim_type_to_cl_object(VAR_FUNC)");
271 EMSG2(_(e_intern2
), "vim_type_to_cl_object()");
277 static cl_object
cl_vim_expr_int(cl_object cmd
)
281 char *buf
= zero_terminate(cmd
);
282 cl_object cl_result
= Cnil
;
284 tv
= eval_expr(buf
, NULL
);
286 cl_result
= vim_type_to_cl_object(tv
);
295 static cl_object
cl_vim_kill_int (cl_object pid
, cl_object sig
)
298 int fixed_pid
= fix(pid
);
299 int fixed_sig
= fix(sig
);
300 if (kill (fixed_pid
, fixed_sig
) == 0)
311 static cl_object
cl_vim_windows_int()
313 cl_object result
= Cnil
;
314 win_T
*vwin
= firstwin
;
318 result
= CONS(ecl_make_foreign_data(g_window_symbol
,
325 return cl_nreverse(result
);
328 static cl_object
cl_vim_current_window_int()
330 return ecl_make_foreign_data(g_window_symbol
,
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
,
377 static cl_object
cl_vim_buffers_int()
379 cl_object result
= Cnil
;
380 buf_T
*vbuf
= firstbuf
;
384 result
= CONS(ecl_make_foreign_data(g_buffer_symbol
,
391 return cl_nreverse(result
);
394 static cl_object
cl_vim_current_buffer_int()
396 return ecl_make_foreign_data(g_buffer_symbol
,
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;
417 cl_object result
= Cnil
;
421 result
= CONS(make_base_string_copy(ml_get_buf(buf
, end
--, FALSE
)),
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
)
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
;
441 curbuf
= ((buf_T
*)ecl_foreign_data_pointer_safe(buf
));
442 start_line
= curbuf
->b_ml
.ml_line_count
;
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 */
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
;
464 if (start_line
== 0) start_line
= 1;
466 while (lines
!= Cnil
) {
467 char_u
*line
= string_to_line (cl_car (lines
));
470 ml_append_string (start_line
, line
, -1);
474 ml_append (start_line
, line
, 0, FALSE
);
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 */
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
;
494 if (ecl_char
!= Cnil
)
496 string
[0] = CHAR_CODE (ecl_char
);
497 if (string
[0] == '\n')
499 ml_append (start_line
, "", 0, FALSE
);
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 */
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
;
525 buf_T
*savebuf
= curbuf
;
527 curbuf
= ((buf_T
*)ecl_foreign_data_pointer_safe(buf
));
531 /* take off the head of the list */
532 lines
= cl_nthcdr(start2_
, lines
);
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
542 if (start1
+ max_len
> curbuf
->b_ml
.ml_line_count
+ 1)
543 u_save(start1
- 1, curbuf
->b_ml
.ml_line_count
+ 1);
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) */
563 char_u
*line
= string_to_line(cl_car(lines
));
564 ml_append(start1
+ i
- 1, line
, 0, FALSE
);
566 lines
= cl_cdr(lines
);
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
));
576 if (curbuf
== savebuf
)
577 fix_cursor(start1
, end1
, (new_len
- old_len
));
579 /* restore and return */
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;
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,
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
);
617 RunEclCommand(exarg_T
*eap
, const char *cmd
)
619 static int have_inited
= 0;
620 static cl_object range
;
624 static char *argv
[] = {"ecl", 0};
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. */
702 safe_eval_string("(vim:eval-range)", TRUE
);
704 safe_eval_string(cmd
, TRUE
);
715 script
= script_get(eap
, eap
->arg
);
719 RunEclCommand(eap
, (char *)eap
->arg
);
721 RunEclCommand(eap
, (char *)script
);