blob: 4838e3197691be54e5ebda364883976e9c2f25e0 [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
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/*
11 * Tcl extensions by Ingo Wilken <Ingo.Wilken@informatik.uni-oldenburg.de>
12 * Last modification: Wed May 10 21:28:44 CEST 2000
13 * Requires Tcl 8.0 or higher.
14 *
15 * Variables:
16 * ::vim::current(buffer) # Name of buffer command for current buffer.
17 * ::vim::current(window) # Name of window command for current window.
18 * ::vim::range(start) # Start of current range (line number).
19 * ::vim::range(end) # End of current range (line number).
20 * ::vim::lbase # Start of line/column numbers (1 or 0).
21 *
22 * Commands:
23 * ::vim::command {cmd} # Execute ex command {cmd}.
24 * ::vim::option {opt} [val] # Get/Set option {opt}.
25 * ::vim::expr {expr} # Evaluate {expr} using vim's evaluator.
26 * ::vim::beep # Guess.
27 *
28 * set buf [::vim::buffer {n}] # Create Tcl command for buffer N.
29 * set bl [::vim::buffer list] # Get list of Tcl commands of all buffers.
30 * ::vim::buffer exists {n} # True if buffer {n} exists.
31 *
32 * set wl [::vim::window list] # Get list of Tcl commands of all windows.
33 *
34 * set n [$win height] # Report window height.
35 * $win height {n} # Set window height to {n}.
36 * array set pos [$win cursor] # Get cursor position.
37 * $win cursor {row} {col} # Set cursor position.
38 * $win cursor pos # Set cursor position from array var "pos"
39 * $win delcmd {cmd} # Register callback command for closed window.
40 * $win option {opt} [val] # Get/Set vim option in context of $win.
41 * $win command {cmd} # Execute ex command in context of $win.
42 * $win expr {expr} # Evaluate vim expression in context of $win.
43 * set buf [$win buffer] # Create Tcl command for window's buffer.
44 *
45 * $buf name # Reports file name in buffer.
46 * $buf number # Reports buffer number.
47 * set l [$buf get {n}] # Get buffer line {n} as a string.
48 * set L [$buf get {n} {m}] # Get lines {n} through {m} as a list.
49 * $buf count # Reports number of lines in buffer.
50 * $buf last # Reports number of last line in buffer.
51 * $buf delete {n} # Delete line {n}.
52 * $buf delete {n} {m} # Delete lines {n} through {m}.
53 * $buf set {n} {l} # Set line {n} to string {l}.
54 * $buf set {n} {m} {L} # Set lines {n} through {m} from list {L}.
55 * # Delete/inserts lines as appropriate.
56 * $buf option {opt} [val] # Get/Set vim option in context of $buf.
57 * $buf command {cmd} # Execute ex command in context of $buf
58 * $buf expr {cmd} # Evaluate vim expression in context of $buf.
59 * array set pos [$buf mark {m}] # Get position of mark.
60 * $buf append {n} {str} # Append string {str} to buffer,after line {n}.
61 * $buf insert {n} {str} # Insert string {str} in buffer as line {n}.
62 * $buf delcmd {cmd} # Register callback command for deleted buffer.
63 * set wl [$buf windows] # Get list of Tcl commands for all windows of
64 * # this buffer.
65TODO:
66 * ::vim::buffer new # create new buffer + Tcl command
67 */
68
69#include "vim.h"
Bram Moolenaar2ab2e862019-12-04 21:24:53 +010070#undef EXTERN // tcl.h defines it too
Bram Moolenaar071d4272004-06-13 20:20:40 +000071
72#ifdef DYNAMIC_TCL
Bram Moolenaar2ab2e862019-12-04 21:24:53 +010073# define USE_TCL_STUBS // use tcl's stubs mechanism
Bram Moolenaar071d4272004-06-13 20:20:40 +000074#endif
75
76#include <tcl.h>
Bram Moolenaar071d4272004-06-13 20:20:40 +000077#include <string.h>
78
79typedef struct
80{
81 Tcl_Interp *interp;
Bram Moolenaar84a4c332012-02-22 16:01:56 +010082 int exitvalue;
Bram Moolenaar071d4272004-06-13 20:20:40 +000083 int range_start, range_end;
84 int lbase;
85 char *curbuf, *curwin;
86} tcl_info;
87
Bram Moolenaar84a4c332012-02-22 16:01:56 +010088static tcl_info tclinfo = { NULL, 0, 0, 0, 0, NULL, NULL };
Bram Moolenaar071d4272004-06-13 20:20:40 +000089
90#define VAR_RANGE1 "::vim::range(start)"
91#define VAR_RANGE2 "::vim::range(begin)"
92#define VAR_RANGE3 "::vim::range(end)"
93#define VAR_CURBUF "::vim::current(buffer)"
94#define VAR_CURWIN "::vim::current(window)"
95#define VAR_LBASE "::vim::lbase"
96#define VAR_CURLINE "line"
97#define VAR_CURLNUM "lnum"
98#define VARNAME_SIZE 64
99
100#define row2tcl(x) ((x) - (tclinfo.lbase==0))
101#define row2vim(x) ((x) + (tclinfo.lbase==0))
102#define col2tcl(x) ((x) + (tclinfo.lbase!=0))
103#define col2vim(x) ((x) - (tclinfo.lbase!=0))
104
105
106#define VIMOUT ((ClientData)1)
107#define VIMERR ((ClientData)2)
108
Bram Moolenaar2ab2e862019-12-04 21:24:53 +0100109// This appears to be new in Tcl 8.4.
Bram Moolenaar7df2d662005-01-25 22:18:08 +0000110#ifndef CONST84
111# define CONST84
112#endif
113
Bram Moolenaar071d4272004-06-13 20:20:40 +0000114/*
115 * List of Tcl interpreters who reference a vim window or buffer.
Bram Moolenaarc4568ab2018-11-16 16:21:05 +0100116 * Each buffer and window has its own list in the w_tcl_ref or b_tcl_ref
Bram Moolenaare344bea2005-09-01 20:46:49 +0000117 * struct member. We need this because Tcl can create sub-interpreters with
118 * the "interp" command, and each interpreter can reference all windows and
119 * buffers.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000120 */
121struct ref
122{
123 struct ref *next;
124
125 Tcl_Interp *interp;
Bram Moolenaar2ab2e862019-12-04 21:24:53 +0100126 Tcl_Command cmd; // Tcl command that represents this object
127 Tcl_Obj *delcmd; // Tcl command to call when object is being del.
128 void *vimobj; // Vim window or buffer (win_T* or buf_T*)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000129};
130static char * tclgetbuffer _ANSI_ARGS_((Tcl_Interp *interp, buf_T *buf));
131static char * tclgetwindow _ANSI_ARGS_((Tcl_Interp *interp, win_T *win));
132static int tclsetdelcmd _ANSI_ARGS_((Tcl_Interp *interp, struct ref *reflist, void *vimobj, Tcl_Obj *delcmd));
133static int tclgetlinenum _ANSI_ARGS_ ((Tcl_Interp *interp, Tcl_Obj *obj, int *valueP, buf_T *buf));
134static win_T *tclfindwin _ANSI_ARGS_ ((buf_T *buf));
135static int tcldoexcommand _ANSI_ARGS_ ((Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], int objn));
136static int tclsetoption _ANSI_ARGS_ ((Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], int objn));
137static int tclvimexpr _ANSI_ARGS_ ((Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], int objn));
138static void tcldelthisinterp _ANSI_ARGS_ ((void));
139
140static int vimerror _ANSI_ARGS_((Tcl_Interp *interp));
141static void tclmsg _ANSI_ARGS_((char *text));
142static void tclerrmsg _ANSI_ARGS_((char *text));
143static void tclupdatevars _ANSI_ARGS_((void));
144
Bram Moolenaar2ab2e862019-12-04 21:24:53 +0100145static struct ref refsdeleted; // dummy object for deleted ref list
Bram Moolenaar071d4272004-06-13 20:20:40 +0000146
Bram Moolenaar2ab2e862019-12-04 21:24:53 +0100147//////////////////////////////////////////////////////////////////////////////
148// TCL interface manager
149////////////////////////////////////////////////////////////////////////////
Bram Moolenaar071d4272004-06-13 20:20:40 +0000150
151#if defined(DYNAMIC_TCL) || defined(PROTO)
152# ifndef DYNAMIC_TCL_DLL
153# define DYNAMIC_TCL_DLL "tcl83.dll"
154# endif
155# ifndef DYNAMIC_TCL_VER
156# define DYNAMIC_TCL_VER "8.3"
157# endif
158
Bram Moolenaar2ab2e862019-12-04 21:24:53 +0100159# ifndef DYNAMIC_TCL // Just generating prototypes
Bram Moolenaar071d4272004-06-13 20:20:40 +0000160typedef int HANDLE;
161# endif
162
Bram Moolenaar4f974752019-02-17 17:44:42 +0100163# ifndef MSWIN
Bram Moolenaar8a5115c2016-01-09 19:41:11 +0100164# include <dlfcn.h>
165# define HANDLE void*
166# define TCL_PROC void*
167# define load_dll(n) dlopen((n), RTLD_LAZY|RTLD_GLOBAL)
168# define symbol_from_dll dlsym
169# define close_dll dlclose
Martin Tournoij1a3e5742021-07-24 13:57:29 +0200170# define load_dll_error dlerror
Bram Moolenaar8a5115c2016-01-09 19:41:11 +0100171# else
172# define TCL_PROC FARPROC
173# define load_dll vimLoadLib
174# define symbol_from_dll GetProcAddress
175# define close_dll FreeLibrary
Martin Tournoij1a3e5742021-07-24 13:57:29 +0200176# define load_dll_error GetWin32Error
Bram Moolenaar8a5115c2016-01-09 19:41:11 +0100177# endif
178
Bram Moolenaar071d4272004-06-13 20:20:40 +0000179/*
Bram Moolenaara41b1392009-05-23 12:28:15 +0000180 * Declare HANDLE for tcl.dll and function pointers.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000181 */
182static HANDLE hTclLib = NULL;
183Tcl_Interp* (*dll_Tcl_CreateInterp)();
Bram Moolenaar0b4db6b2013-10-02 14:25:44 +0200184void (*dll_Tcl_FindExecutable)(const void *);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000185
186/*
187 * Table of name to function pointer of tcl.
188 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000189static struct {
190 char* name;
191 TCL_PROC* ptr;
192} tcl_funcname_table[] = {
193 {"Tcl_CreateInterp", (TCL_PROC*)&dll_Tcl_CreateInterp},
Bram Moolenaar0b4db6b2013-10-02 14:25:44 +0200194 {"Tcl_FindExecutable", (TCL_PROC*)&dll_Tcl_FindExecutable},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000195 {NULL, NULL},
196};
197
198/*
199 * Make all runtime-links of tcl.
200 *
201 * 1. Get module handle using LoadLibraryEx.
Bram Moolenaara41b1392009-05-23 12:28:15 +0000202 * 2. Get pointer to tcl function by GetProcAddress.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000203 * 3. Repeat 2, until get all functions will be used.
204 *
205 * Parameter 'libname' provides name of DLL.
206 * Return OK or FAIL.
207 */
208 static int
209tcl_runtime_link_init(char *libname, int verbose)
210{
211 int i;
212
213 if (hTclLib)
214 return OK;
Bram Moolenaar8a5115c2016-01-09 19:41:11 +0100215 if (!(hTclLib = load_dll(libname)))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000216 {
217 if (verbose)
Martin Tournoij1a3e5742021-07-24 13:57:29 +0200218 semsg(_(e_loadlib), libname, load_dll_error());
Bram Moolenaar071d4272004-06-13 20:20:40 +0000219 return FAIL;
220 }
221 for (i = 0; tcl_funcname_table[i].ptr; ++i)
222 {
Bram Moolenaar8a5115c2016-01-09 19:41:11 +0100223 if (!(*tcl_funcname_table[i].ptr = symbol_from_dll(hTclLib,
Bram Moolenaar071d4272004-06-13 20:20:40 +0000224 tcl_funcname_table[i].name)))
225 {
Bram Moolenaar8a5115c2016-01-09 19:41:11 +0100226 close_dll(hTclLib);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000227 hTclLib = NULL;
228 if (verbose)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100229 semsg(_(e_loadfunc), tcl_funcname_table[i].name);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000230 return FAIL;
231 }
232 }
233 return OK;
234}
Bram Moolenaar2ab2e862019-12-04 21:24:53 +0100235#endif // defined(DYNAMIC_TCL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000236
237#ifdef DYNAMIC_TCL
238static char *find_executable_arg = NULL;
239#endif
240
241 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +0100242vim_tcl_init(char *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000243{
244#ifndef DYNAMIC_TCL
245 Tcl_FindExecutable(arg);
246#else
247 find_executable_arg = arg;
248#endif
249}
250
Bram Moolenaarc7269f82021-12-05 11:36:23 +0000251#if defined(EXITFREE) || defined(PROTO)
252 void
253vim_tcl_finalize(void)
254{
255 Tcl_Finalize();
256}
257#endif
258
Bram Moolenaar071d4272004-06-13 20:20:40 +0000259#if defined(DYNAMIC_TCL) || defined(PROTO)
260
261static int stubs_initialized = FALSE;
262
263/*
264 * Return TRUE if the TCL interface can be used.
265 */
266 int
Bram Moolenaar68c2f632016-01-30 17:24:07 +0100267tcl_enabled(int verbose)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000268{
269 if (!stubs_initialized && find_executable_arg != NULL
Bram Moolenaar8a5115c2016-01-09 19:41:11 +0100270 && tcl_runtime_link_init((char *)p_tcldll, verbose) == OK)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000271 {
272 Tcl_Interp *interp;
273
Bram Moolenaar0b4db6b2013-10-02 14:25:44 +0200274 dll_Tcl_FindExecutable(find_executable_arg);
275
Bram Moolenaar8a5115c2016-01-09 19:41:11 +0100276 if ((interp = dll_Tcl_CreateInterp()) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000277 {
278 if (Tcl_InitStubs(interp, DYNAMIC_TCL_VER, 0))
279 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000280 Tcl_DeleteInterp(interp);
281 stubs_initialized = TRUE;
282 }
Bram Moolenaar2ab2e862019-12-04 21:24:53 +0100283 // FIXME: When Tcl_InitStubs() was failed, how delete interp?
Bram Moolenaar071d4272004-06-13 20:20:40 +0000284 }
285 }
286 return stubs_initialized;
287}
288#endif
289
290 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +0100291tcl_end(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000292{
Bram Moolenaar071d4272004-06-13 20:20:40 +0000293}
294
Bram Moolenaar2ab2e862019-12-04 21:24:53 +0100295/////////////////////////////////////////////////////////////////////////////
296// Tcl commands
297////////////////////////////////////////////////////////////////////////////
Bram Moolenaar071d4272004-06-13 20:20:40 +0000298
299/*
Bram Moolenaar84a4c332012-02-22 16:01:56 +0100300 * Replace standard "exit" command.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000301 *
Bram Moolenaar84a4c332012-02-22 16:01:56 +0100302 * Delete the Tcl interpreter; a new one will be created with the next
303 * :tcl command). The exit code is saved (and retrieved in tclexit()).
304 * Since Tcl's exit is never expected to return and this replacement
305 * does, then (except for a trivial case) additional Tcl commands will
306 * be run. Since the interpreter is now marked as deleted, an error
307 * will be returned -- typically "attempt to call eval in deleted
308 * interpreter". Hopefully, at this point, checks for TCL_ERROR take
309 * place and control percolates back up to Vim -- but with this new error
310 * string in the interpreter's result value. Therefore it would be
311 * useless for this routine to return the exit code via Tcl_SetResult().
Bram Moolenaar071d4272004-06-13 20:20:40 +0000312 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000313 static int
Bram Moolenaar68c2f632016-01-30 17:24:07 +0100314exitcmd(
315 ClientData dummy UNUSED,
316 Tcl_Interp *interp,
317 int objc,
318 Tcl_Obj *CONST objv[])
Bram Moolenaar071d4272004-06-13 20:20:40 +0000319{
320 int value = 0;
321
322 switch (objc)
323 {
324 case 2:
325 if (Tcl_GetIntFromObj(interp, objv[1], &value) != TCL_OK)
326 break;
Bram Moolenaar2ab2e862019-12-04 21:24:53 +0100327 // FALLTHROUGH
Bram Moolenaar071d4272004-06-13 20:20:40 +0000328 case 1:
Bram Moolenaar84a4c332012-02-22 16:01:56 +0100329 tclinfo.exitvalue = value;
330
331 Tcl_DeleteInterp(interp);
332 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000333 default:
334 Tcl_WrongNumArgs(interp, 1, objv, "?returnCode?");
335 }
336 return TCL_ERROR;
337}
338
Bram Moolenaar071d4272004-06-13 20:20:40 +0000339/*
340 * "::vim::beep" - what Vi[m] does best :-)
341 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000342 static int
Bram Moolenaar68c2f632016-01-30 17:24:07 +0100343beepcmd(
344 ClientData dummy UNUSED,
345 Tcl_Interp *interp,
346 int objc,
347 Tcl_Obj *CONST objv[])
Bram Moolenaar071d4272004-06-13 20:20:40 +0000348{
349 if (objc != 1)
350 {
351 Tcl_WrongNumArgs(interp, 1, objv, NULL);
352 return TCL_ERROR;
353 }
Bram Moolenaar165bc692015-07-21 17:53:25 +0200354 vim_beep(BO_LANG);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000355 return TCL_OK;
356}
357
358/*
359 * "::vim::buffer list" - create a list of buffer commands.
Bram Moolenaarb8017e72007-05-10 18:59:07 +0000360 * "::vim::buffer {N}" - create buffer command for buffer N.
Bram Moolenaar84a4c332012-02-22 16:01:56 +0100361 * "::vim::buffer exists {N}" - test if buffer N exists.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000362 * "::vim::buffer new" - create a new buffer (not implemented)
363 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000364 static int
Bram Moolenaar68c2f632016-01-30 17:24:07 +0100365buffercmd(
366 ClientData dummy UNUSED,
367 Tcl_Interp *interp,
368 int objc,
369 Tcl_Obj *CONST objv[])
Bram Moolenaar071d4272004-06-13 20:20:40 +0000370{
371 char *name;
372 buf_T *buf;
373 Tcl_Obj *resobj;
374 int err, n, idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000375 enum {BCMD_EXISTS, BCMD_LIST};
Bram Moolenaar7df2d662005-01-25 22:18:08 +0000376 static CONST84 char *bcmdoptions[] =
Bram Moolenaar071d4272004-06-13 20:20:40 +0000377 {
378 "exists", "list", (char *)0
379 };
380
381 if (objc < 2)
382 {
383 Tcl_WrongNumArgs(interp, 1, objv, "option");
384 return TCL_ERROR;
385 }
386 err = Tcl_GetIntFromObj(interp, objv[1], &n);
387 if (err == TCL_OK)
388 {
389 if (objc != 2)
390 {
391 Tcl_WrongNumArgs(interp, 1, objv, "bufNumber");
392 return TCL_ERROR;
393 }
Bram Moolenaar29323592016-07-24 22:04:11 +0200394 FOR_ALL_BUFFERS(buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000395 {
396 if (buf->b_fnum == n)
397 {
398 name = tclgetbuffer(interp, buf);
399 if (name == NULL)
400 return TCL_ERROR;
401 Tcl_SetResult(interp, name, TCL_VOLATILE);
402 return TCL_OK;
403 }
404 }
405 Tcl_SetResult(interp, _("invalid buffer number"), TCL_STATIC);
406 return TCL_ERROR;
407 }
Bram Moolenaar2ab2e862019-12-04 21:24:53 +0100408 Tcl_ResetResult(interp); // clear error from Tcl_GetIntFromObj
Bram Moolenaar071d4272004-06-13 20:20:40 +0000409
410 err = Tcl_GetIndexFromObj(interp, objv[1], bcmdoptions, "option", 0, &idx);
411 if (err != TCL_OK)
412 return err;
413 switch (idx)
414 {
415 case BCMD_LIST:
416 if (objc != 2)
417 {
418 Tcl_WrongNumArgs(interp, 2, objv, "");
419 err = TCL_ERROR;
420 break;
421 }
Bram Moolenaar29323592016-07-24 22:04:11 +0200422 FOR_ALL_BUFFERS(buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000423 {
424 name = tclgetbuffer(interp, buf);
425 if (name == NULL)
426 {
427 err = TCL_ERROR;
428 break;
429 }
430 Tcl_AppendElement(interp, name);
431 }
432 break;
433
434 case BCMD_EXISTS:
435 if (objc != 3)
436 {
437 Tcl_WrongNumArgs(interp, 2, objv, "bufNumber");
438 err = TCL_ERROR;
439 break;
440 }
441 err = Tcl_GetIntFromObj(interp, objv[2], &n);
442 if (err == TCL_OK)
443 {
444 buf = buflist_findnr(n);
445 resobj = Tcl_NewIntObj(buf != NULL);
446 Tcl_SetObjResult(interp, resobj);
447 }
448 break;
449
450 default:
451 Tcl_SetResult(interp, _("not implemented yet"), TCL_STATIC);
452 err = TCL_ERROR;
453 }
454 return err;
455}
456
457/*
458 * "::vim::window list" - create list of window commands.
459 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000460 static int
Bram Moolenaar68c2f632016-01-30 17:24:07 +0100461windowcmd(
462 ClientData dummy UNUSED,
463 Tcl_Interp *interp,
464 int objc,
465 Tcl_Obj *CONST objv[])
Bram Moolenaar071d4272004-06-13 20:20:40 +0000466{
467 char *what, *string;
468 win_T *win;
469
470 if (objc != 2)
471 {
472 Tcl_WrongNumArgs(interp, 1, objv, "option");
473 return TCL_ERROR;
474 }
475 what = Tcl_GetStringFromObj(objv[1], NULL);
476 if (strcmp(what, "list") == 0)
477 {
478 FOR_ALL_WINDOWS(win)
479 {
480 string = tclgetwindow(interp, win);
481 if (string == NULL)
482 return TCL_ERROR;
483 Tcl_AppendElement(interp, string);
484 }
485 return TCL_OK;
486 }
487 Tcl_SetResult(interp, _("unknown option"), TCL_STATIC);
488 return TCL_ERROR;
489}
490
491/*
492 * flags for bufselfcmd and winselfcmd to indicate outstanding actions.
493 */
494#define FL_UPDATE_SCREEN (1<<0)
495#define FL_UPDATE_CURBUF (1<<1)
496#define FL_ADJUST_CURSOR (1<<2)
497
498/*
499 * This function implements the buffer commands.
500 */
501 static int
Bram Moolenaar68c2f632016-01-30 17:24:07 +0100502bufselfcmd(
503 ClientData ref,
504 Tcl_Interp *interp,
505 int objc,
506 Tcl_Obj *CONST objv[])
Bram Moolenaar071d4272004-06-13 20:20:40 +0000507{
508 int opt, err, idx, flags;
509 int val1, val2, n, i;
510 buf_T *buf, *savebuf;
511 win_T *win, *savewin;
512 Tcl_Obj *resobj;
513 pos_T *pos;
514 char *line;
515
516 enum
517 {
518 BUF_APPEND, BUF_COMMAND, BUF_COUNT, BUF_DELCMD, BUF_DELETE, BUF_EXPR,
519 BUF_GET, BUF_INSERT, BUF_LAST, BUF_MARK, BUF_NAME, BUF_NUMBER,
520 BUF_OPTION, BUF_SET, BUF_WINDOWS
521 };
Bram Moolenaar7df2d662005-01-25 22:18:08 +0000522 static CONST84 char *bufoptions[] =
Bram Moolenaar071d4272004-06-13 20:20:40 +0000523 {
524 "append", "command", "count", "delcmd", "delete", "expr",
525 "get", "insert", "last", "mark", "name", "number",
526 "option", "set", "windows", (char *)0
527 };
528
529 if (objc < 2)
530 {
531 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
532 return TCL_ERROR;
533 }
534
535 err = Tcl_GetIndexFromObj(interp, objv[1], bufoptions, "option", 0, &idx);
536 if (err != TCL_OK)
537 return err;
538
539 buf = (buf_T *)((struct ref *)ref)->vimobj;
540 savebuf = curbuf; curbuf = buf;
541 savewin = curwin; curwin = tclfindwin(buf);
542 flags = 0;
543 opt = 0;
544
545 switch (idx)
546 {
547 case BUF_COMMAND:
548 err = tcldoexcommand(interp, objc, objv, 2);
549 flags |= FL_UPDATE_SCREEN;
550 break;
551
552 case BUF_OPTION:
553 err = tclsetoption(interp, objc, objv, 2);
554 flags |= FL_UPDATE_SCREEN;
555 break;
556
557 case BUF_EXPR:
558 err = tclvimexpr(interp, objc, objv, 2);
559 break;
560
561 case BUF_NAME:
562 /*
563 * Get filename of buffer.
564 */
565 if (objc != 2)
566 {
567 Tcl_WrongNumArgs(interp, 2, objv, NULL);
568 err = TCL_ERROR;
569 break;
570 }
571 if (buf->b_ffname)
572 Tcl_SetResult(interp, (char *)buf->b_ffname, TCL_VOLATILE);
573 else
574 Tcl_SetResult(interp, "", TCL_STATIC);
575 break;
576
577 case BUF_LAST:
578 /*
579 * Get line number of last line.
580 */
581 opt = 1;
Bram Moolenaar2ab2e862019-12-04 21:24:53 +0100582 // fallthrough
Bram Moolenaar071d4272004-06-13 20:20:40 +0000583 case BUF_COUNT:
584 /*
585 * Get number of lines in buffer.
586 */
587 if (objc != 2)
588 {
589 Tcl_WrongNumArgs(interp, 2, objv, NULL);
590 err = TCL_ERROR;
591 break;
592 }
593 val1 = (int)buf->b_ml.ml_line_count;
594 if (opt)
595 val1 = row2tcl(val1);
596
597 resobj = Tcl_NewIntObj(val1);
598 Tcl_SetObjResult(interp, resobj);
599 break;
600
601 case BUF_NUMBER:
602 /*
603 * Get buffer's number.
604 */
605 if (objc != 2)
606 {
607 Tcl_WrongNumArgs(interp, 2, objv, NULL);
608 err = TCL_ERROR;
609 break;
610 }
611 resobj = Tcl_NewIntObj((int)buf->b_fnum);
612 Tcl_SetObjResult(interp, resobj);
613 break;
614
615 case BUF_GET:
616 if (objc != 3 && objc != 4)
617 {
618 Tcl_WrongNumArgs(interp, 2, objv, "lineNumber ?lineNumber?");
619 err = TCL_ERROR;
620 break;
621 }
622 err = tclgetlinenum(interp, objv[2], &val1, buf);
623 if (err != TCL_OK)
624 break;
625 if (objc == 4)
626 {
627 err = tclgetlinenum(interp, objv[3], &val2, buf);
628 if (err != TCL_OK)
629 break;
630 if (val1 > val2)
631 {
632 n = val1; val1 = val2; val2 = n;
633 }
634 Tcl_ResetResult(interp);
635
636 for (n = val1; n <= val2 && err == TCL_OK; n++)
637 {
638 line = (char *)ml_get_buf(buf, (linenr_T)n, FALSE);
639 if (line)
640 Tcl_AppendElement(interp, line);
641 else
642 err = TCL_ERROR;
643 }
644 }
Bram Moolenaar2ab2e862019-12-04 21:24:53 +0100645 else { // objc == 3
Bram Moolenaar071d4272004-06-13 20:20:40 +0000646 line = (char *)ml_get_buf(buf, (linenr_T)val1, FALSE);
647 Tcl_SetResult(interp, line, TCL_VOLATILE);
648 }
649 break;
650
651 case BUF_SET:
652 if (objc != 4 && objc != 5)
653 {
654 Tcl_WrongNumArgs(interp, 3, objv, "lineNumber ?lineNumber? stringOrList");
655 err = TCL_ERROR;
656 break;
657 }
658 err = tclgetlinenum(interp, objv[2], &val1, buf);
659 if (err != TCL_OK)
660 return TCL_ERROR;
661 if (objc == 4)
662 {
663 /*
664 * Replace one line with a string.
665 * $buf set {n} {string}
666 */
667 line = Tcl_GetStringFromObj(objv[3], NULL);
668 if (u_savesub((linenr_T)val1) != OK)
669 {
670 Tcl_SetResult(interp, _("cannot save undo information"), TCL_STATIC);
671 err = TCL_ERROR;
672 }
673 else
674 if (ml_replace((linenr_T)val1, (char_u *)line, TRUE) != OK)
675 {
676 Tcl_SetResult(interp, _("cannot replace line"), TCL_STATIC);
677 err = TCL_ERROR;
678 }
679 else
680 {
681 changed_bytes((linenr_T)val1, 0);
682 flags |= FL_UPDATE_CURBUF;
683 }
684 break;
685 }
686 else
687 {
688 /*
689 * Replace several lines with the elements of a Tcl list.
690 * $buf set {n} {m} {list}
691 * If the list contains more than {m}-{n}+1 elements, they
692 * are * inserted after line {m}. If the list contains fewer
693 * elements, * the lines from {n}+length({list}) through {m}
694 * are deleted.
695 */
696 int lc;
697 Tcl_Obj **lv;
698
699 err = tclgetlinenum(interp, objv[3], &val2, buf);
700 if (err != TCL_OK)
701 break;
702 err = Tcl_ListObjGetElements(interp, objv[4], &lc, &lv);
703 if (err != TCL_OK)
704 break;
705 if (val1 > val2)
706 {
707 n = val1;
708 val1 = val2;
709 val2 = n;
710 }
711
712 n = val1;
713 if (u_save((linenr_T)(val1 - 1), (linenr_T)(val2 + 1)) != OK)
714 {
715 Tcl_SetResult(interp, _("cannot save undo information"),
716 TCL_STATIC);
717 err = TCL_ERROR;
718 break;
719 }
720 flags |= FL_UPDATE_CURBUF;
721
722 for (i = 0; i < lc && n <= val2; i++)
723 {
724 line = Tcl_GetStringFromObj(lv[i], NULL);
725 if (ml_replace((linenr_T)n, (char_u *)line, TRUE) != OK)
726 goto setListError;
727 ++n;
728 }
729 if (i < lc)
730 {
Bram Moolenaar2ab2e862019-12-04 21:24:53 +0100731 // append lines
Bram Moolenaar071d4272004-06-13 20:20:40 +0000732 do
733 {
734 line = Tcl_GetStringFromObj(lv[i], NULL);
735 if (ml_append((linenr_T)(n - 1),
736 (char_u *)line, 0, FALSE) != OK)
737 goto setListError;
738 ++n;
739 ++i;
740 } while (i < lc);
741 }
742 else if (n <= val2)
743 {
Bram Moolenaar2ab2e862019-12-04 21:24:53 +0100744 // did not replace all lines, delete
Bram Moolenaar071d4272004-06-13 20:20:40 +0000745 i = n;
746 do
747 {
Bram Moolenaarca70c072020-05-30 20:30:46 +0200748 if (ml_delete((linenr_T)i) != OK)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000749 goto setListError;
750 ++n;
751 } while (n <= val2);
752 }
Bram Moolenaar2ab2e862019-12-04 21:24:53 +0100753 lc -= val2 - val1 + 1; // number of lines to be replaced
Bram Moolenaar071d4272004-06-13 20:20:40 +0000754 mark_adjust((linenr_T)val1, (linenr_T)val2, (long)MAXLNUM,
755 (long)lc);
756 changed_lines((linenr_T)val1, 0, (linenr_T)val2 + 1, (long)lc);
757 break;
758 setListError:
Bram Moolenaar2ab2e862019-12-04 21:24:53 +0100759 u_undo(1); // ???
Bram Moolenaar071d4272004-06-13 20:20:40 +0000760 Tcl_SetResult(interp, _("cannot set line(s)"), TCL_STATIC);
761 err = TCL_ERROR;
762 }
763 break;
764
765 case BUF_DELETE:
766 if (objc != 3 && objc != 4)
767 {
768 Tcl_WrongNumArgs(interp, 3, objv, "lineNumber ?lineNumber?");
769 err = TCL_ERROR;
770 break;
771 }
772 err = tclgetlinenum(interp, objv[2], &val1, buf);
773 if (err != TCL_OK)
774 break;
775 val2 = val1;
776 if (objc == 4)
777 {
778 err = tclgetlinenum(interp, objv[3], &val2, buf);
779 if (err != TCL_OK)
780 return err;
781 if (val1 > val2)
782 {
783 i = val1; val1 = val2; val2 = i;
784 }
785 }
786 n = val2 - val1 + 1;
787 if (u_savedel((linenr_T)val1, (long)n) != OK)
788 {
789 Tcl_SetResult(interp, _("cannot save undo information"),
790 TCL_STATIC);
791 err = TCL_ERROR;
792 break;
793 }
794 for (i = 0; i < n; i++)
795 {
Bram Moolenaarca70c072020-05-30 20:30:46 +0200796 ml_delete((linenr_T)val1);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000797 err = vimerror(interp);
798 if (err != TCL_OK)
799 break;
800 }
801 if (i > 0)
802 deleted_lines_mark((linenr_T)val1, (long)i);
803 flags |= FL_ADJUST_CURSOR|FL_UPDATE_SCREEN;
804 break;
805
806 case BUF_MARK:
807 if (objc != 3)
808 {
809 Tcl_WrongNumArgs(interp, 2, objv, "markName");
810 err = TCL_ERROR;
811 break;
812 }
813 line = Tcl_GetStringFromObj(objv[2], NULL);
814
815 pos = NULL;
816 if (line[0] != '\0' && line[1] == '\0')
Bram Moolenaar071d4272004-06-13 20:20:40 +0000817 pos = getmark(line[0], FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000818 if (pos == NULL)
819 {
820 Tcl_SetResult(interp, _("invalid mark name"), TCL_STATIC);
821 err = TCL_ERROR;
822 break;
823 }
824 err = vimerror(interp);
825 if (err != TCL_OK)
826 break;
827 if (pos->lnum <= 0)
828 {
829 Tcl_SetResult(interp, _("mark not set"), TCL_STATIC);
830 err = TCL_ERROR;
831 }
832 else
833 {
834 char rbuf[64];
Bram Moolenaar555b2802005-05-19 21:08:39 +0000835
836 sprintf(rbuf, _("row %d column %d"),
837 (int)row2tcl(pos->lnum), (int)col2tcl(pos->col));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000838 Tcl_SetResult(interp, rbuf, TCL_VOLATILE);
839 }
840 break;
841
842 case BUF_INSERT:
843 opt = 1;
Bram Moolenaar2ab2e862019-12-04 21:24:53 +0100844 // fallthrough
Bram Moolenaar071d4272004-06-13 20:20:40 +0000845 case BUF_APPEND:
846 if (objc != 4)
847 {
848 Tcl_WrongNumArgs(interp, 2, objv, "lineNum text");
849 err = TCL_ERROR;
850 break;
851 }
852 err = tclgetlinenum(interp, objv[2], &val1, buf);
853 if (err != TCL_OK)
854 break;
855 if (opt)
856 --val1;
857 if (u_save((linenr_T)val1, (linenr_T)(val1+1)) != OK)
858 {
Bram Moolenaar555b2802005-05-19 21:08:39 +0000859 Tcl_SetResult(interp, _("cannot save undo information"),
860 TCL_STATIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000861 err = TCL_ERROR;
862 break;
863 }
864
865 line = Tcl_GetStringFromObj(objv[3], NULL);
866 if (ml_append((linenr_T)val1, (char_u *)line, 0, FALSE) != OK)
867 {
Bram Moolenaar555b2802005-05-19 21:08:39 +0000868 Tcl_SetResult(interp, _("cannot insert/append line"),
869 TCL_STATIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000870 err = TCL_ERROR;
871 break;
872 }
873 appended_lines_mark((linenr_T)val1, 1L);
874 flags |= FL_UPDATE_SCREEN;
875 break;
876
877 case BUF_WINDOWS:
878 /*
879 * Return list of window commands.
880 */
881 if (objc != 2)
882 {
883 Tcl_WrongNumArgs(interp, 2, objv, NULL);
884 err = TCL_ERROR;
885 break;
886 }
887 Tcl_ResetResult(interp);
888 FOR_ALL_WINDOWS(win)
889 {
890 if (win->w_buffer == buf)
891 {
892 line = tclgetwindow(interp, win);
893 if (line != NULL)
894 Tcl_AppendElement(interp, line);
895 else
896 {
897 err = TCL_ERROR;
898 break;
899 }
900 }
901 }
902 break;
903
904 case BUF_DELCMD:
905 /*
906 * Register deletion callback.
907 * TODO: Should be able to register multiple callbacks
908 */
909 if (objc != 3)
910 {
911 Tcl_WrongNumArgs(interp, 2, objv, "command");
912 err = TCL_ERROR;
913 break;
914 }
Bram Moolenaare344bea2005-09-01 20:46:49 +0000915 err = tclsetdelcmd(interp, buf->b_tcl_ref, (void *)buf, objv[2]);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000916 break;
917
918 default:
919 Tcl_SetResult(interp, _("not implemented yet"), TCL_STATIC);
920 err = TCL_ERROR;
921 }
922
923 if (flags & FL_UPDATE_CURBUF)
924 redraw_curbuf_later(NOT_VALID);
925 curbuf = savebuf;
926 curwin = savewin;
927 if (flags & FL_ADJUST_CURSOR)
928 check_cursor();
929 if (flags & (FL_UPDATE_SCREEN | FL_UPDATE_CURBUF))
930 update_screen(NOT_VALID);
931
932 return err;
933}
934
935/*
936 * This function implements the window commands.
937 */
938 static int
Bram Moolenaar68c2f632016-01-30 17:24:07 +0100939winselfcmd(
940 ClientData ref,
941 Tcl_Interp *interp,
942 int objc,
943 Tcl_Obj *CONST objv[])
Bram Moolenaar071d4272004-06-13 20:20:40 +0000944{
945 int err, idx, flags;
946 int val1, val2;
947 Tcl_Obj *resobj;
948 win_T *savewin, *win;
949 buf_T *savebuf;
950 char *str;
951
952 enum
953 {
954 WIN_BUFFER, WIN_COMMAND, WIN_CURSOR, WIN_DELCMD, WIN_EXPR,
955 WIN_HEIGHT, WIN_OPTION
956 };
Bram Moolenaar7df2d662005-01-25 22:18:08 +0000957 static CONST84 char *winoptions[] =
Bram Moolenaar071d4272004-06-13 20:20:40 +0000958 {
959 "buffer", "command", "cursor", "delcmd", "expr",
960 "height", "option", (char *)0
961 };
962
963 if (objc < 2)
964 {
965 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
966 return TCL_ERROR;
967 }
968
969 err = Tcl_GetIndexFromObj(interp, objv[1], winoptions, "option", 0, &idx);
970 if (err != TCL_OK)
971 return TCL_ERROR;
972
973 win = (win_T *)((struct ref *)ref)->vimobj;
974 savewin = curwin; curwin = win;
975 savebuf = curbuf; curbuf = win->w_buffer;
976 flags = 0;
977
978 switch (idx)
979 {
980 case WIN_OPTION:
981 err = tclsetoption(interp, objc, objv, 2);
982 flags |= FL_UPDATE_SCREEN;
983 break;
984
985 case WIN_COMMAND:
986 err = tcldoexcommand(interp, objc, objv, 2);
987 flags |= FL_UPDATE_SCREEN;
988 break;
989
990 case WIN_EXPR:
991 err = tclvimexpr(interp, objc, objv, 2);
992 break;
993
994 case WIN_HEIGHT:
995 if (objc == 3)
996 {
997 err = Tcl_GetIntFromObj(interp, objv[2], &val1);
998 if (err != TCL_OK)
999 break;
1000#ifdef FEAT_GUI
1001 need_mouse_correct = TRUE;
1002#endif
1003 win_setheight(val1);
1004 err = vimerror(interp);
1005 if (err != TCL_OK)
1006 break;
1007 }
1008 else
1009 if (objc != 2)
1010 {
1011 Tcl_WrongNumArgs(interp, 2, objv, "?value?");
1012 err = TCL_ERROR;
1013 break;
1014 }
1015
1016 resobj = Tcl_NewIntObj((int)(win->w_height));
1017 Tcl_SetObjResult(interp, resobj);
1018 break;
1019
1020 case WIN_BUFFER:
1021 if (objc != 2)
1022 {
1023 Tcl_WrongNumArgs(interp, 2, objv, NULL);
1024 err = TCL_ERROR;
1025 break;
1026 }
1027 str = tclgetbuffer(interp, win->w_buffer);
1028 if (str)
1029 Tcl_SetResult(interp, str, TCL_VOLATILE);
1030 else
1031 err = TCL_ERROR;
1032 break;
1033
1034 case WIN_DELCMD:
1035 if (objc != 3)
1036 {
1037 Tcl_WrongNumArgs(interp, 2, objv, "command");
1038 err = TCL_ERROR;
1039 break;
1040 }
Bram Moolenaare344bea2005-09-01 20:46:49 +00001041 err = tclsetdelcmd(interp, win->w_tcl_ref, (void *)win, objv[2]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001042 break;
1043
1044 case WIN_CURSOR:
1045 if (objc > 4)
1046 {
1047 Tcl_WrongNumArgs(interp, 2, objv, "?arg1 ?arg2??");
1048 err = TCL_ERROR;
1049 break;
1050 }
1051 if (objc == 2)
1052 {
1053 char buf[64];
Bram Moolenaar555b2802005-05-19 21:08:39 +00001054
Bram Moolenaar071d4272004-06-13 20:20:40 +00001055 sprintf(buf, _("row %d column %d"), (int)row2tcl(win->w_cursor.lnum), (int)col2tcl(win->w_cursor.col));
1056 Tcl_SetResult(interp, buf, TCL_VOLATILE);
1057 break;
1058 }
1059 else if (objc == 3)
1060 {
1061 Tcl_Obj *part, *var;
1062
1063 part = Tcl_NewStringObj("row", -1);
1064 var = Tcl_ObjGetVar2(interp, objv[2], part, TCL_LEAVE_ERR_MSG);
1065 if (var == NULL)
1066 {
1067 err = TCL_ERROR;
1068 break;
1069 }
1070 err = tclgetlinenum(interp, var, &val1, win->w_buffer);
1071 if (err != TCL_OK)
1072 break;
1073 part = Tcl_NewStringObj("column", -1);
1074 var = Tcl_ObjGetVar2(interp, objv[2], part, TCL_LEAVE_ERR_MSG);
1075 if (var == NULL)
1076 {
1077 err = TCL_ERROR;
1078 break;
1079 }
1080 err = Tcl_GetIntFromObj(interp, var, &val2);
1081 if (err != TCL_OK)
1082 break;
1083 }
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01001084 else { // objc == 4
Bram Moolenaar071d4272004-06-13 20:20:40 +00001085 err = tclgetlinenum(interp, objv[2], &val1, win->w_buffer);
1086 if (err != TCL_OK)
1087 break;
1088 err = Tcl_GetIntFromObj(interp, objv[3], &val2);
1089 if (err != TCL_OK)
1090 break;
1091 }
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01001092 // TODO: should check column
Bram Moolenaar071d4272004-06-13 20:20:40 +00001093 win->w_cursor.lnum = val1;
1094 win->w_cursor.col = col2vim(val2);
Bram Moolenaar53901442018-07-25 22:02:36 +02001095 win->w_set_curswant = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001096 flags |= FL_UPDATE_SCREEN;
1097 break;
1098
1099 default:
1100 Tcl_SetResult(interp, _("not implemented yet"), TCL_STATIC);
1101 break;
1102 }
1103
1104 curwin = savewin;
1105 curbuf = savebuf;
1106 if (flags & FL_UPDATE_SCREEN)
1107 update_screen(NOT_VALID);
1108
1109 return err;
1110}
1111
1112
Bram Moolenaar071d4272004-06-13 20:20:40 +00001113 static int
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001114commandcmd(
1115 ClientData dummy UNUSED,
1116 Tcl_Interp *interp,
1117 int objc,
1118 Tcl_Obj *CONST objv[])
Bram Moolenaar071d4272004-06-13 20:20:40 +00001119{
1120 int err;
1121
1122 err = tcldoexcommand(interp, objc, objv, 1);
1123 update_screen(VALID);
1124 return err;
1125}
1126
Bram Moolenaar071d4272004-06-13 20:20:40 +00001127 static int
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001128optioncmd(
1129 ClientData dummy UNUSED,
1130 Tcl_Interp *interp,
1131 int objc,
1132 Tcl_Obj *CONST objv[])
Bram Moolenaar071d4272004-06-13 20:20:40 +00001133{
1134 int err;
1135
1136 err = tclsetoption(interp, objc, objv, 1);
1137 update_screen(VALID);
1138 return err;
1139}
1140
Bram Moolenaar071d4272004-06-13 20:20:40 +00001141 static int
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001142exprcmd(
1143 ClientData dummy UNUSED,
1144 Tcl_Interp *interp,
1145 int objc,
1146 Tcl_Obj *CONST objv[])
Bram Moolenaar071d4272004-06-13 20:20:40 +00001147{
1148 return tclvimexpr(interp, objc, objv, 1);
1149}
1150
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01001151/////////////////////////////////////////////////////////////////////////////
1152// Support functions for Tcl commands
1153////////////////////////////////////////////////////////////////////////////
Bram Moolenaar071d4272004-06-13 20:20:40 +00001154
1155/*
1156 * Get a line number from 'obj' and convert it to vim's range.
1157 */
1158 static int
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001159tclgetlinenum(
1160 Tcl_Interp *interp,
1161 Tcl_Obj *obj,
1162 int *valueP,
1163 buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001164{
1165 int err, i;
1166
1167 enum { LN_BEGIN, LN_BOTTOM, LN_END, LN_FIRST, LN_LAST, LN_START, LN_TOP };
1168
Bram Moolenaar7df2d662005-01-25 22:18:08 +00001169 static CONST84 char *keyw[] =
Bram Moolenaar071d4272004-06-13 20:20:40 +00001170 {
1171 "begin", "bottom", "end", "first", "last", "start", "top", (char *)0
1172 };
1173
1174 err = Tcl_GetIndexFromObj(interp, obj, keyw, "", 0, &i);
1175 if (err == TCL_OK)
1176 {
1177 switch (i)
1178 {
1179 case LN_BEGIN:
1180 case LN_FIRST:
1181 case LN_START:
1182 case LN_TOP:
1183 *valueP = 1;
1184 break;
1185 case LN_BOTTOM:
1186 case LN_END:
1187 case LN_LAST:
1188 *valueP = buf->b_ml.ml_line_count;
1189 break;
1190 }
1191 return TCL_OK;
1192 }
1193 Tcl_ResetResult(interp);
1194
1195 err = Tcl_GetIntFromObj(interp, obj, &i);
1196 if (err != TCL_OK)
1197 return err;
1198 i = row2vim(i);
1199 if (i < 1 || i > buf->b_ml.ml_line_count)
1200 {
1201 Tcl_SetResult(interp, _("line number out of range"), TCL_STATIC);
1202 return TCL_ERROR;
1203 }
1204 *valueP = i;
1205 return TCL_OK;
1206}
1207
1208/*
1209 * Find the first window in the window list that displays the buffer.
1210 */
1211 static win_T *
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001212tclfindwin(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001213{
1214 win_T *win;
1215
1216 FOR_ALL_WINDOWS(win)
1217 {
1218 if (win->w_buffer == buf)
1219 return win;
1220 }
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01001221 return curwin; // keep current window context
Bram Moolenaar071d4272004-06-13 20:20:40 +00001222}
1223
1224/*
1225 * Do-it-all function for "::vim::command", "$buf command" and "$win command".
1226 */
1227 static int
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001228tcldoexcommand(
1229 Tcl_Interp *interp,
1230 int objc,
1231 Tcl_Obj *CONST objv[],
1232 int objn)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001233{
1234 tcl_info saveinfo;
1235 int err, flag, nobjs;
1236 char *arg;
1237
1238 nobjs = objc - objn;
1239 if (nobjs < 1 || nobjs > 2)
1240 {
1241 Tcl_WrongNumArgs(interp, objn, objv, "?-quiet? exCommand");
1242 return TCL_ERROR;
1243 }
1244
1245 flag = 0;
1246 if (nobjs == 2)
1247 {
1248 arg = Tcl_GetStringFromObj(objv[objn], NULL);
1249 if (strcmp(arg, "-quiet") == 0)
1250 flag = 1;
1251 else
1252 {
1253 Tcl_ResetResult(interp);
1254 Tcl_AppendResult(interp, _("unknown flag: "), arg, (char *)0);
1255 return TCL_ERROR;
1256 }
1257 ++objn;
1258 }
1259
1260 memcpy(&saveinfo, &tclinfo, sizeof(tcl_info));
1261 tclinfo.interp = NULL;
1262 tclinfo.curwin = NULL;
1263 tclinfo.curbuf = NULL;
1264
1265 arg = Tcl_GetStringFromObj(objv[objn], NULL);
1266 if (flag)
1267 ++emsg_off;
1268 do_cmdline_cmd((char_u *)arg);
1269 if (flag)
1270 --emsg_off;
1271 err = vimerror(interp);
1272
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01001273 // If the ex command created a new Tcl interpreter, remove it
Bram Moolenaar071d4272004-06-13 20:20:40 +00001274 if (tclinfo.interp)
1275 tcldelthisinterp();
1276 memcpy(&tclinfo, &saveinfo, sizeof(tcl_info));
1277 tclupdatevars();
1278
1279 return err;
1280}
1281
1282/*
1283 * Do-it-all function for "::vim::option", "$buf option" and "$win option".
1284 */
1285 static int
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001286tclsetoption(
1287 Tcl_Interp *interp,
1288 int objc,
1289 Tcl_Obj *CONST objv[],
1290 int objn)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001291{
1292 int err, nobjs, idx;
1293 char_u *option;
Bram Moolenaar1779ff42020-12-31 18:11:16 +01001294 getoption_T gov;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001295 long lval;
1296 char_u *sval;
1297 Tcl_Obj *resobj;
1298
1299 enum { OPT_OFF, OPT_ON, OPT_TOGGLE };
Bram Moolenaar7df2d662005-01-25 22:18:08 +00001300 static CONST84 char *optkw[] = { "off", "on", "toggle", (char *)0 };
Bram Moolenaar071d4272004-06-13 20:20:40 +00001301
1302 nobjs = objc - objn;
1303 if (nobjs != 1 && nobjs != 2)
1304 {
1305 Tcl_WrongNumArgs(interp, objn, objv, "vimOption ?value?");
1306 return TCL_ERROR;
1307 }
1308
1309 option = (char_u *)Tcl_GetStringFromObj(objv[objn], NULL);
1310 ++objn;
Bram Moolenaar1779ff42020-12-31 18:11:16 +01001311 gov = get_option_value(option, &lval, &sval, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001312 err = TCL_OK;
Bram Moolenaar1779ff42020-12-31 18:11:16 +01001313 switch (gov)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001314 {
Bram Moolenaar1779ff42020-12-31 18:11:16 +01001315 case gov_string:
Bram Moolenaar071d4272004-06-13 20:20:40 +00001316 Tcl_SetResult(interp, (char *)sval, TCL_VOLATILE);
1317 vim_free(sval);
1318 break;
Bram Moolenaar1779ff42020-12-31 18:11:16 +01001319 case gov_bool:
1320 case gov_number:
Bram Moolenaar071d4272004-06-13 20:20:40 +00001321 resobj = Tcl_NewLongObj(lval);
1322 Tcl_SetObjResult(interp, resobj);
1323 break;
1324 default:
1325 Tcl_SetResult(interp, _("unknown vimOption"), TCL_STATIC);
1326 return TCL_ERROR;
1327 }
1328 if (nobjs == 2)
1329 {
Bram Moolenaar1779ff42020-12-31 18:11:16 +01001330 if (gov != gov_string)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001331 {
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01001332 sval = NULL; // avoid compiler warning
Bram Moolenaar071d4272004-06-13 20:20:40 +00001333 err = Tcl_GetIndexFromObj(interp, objv[objn], optkw, "", 0, &idx);
1334 if (err != TCL_OK)
1335 {
1336 Tcl_ResetResult(interp);
1337 err = Tcl_GetLongFromObj(interp, objv[objn], &lval);
1338 }
1339 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001340 {
Bram Moolenaar1779ff42020-12-31 18:11:16 +01001341 switch (idx)
1342 {
1343 case OPT_ON:
1344 lval = 1;
1345 break;
1346 case OPT_OFF:
1347 lval = 0;
1348 break;
1349 case OPT_TOGGLE:
1350 lval = !lval;
1351 break;
1352 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001353 }
1354 }
1355 else
1356 sval = (char_u *)Tcl_GetStringFromObj(objv[objn], NULL);
1357 if (err == TCL_OK)
1358 {
1359 set_option_value(option, lval, sval, OPT_LOCAL);
1360 err = vimerror(interp);
1361 }
1362 }
1363 return err;
1364}
1365
1366/*
1367 * Do-it-all function for "::vim::expr", "$buf expr" and "$win expr".
1368 */
1369 static int
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001370tclvimexpr(
1371 Tcl_Interp *interp,
1372 int objc,
1373 Tcl_Obj *CONST objv[],
1374 int objn)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001375{
1376#ifdef FEAT_EVAL
1377 char *expr, *str;
1378#endif
1379 int err;
1380
1381 if (objc - objn != 1)
1382 {
1383 Tcl_WrongNumArgs(interp, objn, objv, "vimExpr");
1384 return TCL_ERROR;
1385 }
1386
1387#ifdef FEAT_EVAL
1388 expr = Tcl_GetStringFromObj(objv[objn], NULL);
Bram Moolenaarb171fb12020-06-24 20:34:03 +02001389 str = (char *)eval_to_string((char_u *)expr, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001390 if (str == NULL)
1391 Tcl_SetResult(interp, _("invalid expression"), TCL_STATIC);
1392 else
Bram Moolenaar92959fa2018-07-04 22:12:25 +02001393 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001394 Tcl_SetResult(interp, str, TCL_VOLATILE);
Bram Moolenaar92959fa2018-07-04 22:12:25 +02001395 vim_free(str);
1396 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001397 err = vimerror(interp);
1398#else
1399 Tcl_SetResult(interp, _("expressions disabled at compile time"), TCL_STATIC);
1400 err = TCL_ERROR;
1401#endif
1402
1403 return err;
1404}
1405
1406/*
1407 * Check for internal vim errors.
1408 */
1409 static int
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001410vimerror(Tcl_Interp *interp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001411{
1412 if (got_int)
1413 {
1414 Tcl_SetResult(interp, _("keyboard interrupt"), TCL_STATIC);
1415 return TCL_ERROR;
1416 }
1417 else if (did_emsg)
1418 {
Bram Moolenaar86181df2020-05-11 23:14:04 +02001419 Tcl_SetResult(interp, _("Vim error"), TCL_STATIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001420 return TCL_ERROR;
1421 }
1422 return TCL_OK;
1423}
1424
1425/*
1426 * Functions that handle the reference lists:
1427 * delref() - callback for Tcl's DeleteCommand
1428 * tclgetref() - find/create Tcl command for a win_T* or buf_T* object
1429 * tclgetwindow() - window frontend for tclgetref()
1430 * tclgetbuffer() - buffer frontend for tclgetref()
1431 * tclsetdelcmd() - add Tcl callback command to a vim object
1432 */
1433 static void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001434delref(ClientData cref)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001435{
1436 struct ref *ref = (struct ref *)cref;
1437
1438 if (ref->delcmd)
1439 {
1440 Tcl_DecrRefCount(ref->delcmd);
1441 ref->delcmd = NULL;
1442 }
1443 ref->interp = NULL;
1444}
1445
1446 static char *
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001447tclgetref(
1448 Tcl_Interp *interp,
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01001449 void **refstartP, // ptr to w_tcl_ref/b_tcl-ref member of
1450 // win_T/buf_T struct
1451 char *prefix, // "win" or "buf"
1452 void *vimobj, // win_T* or buf_T*
1453 Tcl_ObjCmdProc *proc) // winselfcmd or bufselfcmd
Bram Moolenaar071d4272004-06-13 20:20:40 +00001454{
1455 struct ref *ref, *unused = NULL;
1456 static char name[VARNAME_SIZE];
1457 Tcl_Command cmd;
1458
1459 ref = (struct ref *)(*refstartP);
1460 if (ref == &refsdeleted)
1461 {
1462 Tcl_SetResult(interp, _("cannot create buffer/window command: object is being deleted"), TCL_STATIC);
1463 return NULL;
1464 }
1465
1466 while (ref != NULL)
1467 {
1468 if (ref->interp == interp)
1469 break;
1470 if (ref->interp == NULL)
1471 unused = ref;
1472 ref = ref->next;
1473 }
1474
1475 if (ref)
Bram Moolenaar555b2802005-05-19 21:08:39 +00001476 vim_snprintf(name, sizeof(name), "::vim::%s",
1477 Tcl_GetCommandName(interp, ref->cmd));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001478 else
1479 {
1480 if (unused)
1481 ref = unused;
1482 else
1483 {
1484 ref = (struct ref *)Tcl_Alloc(sizeof(struct ref));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001485 ref->interp = NULL;
1486 ref->next = (struct ref *)(*refstartP);
1487 (*refstartP) = (void *)ref;
1488 }
1489
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01001490 // This might break on some exotic systems...
Bram Moolenaar555b2802005-05-19 21:08:39 +00001491 vim_snprintf(name, sizeof(name), "::vim::%s_%lx",
1492 prefix, (unsigned long)vimobj);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001493 cmd = Tcl_CreateObjCommand(interp, name, proc,
1494 (ClientData)ref, (Tcl_CmdDeleteProc *)delref);
1495 if (!cmd)
1496 return NULL;
1497
1498 ref->interp = interp;
1499 ref->cmd = cmd;
1500 ref->delcmd = NULL;
1501 ref->vimobj = vimobj;
1502 }
1503 return name;
1504}
1505
1506 static char *
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001507tclgetwindow(Tcl_Interp *interp, win_T *win)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001508{
Bram Moolenaare344bea2005-09-01 20:46:49 +00001509 return tclgetref(interp, &(win->w_tcl_ref), "win", (void *)win, winselfcmd);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001510}
1511
1512 static char *
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001513tclgetbuffer(Tcl_Interp *interp, buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001514{
Bram Moolenaare344bea2005-09-01 20:46:49 +00001515 return tclgetref(interp, &(buf->b_tcl_ref), "buf", (void *)buf, bufselfcmd);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001516}
1517
1518 static int
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001519tclsetdelcmd(
1520 Tcl_Interp *interp,
1521 struct ref *reflist,
1522 void *vimobj,
1523 Tcl_Obj *delcmd)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001524{
1525 if (reflist == &refsdeleted)
1526 {
1527 Tcl_SetResult(interp, _("cannot register callback command: buffer/window is already being deleted"), TCL_STATIC);
1528 return TCL_ERROR;
1529 }
1530
1531 while (reflist != NULL)
1532 {
1533 if (reflist->interp == interp && reflist->vimobj == vimobj)
1534 {
1535 if (reflist->delcmd)
1536 Tcl_DecrRefCount(reflist->delcmd);
1537 Tcl_IncrRefCount(delcmd);
1538 reflist->delcmd = delcmd;
1539 return TCL_OK;
1540 }
1541 reflist = reflist->next;
1542 }
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01001543 // This should never happen. Famous last word?
Dominique Pelleaffd0bc2021-06-15 19:09:43 +02001544 iemsg(_("E280: TCL FATAL ERROR: reflist corrupt!? Please report this to vim-dev@vim.org"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001545 Tcl_SetResult(interp, _("cannot register callback command: buffer/window reference not found"), TCL_STATIC);
1546 return TCL_ERROR;
1547}
1548
1549
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01001550////////////////////////////////////////////
1551// I/O Channel
1552////////////////////////////////////////////
Bram Moolenaar071d4272004-06-13 20:20:40 +00001553
Bram Moolenaar071d4272004-06-13 20:20:40 +00001554 static int
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001555tcl_channel_close(ClientData instance, Tcl_Interp *interp UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001556{
1557 int err = 0;
1558
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01001559 // currently does nothing
Bram Moolenaar071d4272004-06-13 20:20:40 +00001560
1561 if (instance != VIMOUT && instance != VIMERR)
1562 {
1563 Tcl_SetErrno(EBADF);
1564 err = EBADF;
1565 }
1566 return err;
1567}
1568
Bram Moolenaar071d4272004-06-13 20:20:40 +00001569 static int
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001570tcl_channel_input(
1571 ClientData instance UNUSED,
1572 char *buf UNUSED,
1573 int bufsiz UNUSED,
1574 int *errptr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001575{
1576
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01001577 // input is currently not supported
Bram Moolenaar071d4272004-06-13 20:20:40 +00001578
1579 Tcl_SetErrno(EINVAL);
1580 if (errptr)
1581 *errptr = EINVAL;
1582 return -1;
1583}
1584
1585 static int
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001586tcl_channel_output(
1587 ClientData instance,
1588 const char *buf,
1589 int bufsiz,
1590 int *errptr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001591{
1592 char_u *str;
1593 int result;
1594
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01001595 // The buffer is not guaranteed to be 0-terminated, and we don't if
1596 // there is enough room to add a '\0'. So we have to create a copy
1597 // of the buffer...
Bram Moolenaar071d4272004-06-13 20:20:40 +00001598 str = vim_strnsave((char_u *)buf, bufsiz);
1599 if (!str)
1600 {
1601 Tcl_SetErrno(ENOMEM);
1602 if (errptr)
1603 *errptr = ENOMEM;
1604 return -1;
1605 }
1606
1607 result = bufsiz;
1608 if (instance == VIMOUT)
1609 tclmsg((char *)str);
1610 else
1611 if (instance == VIMERR)
1612 tclerrmsg((char *)str);
1613 else
1614 {
1615 Tcl_SetErrno(EBADF);
1616 if (errptr)
1617 *errptr = EBADF;
1618 result = -1;
1619 }
1620 vim_free(str);
1621 return result;
1622}
1623
Bram Moolenaar071d4272004-06-13 20:20:40 +00001624 static void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001625tcl_channel_watch(ClientData instance UNUSED, int mask UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001626{
1627 Tcl_SetErrno(EINVAL);
1628}
1629
Bram Moolenaar071d4272004-06-13 20:20:40 +00001630 static int
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001631tcl_channel_gethandle(
1632 ClientData instance UNUSED,
1633 int direction UNUSED,
1634 ClientData *handleptr UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001635{
1636 Tcl_SetErrno(EINVAL);
1637 return EINVAL;
1638}
1639
1640
Bram Moolenaar0d6f8352016-01-27 11:07:47 +01001641static Tcl_ChannelType tcl_channel_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00001642{
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01001643 "vimmessage", // typeName
1644 TCL_CHANNEL_VERSION_2, // version
1645 tcl_channel_close, // closeProc
1646 tcl_channel_input, // inputProc
1647 tcl_channel_output, // outputProc
1648 NULL, // seekProc
1649 NULL, // setOptionProc
1650 NULL, // getOptionProc
1651 tcl_channel_watch, // watchProc
1652 tcl_channel_gethandle, // getHandleProc
1653 NULL, // close2Proc
1654 NULL, // blockModeProc
Bram Moolenaara41b1392009-05-23 12:28:15 +00001655#ifdef TCL_CHANNEL_VERSION_2
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01001656 NULL, // flushProc
1657 NULL, // handlerProc
Bram Moolenaara41b1392009-05-23 12:28:15 +00001658#endif
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01001659// The following should not be necessary since TCL_CHANNEL_VERSION_2 was
1660// set above
Bram Moolenaara41b1392009-05-23 12:28:15 +00001661#ifdef TCL_CHANNEL_VERSION_3
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01001662 NULL, // wideSeekProc
Bram Moolenaara41b1392009-05-23 12:28:15 +00001663#endif
1664#ifdef TCL_CHANNEL_VERSION_4
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01001665 NULL, // threadActionProc
Bram Moolenaara41b1392009-05-23 12:28:15 +00001666#endif
1667#ifdef TCL_CHANNEL_VERSION_5
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01001668 NULL // truncateProc
Bram Moolenaara41b1392009-05-23 12:28:15 +00001669#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001670};
1671
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01001672///////////////////////////////////
1673// Interface to vim
1674//////////////////////////////////
Bram Moolenaar071d4272004-06-13 20:20:40 +00001675
1676 static void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001677tclupdatevars(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001678{
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01001679 char varname[VARNAME_SIZE]; // must be writeable
Bram Moolenaar071d4272004-06-13 20:20:40 +00001680 char *name;
1681
1682 strcpy(varname, VAR_RANGE1);
1683 Tcl_UpdateLinkedVar(tclinfo.interp, varname);
1684 strcpy(varname, VAR_RANGE2);
1685 Tcl_UpdateLinkedVar(tclinfo.interp, varname);
1686 strcpy(varname, VAR_RANGE3);
1687 Tcl_UpdateLinkedVar(tclinfo.interp, varname);
1688
1689 strcpy(varname, VAR_LBASE);
1690 Tcl_UpdateLinkedVar(tclinfo.interp, varname);
1691
1692 name = tclgetbuffer(tclinfo.interp, curbuf);
1693 strcpy(tclinfo.curbuf, name);
1694 strcpy(varname, VAR_CURBUF);
1695 Tcl_UpdateLinkedVar(tclinfo.interp, varname);
1696
1697 name = tclgetwindow(tclinfo.interp, curwin);
1698 strcpy(tclinfo.curwin, name);
1699 strcpy(varname, VAR_CURWIN);
1700 Tcl_UpdateLinkedVar(tclinfo.interp, varname);
1701}
1702
1703
1704 static int
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001705tclinit(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001706{
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01001707 char varname[VARNAME_SIZE]; // Tcl_LinkVar requires writeable varname
Bram Moolenaar071d4272004-06-13 20:20:40 +00001708 char *name;
1709
1710#ifdef DYNAMIC_TCL
1711 if (!tcl_enabled(TRUE))
1712 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001713 emsg(_("E571: Sorry, this command is disabled: the Tcl library could not be loaded."));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001714 return FAIL;
1715 }
1716#endif
1717
1718 if (!tclinfo.interp)
1719 {
1720 Tcl_Interp *interp;
1721 static Tcl_Channel ch1, ch2;
1722
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01001723 // Create replacement channels for stdout and stderr; this has to be
1724 // done each time an interpreter is created since the channels are closed
1725 // when the interpreter is deleted
Bram Moolenaar0d6f8352016-01-27 11:07:47 +01001726 ch1 = Tcl_CreateChannel(&tcl_channel_type, "vimout", VIMOUT, TCL_WRITABLE);
1727 ch2 = Tcl_CreateChannel(&tcl_channel_type, "vimerr", VIMERR, TCL_WRITABLE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001728 Tcl_SetStdChannel(ch1, TCL_STDOUT);
1729 Tcl_SetStdChannel(ch2, TCL_STDERR);
1730
1731 interp = Tcl_CreateInterp();
1732 Tcl_Preserve(interp);
1733 if (Tcl_Init(interp) == TCL_ERROR)
1734 {
1735 Tcl_Release(interp);
1736 Tcl_DeleteInterp(interp);
1737 return FAIL;
1738 }
1739#if 0
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01001740 // VIM sure is interactive
Bram Moolenaar071d4272004-06-13 20:20:40 +00001741 Tcl_SetVar(interp, "tcl_interactive", "1", TCL_GLOBAL_ONLY);
1742#endif
1743
1744 Tcl_SetChannelOption(interp, ch1, "-buffering", "line");
Bram Moolenaar4f974752019-02-17 17:44:42 +01001745#ifdef MSWIN
Bram Moolenaar84a4c332012-02-22 16:01:56 +01001746 Tcl_SetChannelOption(interp, ch1, "-translation", "lf");
1747#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001748 Tcl_SetChannelOption(interp, ch2, "-buffering", "line");
Bram Moolenaar4f974752019-02-17 17:44:42 +01001749#ifdef MSWIN
Bram Moolenaar84a4c332012-02-22 16:01:56 +01001750 Tcl_SetChannelOption(interp, ch2, "-translation", "lf");
1751#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001752
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01001753 // replace standard Tcl exit command
Bram Moolenaar071d4272004-06-13 20:20:40 +00001754 Tcl_DeleteCommand(interp, "exit");
1755 Tcl_CreateObjCommand(interp, "exit", exitcmd,
1756 (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001757
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01001758 // new commands, in ::vim namespace
Bram Moolenaar071d4272004-06-13 20:20:40 +00001759 Tcl_CreateObjCommand(interp, "::vim::buffer", buffercmd,
1760 (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
1761 Tcl_CreateObjCommand(interp, "::vim::window", windowcmd,
1762 (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
1763 Tcl_CreateObjCommand(interp, "::vim::command", commandcmd,
1764 (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
1765 Tcl_CreateObjCommand(interp, "::vim::beep", beepcmd,
1766 (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
1767 Tcl_CreateObjCommand(interp, "::vim::option", optioncmd,
1768 (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
1769 Tcl_CreateObjCommand(interp, "::vim::expr", exprcmd,
1770 (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
1771
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01001772 // "lbase" variable
Bram Moolenaar071d4272004-06-13 20:20:40 +00001773 tclinfo.lbase = 1;
1774 strcpy(varname, VAR_LBASE);
1775 Tcl_LinkVar(interp, varname, (char *)&tclinfo.lbase, TCL_LINK_INT);
1776
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01001777 // "range" variable
Bram Moolenaar071d4272004-06-13 20:20:40 +00001778 tclinfo.range_start = eap->line1;
1779 strcpy(varname, VAR_RANGE1);
1780 Tcl_LinkVar(interp, varname, (char *)&tclinfo.range_start, TCL_LINK_INT|TCL_LINK_READ_ONLY);
1781 strcpy(varname, VAR_RANGE2);
1782 Tcl_LinkVar(interp, varname, (char *)&tclinfo.range_start, TCL_LINK_INT|TCL_LINK_READ_ONLY);
1783 tclinfo.range_end = eap->line2;
1784 strcpy(varname, VAR_RANGE3);
1785 Tcl_LinkVar(interp, varname, (char *)&tclinfo.range_end, TCL_LINK_INT|TCL_LINK_READ_ONLY);
1786
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01001787 // "current" variable
Bram Moolenaar071d4272004-06-13 20:20:40 +00001788 tclinfo.curbuf = Tcl_Alloc(VARNAME_SIZE);
1789 tclinfo.curwin = Tcl_Alloc(VARNAME_SIZE);
1790 name = tclgetbuffer(interp, curbuf);
1791 strcpy(tclinfo.curbuf, name);
1792 strcpy(varname, VAR_CURBUF);
1793 Tcl_LinkVar(interp, varname, (char *)&tclinfo.curbuf, TCL_LINK_STRING|TCL_LINK_READ_ONLY);
1794 name = tclgetwindow(interp, curwin);
1795 strcpy(tclinfo.curwin, name);
1796 strcpy(varname, VAR_CURWIN);
1797 Tcl_LinkVar(interp, varname, (char *)&tclinfo.curwin, TCL_LINK_STRING|TCL_LINK_READ_ONLY);
1798
1799 tclinfo.interp = interp;
1800 }
1801 else
1802 {
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01001803 // Interpreter already exists, just update variables
Bram Moolenaar071d4272004-06-13 20:20:40 +00001804 tclinfo.range_start = row2tcl(eap->line1);
1805 tclinfo.range_end = row2tcl(eap->line2);
1806 tclupdatevars();
1807 }
Bram Moolenaar84a4c332012-02-22 16:01:56 +01001808
1809 tclinfo.exitvalue = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001810 return OK;
1811}
1812
1813 static void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001814tclerrmsg(char *text)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001815{
1816 char *next;
1817
1818 while ((next=strchr(text, '\n')))
1819 {
1820 *next++ = '\0';
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001821 emsg(text);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001822 text = next;
1823 }
1824 if (*text)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001825 emsg(text);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001826}
1827
1828 static void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001829tclmsg(char *text)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001830{
1831 char *next;
1832
1833 while ((next=strchr(text, '\n')))
1834 {
1835 *next++ = '\0';
Bram Moolenaar32526b32019-01-19 17:43:09 +01001836 msg(text);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001837 text = next;
1838 }
1839 if (*text)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001840 msg(text);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001841}
1842
1843 static void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001844tcldelthisinterp(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001845{
1846 if (!Tcl_InterpDeleted(tclinfo.interp))
1847 Tcl_DeleteInterp(tclinfo.interp);
1848 Tcl_Release(tclinfo.interp);
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01001849 // The interpreter is now gets deleted. All registered commands (esp.
1850 // window and buffer commands) are deleted, triggering their deletion
1851 // callback, which deletes all refs pointing to this interpreter.
1852 // We could garbage-collect the unused ref structs in all windows and
1853 // buffers, but unless the user creates hundreds of sub-interpreters
1854 // all referring to lots of windows and buffers, this is hardly worth
1855 // the effort. Unused refs are recycled by other interpreters, and
1856 // all refs are free'd when the window/buffer gets closed by vim.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001857
1858 tclinfo.interp = NULL;
1859 Tcl_Free(tclinfo.curbuf);
1860 Tcl_Free(tclinfo.curwin);
1861 tclinfo.curbuf = tclinfo.curwin = NULL;
1862}
1863
1864 static int
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001865tclexit(int error)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001866{
1867 int newerr = OK;
1868
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01001869 if (Tcl_InterpDeleted(tclinfo.interp) // True if we intercepted Tcl's exit command
Bram Moolenaar84a4c332012-02-22 16:01:56 +01001870#if (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION >= 5) || TCL_MAJOR_VERSION > 8
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01001871 || Tcl_LimitExceeded(tclinfo.interp) // True if the interpreter cannot continue
Bram Moolenaar84a4c332012-02-22 16:01:56 +01001872#endif
1873 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001874 {
Bram Moolenaar555b2802005-05-19 21:08:39 +00001875 char buf[50];
Bram Moolenaar071d4272004-06-13 20:20:40 +00001876
Bram Moolenaar84a4c332012-02-22 16:01:56 +01001877 sprintf(buf, _("E572: exit code %d"), tclinfo.exitvalue);
1878 tclerrmsg(buf);
1879 if (tclinfo.exitvalue == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001880 {
Bram Moolenaar84a4c332012-02-22 16:01:56 +01001881 did_emsg = 0;
1882 newerr = OK;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001883 }
1884 else
Bram Moolenaar84a4c332012-02-22 16:01:56 +01001885 newerr = FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001886
1887 tcldelthisinterp();
1888 }
1889 else
1890 {
1891 char *result;
1892
Bram Moolenaar7df2d662005-01-25 22:18:08 +00001893 result = (char *)Tcl_GetStringResult(tclinfo.interp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001894 if (error == TCL_OK)
1895 {
1896 tclmsg(result);
1897 newerr = OK;
1898 }
1899 else
1900 {
1901 tclerrmsg(result);
1902 newerr = FAIL;
1903 }
1904 }
1905
1906 return newerr;
1907}
1908
1909/*
1910 * ":tcl"
1911 */
1912 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001913ex_tcl(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001914{
1915 char_u *script;
1916 int err;
1917
1918 script = script_get(eap, eap->arg);
1919 if (!eap->skip)
1920 {
1921 err = tclinit(eap);
1922 if (err == OK)
1923 {
1924 Tcl_AllowExceptions(tclinfo.interp);
1925 if (script == NULL)
1926 err = Tcl_Eval(tclinfo.interp, (char *)eap->arg);
1927 else
1928 err = Tcl_Eval(tclinfo.interp, (char *)script);
1929 err = tclexit(err);
1930 }
1931 }
1932 vim_free(script);
1933}
1934
1935/*
1936 * ":tclfile"
1937 */
1938 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001939ex_tclfile(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001940{
1941 char *file = (char *)eap->arg;
1942 int err;
1943
1944 err = tclinit(eap);
1945 if (err == OK)
1946 {
1947 Tcl_AllowExceptions(tclinfo.interp);
1948 err = Tcl_EvalFile(tclinfo.interp, file);
1949 err = tclexit(err);
1950 }
1951}
1952
1953/*
1954 * ":tcldo"
1955 */
1956 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001957ex_tcldo(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001958{
1959 char *script, *line;
1960 int err, rs, re, lnum;
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01001961 char var_lnum[VARNAME_SIZE]; // must be writeable memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00001962 char var_line[VARNAME_SIZE];
1963 linenr_T first_line = 0;
1964 linenr_T last_line = 0;
Bram Moolenaara4c906a2017-01-29 23:26:37 +01001965 buf_T *was_curbuf = curbuf;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001966
1967 rs = eap->line1;
1968 re = eap->line2;
1969 script = (char *)eap->arg;
1970 strcpy(var_lnum, VAR_CURLNUM);
1971 strcpy(var_line, VAR_CURLINE);
1972
1973 err = tclinit(eap);
1974 if (err != OK)
1975 return;
1976
1977 lnum = row2tcl(rs);
1978 Tcl_LinkVar(tclinfo.interp, var_lnum, (char *)&lnum, TCL_LINK_INT|TCL_LINK_READ_ONLY);
1979 err = TCL_OK;
1980 if (u_save((linenr_T)(rs-1), (linenr_T)(re+1)) != OK)
1981 {
1982 Tcl_SetResult(tclinfo.interp, _("cannot save undo information"), TCL_STATIC);
1983 err = TCL_ERROR;
1984 }
1985 while (err == TCL_OK && rs <= re)
1986 {
Bram Moolenaara4c906a2017-01-29 23:26:37 +01001987 if ((linenr_T)rs > curbuf->b_ml.ml_line_count)
1988 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001989 line = (char *)ml_get_buf(curbuf, (linenr_T)rs, FALSE);
1990 if (!line)
1991 {
1992 Tcl_SetResult(tclinfo.interp, _("cannot get line"), TCL_STATIC);
1993 err = TCL_ERROR;
1994 break;
1995 }
1996 Tcl_SetVar(tclinfo.interp, var_line, line, 0);
1997 Tcl_AllowExceptions(tclinfo.interp);
1998 err = Tcl_Eval(tclinfo.interp, script);
Bram Moolenaar84a4c332012-02-22 16:01:56 +01001999 if (err != TCL_OK
2000 || Tcl_InterpDeleted(tclinfo.interp)
2001#if (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION >= 5) || TCL_MAJOR_VERSION > 8
2002 || Tcl_LimitExceeded(tclinfo.interp)
2003#endif
Bram Moolenaara4c906a2017-01-29 23:26:37 +01002004 || curbuf != was_curbuf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002005 break;
Bram Moolenaar7df2d662005-01-25 22:18:08 +00002006 line = (char *)Tcl_GetVar(tclinfo.interp, var_line, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002007 if (line)
2008 {
2009 if (ml_replace((linenr_T)rs, (char_u *)line, TRUE) != OK)
2010 {
2011 Tcl_SetResult(tclinfo.interp, _("cannot replace line"), TCL_STATIC);
2012 err = TCL_ERROR;
2013 break;
2014 }
2015 if (first_line == 0)
2016 first_line = rs;
2017 last_line = rs;
2018 }
2019 ++rs;
2020 ++lnum;
2021 Tcl_UpdateLinkedVar(tclinfo.interp, var_lnum);
2022 }
2023 if (first_line)
2024 changed_lines(first_line, 0, last_line + 1, (long)0);
2025
2026 Tcl_UnsetVar(tclinfo.interp, var_line, 0);
2027 Tcl_UnlinkVar(tclinfo.interp, var_lnum);
2028 if (err == TCL_OK)
2029 Tcl_ResetResult(tclinfo.interp);
2030
2031 (void)tclexit(err);
2032}
2033
2034 static void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002035tcldelallrefs(struct ref *ref)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002036{
2037 struct ref *next;
2038 int err;
2039 char *result;
2040
Bram Moolenaar858b96f2016-01-10 16:12:24 +01002041#ifdef DYNAMIC_TCL
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01002042 // TODO: this code currently crashes Vim on exit
Bram Moolenaar8a5115c2016-01-09 19:41:11 +01002043 if (exiting)
2044 return;
Bram Moolenaar858b96f2016-01-10 16:12:24 +01002045#endif
Bram Moolenaar8a5115c2016-01-09 19:41:11 +01002046
Bram Moolenaar071d4272004-06-13 20:20:40 +00002047 while (ref != NULL)
2048 {
2049 next = ref->next;
2050 if (ref->interp)
2051 {
2052 if (ref->delcmd)
2053 {
2054 err = Tcl_GlobalEvalObj(ref->interp, ref->delcmd);
2055 if (err != TCL_OK)
2056 {
Bram Moolenaar7df2d662005-01-25 22:18:08 +00002057 result = (char *)Tcl_GetStringResult(ref->interp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002058 if (result)
2059 tclerrmsg(result);
2060 }
2061 Tcl_DecrRefCount(ref->delcmd);
2062 ref->delcmd = NULL;
2063 }
2064 Tcl_DeleteCommandFromToken(ref->interp, ref->cmd);
2065 }
2066 Tcl_Free((char *)ref);
2067 ref = next;
2068 }
2069}
2070
2071 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002072tcl_buffer_free(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002073{
2074 struct ref *reflist;
2075
2076#ifdef DYNAMIC_TCL
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01002077 if (!stubs_initialized) // Not using Tcl, nothing to do.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002078 return;
2079#endif
2080
Bram Moolenaare344bea2005-09-01 20:46:49 +00002081 reflist = (struct ref *)(buf->b_tcl_ref);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002082 if (reflist != &refsdeleted)
2083 {
Bram Moolenaare344bea2005-09-01 20:46:49 +00002084 buf->b_tcl_ref = (void *)&refsdeleted;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002085 tcldelallrefs(reflist);
Bram Moolenaare344bea2005-09-01 20:46:49 +00002086 buf->b_tcl_ref = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002087 }
2088}
2089
Bram Moolenaar071d4272004-06-13 20:20:40 +00002090 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002091tcl_window_free(win_T *win)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002092{
2093 struct ref *reflist;
2094
2095#ifdef DYNAMIC_TCL
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01002096 if (!stubs_initialized) // Not using Tcl, nothing to do.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002097 return;
2098#endif
2099
Bram Moolenaare344bea2005-09-01 20:46:49 +00002100 reflist = (struct ref*)(win->w_tcl_ref);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002101 if (reflist != &refsdeleted)
2102 {
Bram Moolenaare344bea2005-09-01 20:46:49 +00002103 win->w_tcl_ref = (void *)&refsdeleted;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002104 tcldelallrefs(reflist);
Bram Moolenaare344bea2005-09-01 20:46:49 +00002105 win->w_tcl_ref = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002106 }
2107}
Bram Moolenaar071d4272004-06-13 20:20:40 +00002108
Bram Moolenaar2ab2e862019-12-04 21:24:53 +01002109// The End