diff options
Diffstat (limited to 'src/if_tcl.c')
-rw-r--r-- | src/if_tcl.c | 2118 |
1 files changed, 2118 insertions, 0 deletions
diff --git a/src/if_tcl.c b/src/if_tcl.c new file mode 100644 index 0000000000..61b1fb9da9 --- /dev/null +++ b/src/if_tcl.c @@ -0,0 +1,2118 @@ +/* vi:set ts=8 sw=4: + * + * VIM - Vi IMproved by Bram Moolenaar + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + * See README.txt for an overview of the Vim source code. + */ + +/* + * Tcl extensions by Ingo Wilken <Ingo.Wilken@informatik.uni-oldenburg.de> + * Last modification: Wed May 10 21:28:44 CEST 2000 + * Requires Tcl 8.0 or higher. + * + * Variables: + * ::vim::current(buffer) # Name of buffer command for current buffer. + * ::vim::current(window) # Name of window command for current window. + * ::vim::range(start) # Start of current range (line number). + * ::vim::range(end) # End of current range (line number). + * ::vim::lbase # Start of line/column numbers (1 or 0). + * + * Commands: + * ::vim::command {cmd} # Execute ex command {cmd}. + * ::vim::option {opt} [val] # Get/Set option {opt}. + * ::vim::expr {expr} # Evaluate {expr} using vim's evaluator. + * ::vim::beep # Guess. + * + * set buf [::vim::buffer {n}] # Create Tcl command for buffer N. + * set bl [::vim::buffer list] # Get list of Tcl commands of all buffers. + * ::vim::buffer exists {n} # True if buffer {n} exists. + * + * set wl [::vim::window list] # Get list of Tcl commands of all windows. + * + * set n [$win height] # Report window height. + * $win height {n} # Set window height to {n}. + * array set pos [$win cursor] # Get cursor position. + * $win cursor {row} {col} # Set cursor position. + * $win cursor pos # Set cursor position from array var "pos" + * $win delcmd {cmd} # Register callback command for closed window. + * $win option {opt} [val] # Get/Set vim option in context of $win. + * $win command {cmd} # Execute ex command in context of $win. + * $win expr {expr} # Evaluate vim expression in context of $win. + * set buf [$win buffer] # Create Tcl command for window's buffer. + * + * $buf name # Reports file name in buffer. + * $buf number # Reports buffer number. + * set l [$buf get {n}] # Get buffer line {n} as a string. + * set L [$buf get {n} {m}] # Get lines {n} through {m} as a list. + * $buf count # Reports number of lines in buffer. + * $buf last # Reports number of last line in buffer. + * $buf delete {n} # Delete line {n}. + * $buf delete {n} {m} # Delete lines {n} through {m}. + * $buf set {n} {l} # Set line {n} to string {l}. + * $buf set {n} {m} {L} # Set lines {n} through {m} from list {L}. + * # Delete/inserts lines as appropriate. + * $buf option {opt} [val] # Get/Set vim option in context of $buf. + * $buf command {cmd} # Execute ex command in context of $buf + * $buf expr {cmd} # Evaluate vim expression in context of $buf. + * array set pos [$buf mark {m}] # Get position of mark. + * $buf append {n} {str} # Append string {str} to buffer,after line {n}. + * $buf insert {n} {str} # Insert string {str} in buffer as line {n}. + * $buf delcmd {cmd} # Register callback command for deleted buffer. + * set wl [$buf windows] # Get list of Tcl commands for all windows of + * # this buffer. +TODO: + * ::vim::buffer new # create new buffer + Tcl command + */ + +#include "vim.h" +#undef EXTERN /* tcl.h defines it too */ + +#ifdef DYNAMIC_TCL +# define USE_TCL_STUBS /* use tcl's stubs mechanism */ +#endif + +#include <tcl.h> +#include <errno.h> +#include <string.h> + +typedef struct +{ + Tcl_Interp *interp; + int range_start, range_end; + int lbase; + char *curbuf, *curwin; +} tcl_info; + +static tcl_info tclinfo = { NULL, 0, 0, 0, NULL, NULL }; + +#define VAR_RANGE1 "::vim::range(start)" +#define VAR_RANGE2 "::vim::range(begin)" +#define VAR_RANGE3 "::vim::range(end)" +#define VAR_CURBUF "::vim::current(buffer)" +#define VAR_CURWIN "::vim::current(window)" +#define VAR_LBASE "::vim::lbase" +#define VAR_CURLINE "line" +#define VAR_CURLNUM "lnum" +#define VARNAME_SIZE 64 + +#define row2tcl(x) ((x) - (tclinfo.lbase==0)) +#define row2vim(x) ((x) + (tclinfo.lbase==0)) +#define col2tcl(x) ((x) + (tclinfo.lbase!=0)) +#define col2vim(x) ((x) - (tclinfo.lbase!=0)) + + +#define VIMOUT ((ClientData)1) +#define VIMERR ((ClientData)2) + +/* + * List of Tcl interpreters who reference a vim window or buffer. + * Each buffer and window has it's own list in the tcl_ref struct member. + * We need this because Tcl can create sub-interpreters with the "interp" + * command, and each interpreter can reference all windows and buffers. + */ +struct ref +{ + struct ref *next; + + Tcl_Interp *interp; + Tcl_Command cmd; /* Tcl command that represents this object */ + Tcl_Obj *delcmd; /* Tcl command to call when object is being del. */ + void *vimobj; /* Vim window or buffer (win_T* or buf_T*) */ +}; +static char * tclgetbuffer _ANSI_ARGS_((Tcl_Interp *interp, buf_T *buf)); +static char * tclgetwindow _ANSI_ARGS_((Tcl_Interp *interp, win_T *win)); +static int tclsetdelcmd _ANSI_ARGS_((Tcl_Interp *interp, struct ref *reflist, void *vimobj, Tcl_Obj *delcmd)); +static int tclgetlinenum _ANSI_ARGS_ ((Tcl_Interp *interp, Tcl_Obj *obj, int *valueP, buf_T *buf)); +static win_T *tclfindwin _ANSI_ARGS_ ((buf_T *buf)); +static int tcldoexcommand _ANSI_ARGS_ ((Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], int objn)); +static int tclsetoption _ANSI_ARGS_ ((Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], int objn)); +static int tclvimexpr _ANSI_ARGS_ ((Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], int objn)); +static void tcldelthisinterp _ANSI_ARGS_ ((void)); + +static int vimerror _ANSI_ARGS_((Tcl_Interp *interp)); +static void tclmsg _ANSI_ARGS_((char *text)); +static void tclerrmsg _ANSI_ARGS_((char *text)); +static void tclupdatevars _ANSI_ARGS_((void)); + +static struct ref refsdeleted; /* dummy object for deleted ref list */ + +/***************************************************************************** + * TCL interface manager + ****************************************************************************/ + +#if defined(DYNAMIC_TCL) || defined(PROTO) +# ifndef DYNAMIC_TCL_DLL +# define DYNAMIC_TCL_DLL "tcl83.dll" +# endif +# ifndef DYNAMIC_TCL_VER +# define DYNAMIC_TCL_VER "8.3" +# endif + +# ifndef DYNAMIC_TCL /* Just generating prototypes */ +typedef int HANDLE; +# endif + +/* + * Declare HANDLE for perl.dll and function pointers. + */ +static HANDLE hTclLib = NULL; +Tcl_Interp* (*dll_Tcl_CreateInterp)(); + +/* + * Table of name to function pointer of tcl. + */ +#define TCL_PROC FARPROC +static struct { + char* name; + TCL_PROC* ptr; +} tcl_funcname_table[] = { + {"Tcl_CreateInterp", (TCL_PROC*)&dll_Tcl_CreateInterp}, + {NULL, NULL}, +}; + +/* + * Make all runtime-links of tcl. + * + * 1. Get module handle using LoadLibraryEx. + * 2. Get pointer to perl function by GetProcAddress. + * 3. Repeat 2, until get all functions will be used. + * + * Parameter 'libname' provides name of DLL. + * Return OK or FAIL. + */ + static int +tcl_runtime_link_init(char *libname, int verbose) +{ + int i; + + if (hTclLib) + return OK; + if (!(hTclLib = LoadLibraryEx(libname, NULL, 0))) + { + if (verbose) + EMSG2(_(e_loadlib), libname); + return FAIL; + } + for (i = 0; tcl_funcname_table[i].ptr; ++i) + { + if (!(*tcl_funcname_table[i].ptr = GetProcAddress(hTclLib, + tcl_funcname_table[i].name))) + { + FreeLibrary(hTclLib); + hTclLib = NULL; + if (verbose) + EMSG2(_(e_loadfunc), tcl_funcname_table[i].name); + return FAIL; + } + } + return OK; +} +#endif /* defined(DYNAMIC_TCL) || defined(PROTO) */ + +#ifdef DYNAMIC_TCL +static char *find_executable_arg = NULL; +#endif + + void +vim_tcl_init(arg) + char *arg; +{ +#ifndef DYNAMIC_TCL + Tcl_FindExecutable(arg); +#else + find_executable_arg = arg; +#endif +} + +#if defined(DYNAMIC_TCL) || defined(PROTO) + +static int stubs_initialized = FALSE; + +/* + * Return TRUE if the TCL interface can be used. + */ + int +tcl_enabled(verbose) + int verbose; +{ + if (!stubs_initialized && find_executable_arg != NULL + && tcl_runtime_link_init(DYNAMIC_TCL_DLL, verbose) == OK) + { + Tcl_Interp *interp; + + if (interp = dll_Tcl_CreateInterp()) + { + if (Tcl_InitStubs(interp, DYNAMIC_TCL_VER, 0)) + { + Tcl_FindExecutable(find_executable_arg); + Tcl_DeleteInterp(interp); + stubs_initialized = TRUE; + } + /* FIXME: When Tcl_InitStubs() was failed, how delete interp? */ + } + } + return stubs_initialized; +} +#endif + + void +tcl_end() +{ +#ifdef DYNAMIC_TCL + if (hTclLib) + { + FreeLibrary(hTclLib); + hTclLib = NULL; + } +#endif +} + +/**************************************************************************** + Tcl commands + ****************************************************************************/ + +/* + * Replace standard "exit" and "catch" commands. + * + * This is a design flaw in Tcl - the standard "exit" command just calls + * exit() and kills the application. It should return TCL_EXIT to the + * app, which then decides if it wants to terminate or not. In our case, + * we just delete the Tcl interpreter (and create a new one with the next + * :tcl command). + */ +#define TCL_EXIT 5 + +/* ARGSUSED */ + static int +exitcmd(dummy, interp, objc, objv) + ClientData dummy; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST objv[]; +{ + int value = 0; + + switch (objc) + { + case 2: + if (Tcl_GetIntFromObj(interp, objv[1], &value) != TCL_OK) + break; + /* FALLTHROUGH */ + case 1: + Tcl_SetObjResult(interp, Tcl_NewIntObj(value)); + return TCL_EXIT; + default: + Tcl_WrongNumArgs(interp, 1, objv, "?returnCode?"); + } + return TCL_ERROR; +} + +/* ARGSUSED */ + static int +catchcmd(dummy, interp, objc, objv) + ClientData dummy; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST objv[]; +{ + char *varname = NULL; + int result; + + switch (objc) + { + case 3: + varname = Tcl_GetStringFromObj(objv[2], NULL); + /* fallthrough */ + case 2: + Tcl_ResetResult(interp); + Tcl_AllowExceptions(interp); + result = Tcl_EvalObj(interp, objv[1]); + if (result == TCL_EXIT) + return result; + if (varname) + { + if (Tcl_SetVar(interp, varname, Tcl_GetStringResult(interp), 0) == NULL) + { + Tcl_SetResult(interp, "couldn't save command result in variable", TCL_STATIC); + return TCL_ERROR; + } + } + Tcl_SetObjResult(interp, Tcl_NewIntObj(result)); + return TCL_OK; + default: + Tcl_WrongNumArgs(interp, 1, objv, "command ?varName?"); + } + return TCL_ERROR; +} + +/* + * "::vim::beep" - what Vi[m] does best :-) + */ +/* ARGSUSED */ + static int +beepcmd(dummy, interp, objc, objv) + ClientData dummy; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST objv[]; +{ + if (objc != 1) + { + Tcl_WrongNumArgs(interp, 1, objv, NULL); + return TCL_ERROR; + } + vim_beep(); + return TCL_OK; +} + +/* + * "::vim::buffer list" - create a list of buffer commands. + * "::vim::bufffer {N}" - create buffer command for buffer N. + * "::vim::buffer new" - create a new buffer (not implemented) + */ +/* ARGSUSED */ + static int +buffercmd(dummy, interp, objc, objv) + ClientData dummy; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST objv[]; +{ + char *name; + buf_T *buf; + Tcl_Obj *resobj; + int err, n, idx; + + enum {BCMD_EXISTS, BCMD_LIST}; + static char *bcmdoptions[] = + { + "exists", "list", (char *)0 + }; + + if (objc < 2) + { + Tcl_WrongNumArgs(interp, 1, objv, "option"); + return TCL_ERROR; + } + err = Tcl_GetIntFromObj(interp, objv[1], &n); + if (err == TCL_OK) + { + if (objc != 2) + { + Tcl_WrongNumArgs(interp, 1, objv, "bufNumber"); + return TCL_ERROR; + } + for (buf = firstbuf; buf != NULL; buf = buf->b_next) + { + if (buf->b_fnum == n) + { + name = tclgetbuffer(interp, buf); + if (name == NULL) + return TCL_ERROR; + Tcl_SetResult(interp, name, TCL_VOLATILE); + return TCL_OK; + } + } + Tcl_SetResult(interp, _("invalid buffer number"), TCL_STATIC); + return TCL_ERROR; + } + Tcl_ResetResult(interp); /* clear error from Tcl_GetIntFromObj */ + + err = Tcl_GetIndexFromObj(interp, objv[1], bcmdoptions, "option", 0, &idx); + if (err != TCL_OK) + return err; + switch (idx) + { + case BCMD_LIST: + if (objc != 2) + { + Tcl_WrongNumArgs(interp, 2, objv, ""); + err = TCL_ERROR; + break; + } + for (buf = firstbuf; buf != NULL; buf = buf->b_next) + { + name = tclgetbuffer(interp, buf); + if (name == NULL) + { + err = TCL_ERROR; + break; + } + Tcl_AppendElement(interp, name); + } + break; + + case BCMD_EXISTS: + if (objc != 3) + { + Tcl_WrongNumArgs(interp, 2, objv, "bufNumber"); + err = TCL_ERROR; + break; + } + err = Tcl_GetIntFromObj(interp, objv[2], &n); + if (err == TCL_OK) + { + buf = buflist_findnr(n); + resobj = Tcl_NewIntObj(buf != NULL); + Tcl_SetObjResult(interp, resobj); + } + break; + + default: + Tcl_SetResult(interp, _("not implemented yet"), TCL_STATIC); + err = TCL_ERROR; + } + return err; +} + +/* + * "::vim::window list" - create list of window commands. + */ +/* ARGSUSED */ + static int +windowcmd(dummy, interp, objc, objv) + ClientData dummy; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST objv[]; +{ + char *what, *string; + win_T *win; + + if (objc != 2) + { + Tcl_WrongNumArgs(interp, 1, objv, "option"); + return TCL_ERROR; + } + what = Tcl_GetStringFromObj(objv[1], NULL); + if (strcmp(what, "list") == 0) + { + FOR_ALL_WINDOWS(win) + { + string = tclgetwindow(interp, win); + if (string == NULL) + return TCL_ERROR; + Tcl_AppendElement(interp, string); + } + return TCL_OK; + } + Tcl_SetResult(interp, _("unknown option"), TCL_STATIC); + return TCL_ERROR; +} + +/* + * flags for bufselfcmd and winselfcmd to indicate outstanding actions. + */ +#define FL_UPDATE_SCREEN (1<<0) +#define FL_UPDATE_CURBUF (1<<1) +#define FL_ADJUST_CURSOR (1<<2) + +/* + * This function implements the buffer commands. + */ + static int +bufselfcmd(ref, interp, objc, objv) + ClientData ref; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST objv[]; +{ + int opt, err, idx, flags; + int val1, val2, n, i; + buf_T *buf, *savebuf; + win_T *win, *savewin; + Tcl_Obj *resobj; + pos_T *pos; + char *line; + + enum + { + BUF_APPEND, BUF_COMMAND, BUF_COUNT, BUF_DELCMD, BUF_DELETE, BUF_EXPR, + BUF_GET, BUF_INSERT, BUF_LAST, BUF_MARK, BUF_NAME, BUF_NUMBER, + BUF_OPTION, BUF_SET, BUF_WINDOWS + }; + static char *bufoptions[] = + { + "append", "command", "count", "delcmd", "delete", "expr", + "get", "insert", "last", "mark", "name", "number", + "option", "set", "windows", (char *)0 + }; + + if (objc < 2) + { + Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?"); + return TCL_ERROR; + } + + err = Tcl_GetIndexFromObj(interp, objv[1], bufoptions, "option", 0, &idx); + if (err != TCL_OK) + return err; + + buf = (buf_T *)((struct ref *)ref)->vimobj; + savebuf = curbuf; curbuf = buf; + savewin = curwin; curwin = tclfindwin(buf); + flags = 0; + opt = 0; + + switch (idx) + { + case BUF_COMMAND: + err = tcldoexcommand(interp, objc, objv, 2); + flags |= FL_UPDATE_SCREEN; + break; + + case BUF_OPTION: + err = tclsetoption(interp, objc, objv, 2); + flags |= FL_UPDATE_SCREEN; + break; + + case BUF_EXPR: + err = tclvimexpr(interp, objc, objv, 2); + break; + + case BUF_NAME: + /* + * Get filename of buffer. + */ + if (objc != 2) + { + Tcl_WrongNumArgs(interp, 2, objv, NULL); + err = TCL_ERROR; + break; + } + if (buf->b_ffname) + Tcl_SetResult(interp, (char *)buf->b_ffname, TCL_VOLATILE); + else + Tcl_SetResult(interp, "", TCL_STATIC); + break; + + case BUF_LAST: + /* + * Get line number of last line. + */ + opt = 1; + /* fallthrough */ + case BUF_COUNT: + /* + * Get number of lines in buffer. + */ + if (objc != 2) + { + Tcl_WrongNumArgs(interp, 2, objv, NULL); + err = TCL_ERROR; + break; + } + val1 = (int)buf->b_ml.ml_line_count; + if (opt) + val1 = row2tcl(val1); + + resobj = Tcl_NewIntObj(val1); + Tcl_SetObjResult(interp, resobj); + break; + + case BUF_NUMBER: + /* + * Get buffer's number. + */ + if (objc != 2) + { + Tcl_WrongNumArgs(interp, 2, objv, NULL); + err = TCL_ERROR; + break; + } + resobj = Tcl_NewIntObj((int)buf->b_fnum); + Tcl_SetObjResult(interp, resobj); + break; + + case BUF_GET: + if (objc != 3 && objc != 4) + { + Tcl_WrongNumArgs(interp, 2, objv, "lineNumber ?lineNumber?"); + err = TCL_ERROR; + break; + } + err = tclgetlinenum(interp, objv[2], &val1, buf); + if (err != TCL_OK) + break; + if (objc == 4) + { + err = tclgetlinenum(interp, objv[3], &val2, buf); + if (err != TCL_OK) + break; + if (val1 > val2) + { + n = val1; val1 = val2; val2 = n; + } + Tcl_ResetResult(interp); + + for (n = val1; n <= val2 && err == TCL_OK; n++) + { + line = (char *)ml_get_buf(buf, (linenr_T)n, FALSE); + if (line) + Tcl_AppendElement(interp, line); + else + err = TCL_ERROR; + } + } + else { /* objc == 3 */ + line = (char *)ml_get_buf(buf, (linenr_T)val1, FALSE); + Tcl_SetResult(interp, line, TCL_VOLATILE); + } + break; + + case BUF_SET: + if (objc != 4 && objc != 5) + { + Tcl_WrongNumArgs(interp, 3, objv, "lineNumber ?lineNumber? stringOrList"); + err = TCL_ERROR; + break; + } + err = tclgetlinenum(interp, objv[2], &val1, buf); + if (err != TCL_OK) + return TCL_ERROR; + if (objc == 4) + { + /* + * Replace one line with a string. + * $buf set {n} {string} + */ + line = Tcl_GetStringFromObj(objv[3], NULL); + if (u_savesub((linenr_T)val1) != OK) + { + Tcl_SetResult(interp, _("cannot save undo information"), TCL_STATIC); + err = TCL_ERROR; + } + else + if (ml_replace((linenr_T)val1, (char_u *)line, TRUE) != OK) + { + Tcl_SetResult(interp, _("cannot replace line"), TCL_STATIC); + err = TCL_ERROR; + } + else + { + changed_bytes((linenr_T)val1, 0); + flags |= FL_UPDATE_CURBUF; + } + break; + } + else + { + /* + * Replace several lines with the elements of a Tcl list. + * $buf set {n} {m} {list} + * If the list contains more than {m}-{n}+1 elements, they + * are * inserted after line {m}. If the list contains fewer + * elements, * the lines from {n}+length({list}) through {m} + * are deleted. + */ + int lc; + Tcl_Obj **lv; + + err = tclgetlinenum(interp, objv[3], &val2, buf); + if (err != TCL_OK) + break; + err = Tcl_ListObjGetElements(interp, objv[4], &lc, &lv); + if (err != TCL_OK) + break; + if (val1 > val2) + { + n = val1; + val1 = val2; + val2 = n; + } + + n = val1; + if (u_save((linenr_T)(val1 - 1), (linenr_T)(val2 + 1)) != OK) + { + Tcl_SetResult(interp, _("cannot save undo information"), + TCL_STATIC); + err = TCL_ERROR; + break; + } + flags |= FL_UPDATE_CURBUF; + + for (i = 0; i < lc && n <= val2; i++) + { + line = Tcl_GetStringFromObj(lv[i], NULL); + if (ml_replace((linenr_T)n, (char_u *)line, TRUE) != OK) + goto setListError; + ++n; + } + if (i < lc) + { + /* append lines */ + do + { + line = Tcl_GetStringFromObj(lv[i], NULL); + if (ml_append((linenr_T)(n - 1), + (char_u *)line, 0, FALSE) != OK) + goto setListError; + ++n; + ++i; + } while (i < lc); + } + else if (n <= val2) + { + /* did not replace all lines, delete */ + i = n; + do + { + if (ml_delete((linenr_T)i, FALSE) != OK) + goto setListError; + ++n; + } while (n <= val2); + } + lc -= val2 - val1 + 1; /* number of lines to be replaced */ + mark_adjust((linenr_T)val1, (linenr_T)val2, (long)MAXLNUM, + (long)lc); + changed_lines((linenr_T)val1, 0, (linenr_T)val2 + 1, (long)lc); + break; + setListError: + u_undo(1); /* ??? */ + Tcl_SetResult(interp, _("cannot set line(s)"), TCL_STATIC); + err = TCL_ERROR; + } + break; + + case BUF_DELETE: + if (objc != 3 && objc != 4) + { + Tcl_WrongNumArgs(interp, 3, objv, "lineNumber ?lineNumber?"); + err = TCL_ERROR; + break; + } + err = tclgetlinenum(interp, objv[2], &val1, buf); + if (err != TCL_OK) + break; + val2 = val1; + if (objc == 4) + { + err = tclgetlinenum(interp, objv[3], &val2, buf); + if (err != TCL_OK) + return err; + if (val1 > val2) + { + i = val1; val1 = val2; val2 = i; + } + } + n = val2 - val1 + 1; + if (u_savedel((linenr_T)val1, (long)n) != OK) + { + Tcl_SetResult(interp, _("cannot save undo information"), + TCL_STATIC); + err = TCL_ERROR; + break; + } + for (i = 0; i < n; i++) + { + ml_delete((linenr_T)val1, FALSE); + err = vimerror(interp); + if (err != TCL_OK) + break; + } + if (i > 0) + deleted_lines_mark((linenr_T)val1, (long)i); + flags |= FL_ADJUST_CURSOR|FL_UPDATE_SCREEN; + break; + + case BUF_MARK: + if (objc != 3) + { + Tcl_WrongNumArgs(interp, 2, objv, "markName"); + err = TCL_ERROR; + break; + } + line = Tcl_GetStringFromObj(objv[2], NULL); + + pos = NULL; + if (line[0] != '\0' && line[1] == '\0') + { + pos = getmark(line[0], FALSE); + } + if (pos == NULL) + { + Tcl_SetResult(interp, _("invalid mark name"), TCL_STATIC); + err = TCL_ERROR; + break; + } + err = vimerror(interp); + if (err != TCL_OK) + break; + if (pos->lnum <= 0) + { + Tcl_SetResult(interp, _("mark not set"), TCL_STATIC); + err = TCL_ERROR; + } + else + { + char rbuf[64]; + sprintf(rbuf, _("row %d column %d"), (int)row2tcl(pos->lnum), (int)col2tcl(pos->col)); + Tcl_SetResult(interp, rbuf, TCL_VOLATILE); + } + break; + + case BUF_INSERT: + opt = 1; + /* fallthrough */ + case BUF_APPEND: + if (objc != 4) + { + Tcl_WrongNumArgs(interp, 2, objv, "lineNum text"); + err = TCL_ERROR; + break; + } + err = tclgetlinenum(interp, objv[2], &val1, buf); + if (err != TCL_OK) + break; + if (opt) + --val1; + if (u_save((linenr_T)val1, (linenr_T)(val1+1)) != OK) + { + Tcl_SetResult(interp, _("cannot save undo information"), TCL_STATIC); + err = TCL_ERROR; + break; + } + + line = Tcl_GetStringFromObj(objv[3], NULL); + if (ml_append((linenr_T)val1, (char_u *)line, 0, FALSE) != OK) + { + Tcl_SetResult(interp, _("cannot insert/append line"), TCL_STATIC); + err = TCL_ERROR; + break; + } + appended_lines_mark((linenr_T)val1, 1L); + flags |= FL_UPDATE_SCREEN; + break; + + case BUF_WINDOWS: + /* + * Return list of window commands. + */ + if (objc != 2) + { + Tcl_WrongNumArgs(interp, 2, objv, NULL); + err = TCL_ERROR; + break; + } + Tcl_ResetResult(interp); + FOR_ALL_WINDOWS(win) + { + if (win->w_buffer == buf) + { + line = tclgetwindow(interp, win); + if (line != NULL) + Tcl_AppendElement(interp, line); + else + { + err = TCL_ERROR; + break; + } + } + } + break; + + case BUF_DELCMD: + /* + * Register deletion callback. + * TODO: Should be able to register multiple callbacks + */ + if (objc != 3) + { + Tcl_WrongNumArgs(interp, 2, objv, "command"); + err = TCL_ERROR; + break; + } + err = tclsetdelcmd(interp, buf->tcl_ref, (void *)buf, objv[2]); + break; + + default: + Tcl_SetResult(interp, _("not implemented yet"), TCL_STATIC); + err = TCL_ERROR; + } + + if (flags & FL_UPDATE_CURBUF) + redraw_curbuf_later(NOT_VALID); + curbuf = savebuf; + curwin = savewin; + if (flags & FL_ADJUST_CURSOR) + check_cursor(); + if (flags & (FL_UPDATE_SCREEN | FL_UPDATE_CURBUF)) + update_screen(NOT_VALID); + + return err; +} + +/* + * This function implements the window commands. + */ + static int +winselfcmd(ref, interp, objc, objv) + ClientData ref; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST objv[]; +{ + int err, idx, flags; + int val1, val2; + Tcl_Obj *resobj; + win_T *savewin, *win; + buf_T *savebuf; + char *str; + + enum + { + WIN_BUFFER, WIN_COMMAND, WIN_CURSOR, WIN_DELCMD, WIN_EXPR, + WIN_HEIGHT, WIN_OPTION + }; + static char *winoptions[] = + { + "buffer", "command", "cursor", "delcmd", "expr", + "height", "option", (char *)0 + }; + + if (objc < 2) + { + Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?"); + return TCL_ERROR; + } + + err = Tcl_GetIndexFromObj(interp, objv[1], winoptions, "option", 0, &idx); + if (err != TCL_OK) + return TCL_ERROR; + + win = (win_T *)((struct ref *)ref)->vimobj; + savewin = curwin; curwin = win; + savebuf = curbuf; curbuf = win->w_buffer; + flags = 0; + + switch (idx) + { + case WIN_OPTION: + err = tclsetoption(interp, objc, objv, 2); + flags |= FL_UPDATE_SCREEN; + break; + + case WIN_COMMAND: + err = tcldoexcommand(interp, objc, objv, 2); + flags |= FL_UPDATE_SCREEN; + break; + + case WIN_EXPR: + err = tclvimexpr(interp, objc, objv, 2); + break; + + case WIN_HEIGHT: + if (objc == 3) + { + err = Tcl_GetIntFromObj(interp, objv[2], &val1); + if (err != TCL_OK) + break; +#ifdef FEAT_GUI + need_mouse_correct = TRUE; +#endif + win_setheight(val1); + err = vimerror(interp); + if (err != TCL_OK) + break; + } + else + if (objc != 2) + { + Tcl_WrongNumArgs(interp, 2, objv, "?value?"); + err = TCL_ERROR; + break; + } + + resobj = Tcl_NewIntObj((int)(win->w_height)); + Tcl_SetObjResult(interp, resobj); + break; + + case WIN_BUFFER: + if (objc != 2) + { + Tcl_WrongNumArgs(interp, 2, objv, NULL); + err = TCL_ERROR; + break; + } + str = tclgetbuffer(interp, win->w_buffer); + if (str) + Tcl_SetResult(interp, str, TCL_VOLATILE); + else + err = TCL_ERROR; + break; + + case WIN_DELCMD: + if (objc != 3) + { + Tcl_WrongNumArgs(interp, 2, objv, "command"); + err = TCL_ERROR; + break; + } + err = tclsetdelcmd(interp, win->tcl_ref, (void *)win, objv[2]); + break; + + case WIN_CURSOR: + if (objc > 4) + { + Tcl_WrongNumArgs(interp, 2, objv, "?arg1 ?arg2??"); + err = TCL_ERROR; + break; + } + if (objc == 2) + { + char buf[64]; + sprintf(buf, _("row %d column %d"), (int)row2tcl(win->w_cursor.lnum), (int)col2tcl(win->w_cursor.col)); + Tcl_SetResult(interp, buf, TCL_VOLATILE); + break; + } + else if (objc == 3) + { + Tcl_Obj *part, *var; + + part = Tcl_NewStringObj("row", -1); + var = Tcl_ObjGetVar2(interp, objv[2], part, TCL_LEAVE_ERR_MSG); + if (var == NULL) + { + err = TCL_ERROR; + break; + } + err = tclgetlinenum(interp, var, &val1, win->w_buffer); + if (err != TCL_OK) + break; + part = Tcl_NewStringObj("column", -1); + var = Tcl_ObjGetVar2(interp, objv[2], part, TCL_LEAVE_ERR_MSG); + if (var == NULL) + { + err = TCL_ERROR; + break; + } + err = Tcl_GetIntFromObj(interp, var, &val2); + if (err != TCL_OK) + break; + } + else { /* objc == 4 */ + err = tclgetlinenum(interp, objv[2], &val1, win->w_buffer); + if (err != TCL_OK) + break; + err = Tcl_GetIntFromObj(interp, objv[3], &val2); + if (err != TCL_OK) + break; + } + /* TODO: should check column */ + win->w_cursor.lnum = val1; + win->w_cursor.col = col2vim(val2); + flags |= FL_UPDATE_SCREEN; + break; + + default: + Tcl_SetResult(interp, _("not implemented yet"), TCL_STATIC); + break; + } + + curwin = savewin; + curbuf = savebuf; + if (flags & FL_UPDATE_SCREEN) + update_screen(NOT_VALID); + + return err; +} + + +/* ARGSUSED */ + static int +commandcmd(dummy, interp, objc, objv) + ClientData dummy; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST objv[]; +{ + int err; + + err = tcldoexcommand(interp, objc, objv, 1); + update_screen(VALID); + return err; +} + +/* ARGSUSED */ + static int +optioncmd(dummy, interp, objc, objv) + ClientData dummy; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST objv[]; +{ + int err; + + err = tclsetoption(interp, objc, objv, 1); + update_screen(VALID); + return err; +} + +/* ARGSUSED */ + static int +exprcmd(dummy, interp, objc, objv) + ClientData dummy; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST objv[]; +{ + return tclvimexpr(interp, objc, objv, 1); +} + +/**************************************************************************** + Support functions for Tcl commands + ****************************************************************************/ + +/* + * Get a line number from 'obj' and convert it to vim's range. + */ + static int +tclgetlinenum(interp, obj, valueP, buf) + Tcl_Interp *interp; + Tcl_Obj *obj; + int *valueP; + buf_T *buf; +{ + int err, i; + + enum { LN_BEGIN, LN_BOTTOM, LN_END, LN_FIRST, LN_LAST, LN_START, LN_TOP }; + + static char *keyw[] = + { + "begin", "bottom", "end", "first", "last", "start", "top", (char *)0 + }; + + err = Tcl_GetIndexFromObj(interp, obj, keyw, "", 0, &i); + if (err == TCL_OK) + { + switch (i) + { + case LN_BEGIN: + case LN_FIRST: + case LN_START: + case LN_TOP: + *valueP = 1; + break; + case LN_BOTTOM: + case LN_END: + case LN_LAST: + *valueP = buf->b_ml.ml_line_count; + break; + } + return TCL_OK; + } + Tcl_ResetResult(interp); + + err = Tcl_GetIntFromObj(interp, obj, &i); + if (err != TCL_OK) + return err; + i = row2vim(i); + if (i < 1 || i > buf->b_ml.ml_line_count) + { + Tcl_SetResult(interp, _("line number out of range"), TCL_STATIC); + return TCL_ERROR; + } + *valueP = i; + return TCL_OK; +} + +/* + * Find the first window in the window list that displays the buffer. + */ + static win_T * +tclfindwin(buf) + buf_T *buf; +{ + win_T *win; + + FOR_ALL_WINDOWS(win) + { + if (win->w_buffer == buf) + return win; + } + return curwin; /* keep current window context */ +} + +/* + * Do-it-all function for "::vim::command", "$buf command" and "$win command". + */ + static int +tcldoexcommand(interp, objc, objv, objn) + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST objv[]; + int objn; +{ + tcl_info saveinfo; + int err, flag, nobjs; + char *arg; + + nobjs = objc - objn; + if (nobjs < 1 || nobjs > 2) + { + Tcl_WrongNumArgs(interp, objn, objv, "?-quiet? exCommand"); + return TCL_ERROR; + } + + flag = 0; + if (nobjs == 2) + { + arg = Tcl_GetStringFromObj(objv[objn], NULL); + if (strcmp(arg, "-quiet") == 0) + flag = 1; + else + { + Tcl_ResetResult(interp); + Tcl_AppendResult(interp, _("unknown flag: "), arg, (char *)0); + return TCL_ERROR; + } + ++objn; + } + + memcpy(&saveinfo, &tclinfo, sizeof(tcl_info)); + tclinfo.interp = NULL; + tclinfo.curwin = NULL; + tclinfo.curbuf = NULL; + + arg = Tcl_GetStringFromObj(objv[objn], NULL); + if (flag) |