Analyse how f_system
works (the VimL system call).
eval.c: f_system()
infile = NULL if arg[1].v_type is not VAR_UNKNOWN:
- this is an input string
- look for a temp file name (vim_tempname, horrible and complex). infile
- fopen/fwrite/fclose
- res = get_cmd_output(get_tv_string(&arg[0]), infile, kShellOptSilent | kShellOptCooked)
- translate line endings if necessary
- os_remove(infile)
misc1.c: get_cmd_output(cmd, infile, flags) used by: f_system(infile), find_locales(NULL), expand_backtick(NULL)
// get the stdout of an external command, returns alloced string or NULL
- tempname = look for a temp file name (vim_tempname)
- cmd = make_filter_cmd(cmd, infile, tempname)
- call_shell(cmd, kShellOptDoOut | kShellOptExpand | flags, NULL)
- read tempname into memory (fopen, ftell, fread, fclose, os_remove)
- change NULs into SOHs, NUL-terminate
- return output
ex_cmds.c: make_filter_cmd(cmd, itmp, otmp)
// create a shell command from a cmd string, input redirection file and // output redirection file. Returns an alloced string with the shell // command
- (optionally) put braces around command
- (optionally itmp != NULL) add file input: " < "
- (optionally) special casing for non-UNIX shells that need " < " on the first command and not at the end of a pipe
- (optionally otmp != NULL) append_redir()
- return buf
ex_cmds.c: append_redir(buf, buflen, opt, fname)
// append output redirection for file fname to the end of string buffer, // works with the shellredir and shellpipe options // NOTE: shellredir = EXTERN p_srr = usually > I suppose... // NOTE: shellpipe = EXTERN p_sp = "| tee" (for unix), ">" otherwise
shell.c: os_call_shell(cmd, opts, extra_shell_args)
typedef void (shell_read_cb)(char_u buf, size_t cnt); proposed: os_call_shell(cmd, opts, extra_shell_args, write_buf, write_buf_size, shell_read_cb read_cb);
Functions and commands are defined differently, an example:
function! s:MyFunc(myParam)
do something
endfunction
command! -nargs=1 MyCommand call s:MyFunc(<f-args>)
There's buffer-local commands as well. All user-defined commands must stat with a capital letter. User-defined commands can be abbreviated, but it's not recommended. They can be undefined with :delcommand
.
- hash/func_hashtab
- ga_array/functions: ordered list of default functions, ordered because it needs to be able to do lookup of partial strings (e.g.: "function" can also be "fun")
- ga_array/script_items: store sourced scripts (an array of scriptitem_S)
- ga_array/ga_scripts: array that holds a dictionary for each script (each ) with the script-local variables
typedef struct exarg {
char_u *arg; /* argument of the command */
char_u *nextcmd; /* next command (NULL if none) */
char_u *cmd; /* the name of the command (except for :make) */
char_u **cmdlinep; /* pointer to pointer of allocated cmdline */
cmdidx_T cmdidx; /* the index for the command */
long argt; /* flags for the command */
int skip; /* don't execute the command, only parse it */
int forceit; /* TRUE if ! present */
int addr_count; /* the number of addresses given */
linenr_T line1; /* the first line number */
linenr_T line2; /* the second line number or count */
int flags; /* extra flags after count: EXFLAG_ */
char_u *do_ecmd_cmd; /* +command arg to be used in edited file */
linenr_T do_ecmd_lnum; /* the line number in an edited file */
int append; /* TRUE with ":w >>file" command */
int usefilter; /* TRUE with ":w !command" and ":r!command" */
int amount; /* number of '>' or '<' for shift command */
int regname; /* register name (NUL if none) */
int force_bin; /* 0, FORCE_BIN or FORCE_NOBIN */
int read_edit; /* ++edit argument */
int force_ff; /* ++ff= argument (index in cmd[]) */
int force_enc; /* ++enc= argument (index in cmd[]) */
int bad_char; /* BAD_KEEP, BAD_DROP or replacement byte */
int useridx; /* user command index */
char_u *errmsg; /* returned error message */
char_u *(*getline)(int, void *, int);
void *cookie; /* argument for getline() */
struct condstack *cstack; /* condition stack for ":if" etc. */
} exarg_T;
How doe eval_1
, eval_2
, et cetera intertwine with do_source
and the like? Surely do_source
must somehow call a parsing function. How does the parse call back? Is it just used as a verifier that everything is ok?
These are NOT functions. Mostly around ex_docmd.c
.
- Builtin commands are defined in a pre-defined array:
cmdnames[]
- User-defined commands are executed via
do_ucmd()
| `-> do_source (parse every line in file as an EX command)
functions for Ex command line for the +eval feature.
Seems to contains a few control structures, function prototype void ex_KEYWORD(exarg_T *eap)
These commands often adjust of exarg_T.cstack (struct condstack)
, all conditional branches are on a stack. The maximum stacksize is 50 deep.
- try/catch/finally:
- throw: throw (do_throw called by ex_throw)
- if/else: if, else
- while/for: while
- endwhile/endfor: endwhile
- continue: continue
- break: break
Depending on what's #define
'd, it declares a series of enums or an array of structs with builtin commands.
A couple of very important functions are defined here:
do_one_command
: execute one EX command
-
- skip comment lines and leading space
-
- handle command modifiers
-
- parse range
-
- parse command
-
- parse arguments
-
- switch on command name
- lots of ex commands:
- ex_command (:command)
- ex_tabclose (:tabclose)
- ex_cd (:cd, :chdir, ...)
- do_sub (:s/.../.../) ...
What's the difference between:
- builtin functions, f_*, e.g.: f_function)
- ex commands, ex_*, e.g.: ex_echo, ex_execute, ex_return
- do commands, do_*, e.g.: do_ascii (:ascii), do_bang (only caller of do_filter) -> it appears "do" is mostly synonymous to "ex" in this case (cruft)
IBM Vimscript guide: Note, however, that, unlike C or Perl, Vimscript does not allow you to throw away the return value of a function without using it. So, if you intend to use the function as a procedure or subroutine and ignore its return value, you must prefix the invocation with the call command. Otherwise, Vimscript will assume that the function call is actually a built-in Vim command and will most likely complain that no such command exists. We'll look at the difference between functions and commands in a future article in this series.
Line ranges:
You can invoke any standard Vim command—including call—with a preliminary line range, which causes the command to be repeated once for every line in the range:
"Delete every line from the current line (.) to the end-of-file ($)... :.,$delete
"Replace "foo" with "bar" everywhere in lines 1 to 10 :1,10s/foo/bar/
And there's a crazy special-special case:
Internalizing function line ranges This call-the-function-repeatedly-for-each-line behavior is a convenient default. However, sometimes you might prefer to specify a range but then have the function called only once, and then handle the range semantics within the function itself. That's also easy in Vimscript. You simply append a special modifier (range) to the function declaration:
function DeAmperfyAll() range"Step through each line in the range...
for linenum in range(a:firstline, a:lastline)
...
then the function is invoked only once, and two special arguments, a:firstline and a:lastline, are set to the first and last line numbers in the range. If no range is specified, both a:firstline and a:lastline are set to the current line number.
How about '|'?
Some commands handle '|' themselves! (mentioned in ex_docmd.c:1970)