From b75634e55ee4cdfee7917b29f39e3ca1307cb059 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 16 Apr 2023 07:50:18 +0800 Subject: vim-patch:9.0.0370: cleaning up afterwards can make a function messy Problem: Cleaning up afterwards can make a function messy. Solution: Add the :defer command. https://github.com/vim/vim/commit/1d84f7608f1e41dad03b8cc7925895437775f7c0 Omit EX_EXPR_ARG: Vim9 script only. Make :def throw E319 to avoid confusing behavior. Co-authored-by: Bram Moolenaar --- runtime/doc/userfunc.txt | 59 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/doc/userfunc.txt b/runtime/doc/userfunc.txt index ce6f2fc2e9..2c72f3d584 100644 --- a/runtime/doc/userfunc.txt +++ b/runtime/doc/userfunc.txt @@ -350,10 +350,67 @@ A function can also be called as part of evaluating an expression or when it is used as a method: > let x = GetList() let y = GetList()->Filter() +< +============================================================================== +3. Cleaning up in a function ~ + *:defer* +:defer {func}({args}) Call {func} when the current function is done. + {args} are evaluated here. + +Quite often a command in a function has a global effect, which must be undone +when the function finishes. Handling this in all kinds of situations can be a +hassle. Especially when an unexpected error is encountered. This can be done +with `try` / `finally` blocks, but this gets complicated when there is more +than one. + +A much simpler solution is using `defer`. It schedules a function call when +the function is returning, no matter if there is an error. Example: > + func Filter(text) abort + call writefile(a:text, 'Tempfile') + call system('filter < Tempfile > Outfile') + call Handle('Outfile') + call delete('Tempfile') + call delete('Outfile') + endfunc + +Here 'Tempfile' and 'Outfile' will not be deleted if something causes the +function to abort. `:defer` can be used to avoid that: > + func Filter(text) abort + call writefile(a:text, 'Tempfile') + defer delete('Tempfile') + defer delete('Outfile') + call system('filter < Tempfile > Outfile') + call Handle('Outfile') + endfunc + +Note that deleting "Outfile" is scheduled before calling `system()`, since it +can be created even when `system()` fails. + +The deferred functions are called in reverse order, the last one added is +executed first. A useless example: > + func Useless() abort + for s in range(3) + defer execute('echomsg "number ' .. s .. '"') + endfor + endfunc + +Now `:messages` shows: + number 2 + number 1 + number 0 + +Any return value of the deferred function is discarded. The function cannot +be followed by anything, such as "->func" or ".member". Currently `:defer +GetArg()->TheFunc()` does not work, it may work in a later version. + +Errors are reported but do not cause aborting execution of deferred functions. + +No range is accepted. ============================================================================== -3. Automatically loading functions ~ + +4. Automatically loading functions ~ *autoload-functions* When using many or large functions, it's possible to automatically define them only when they are used. There are two methods: with an autocommand and with -- cgit From 0167649ce4071e60d985b65f3f9408ffb21cb58c Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 16 Apr 2023 11:07:48 +0800 Subject: vim-patch:9.0.0379: cleaning up after writefile() is a hassle Problem: Cleaning up after writefile() is a hassle. Solution: Add the 'D' flag to defer deleting the written file. Very useful in tests. https://github.com/vim/vim/commit/806a273f3c84ecd475913d901890bb1929be9a0a Co-authored-by: Bram Moolenaar --- runtime/doc/builtin.txt | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index cb05718ab9..adb3164429 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -9507,31 +9507,43 @@ writefile({object}, {fname} [, {flags}]) When {object} is a |List| write it to file {fname}. Each list item is separated with a NL. Each list item must be a String or Number. - When {flags} contains "b" then binary mode is used: There will - not be a NL after the last list item. An empty item at the - end does cause the last line in the file to end in a NL. + All NL characters are replaced with a NUL character. + Inserting CR characters needs to be done before passing {list} + to writefile(). When {object} is a |Blob| write the bytes to file {fname} - unmodified. + unmodified, also when binary mode is not specified. - When {flags} contains "a" then append mode is used, lines are - appended to the file: > + {flags} must be a String. These characters are recognized: + + 'b' Binary mode is used: There will not be a NL after the + last list item. An empty item at the end does cause the + last line in the file to end in a NL. + + 'a' Append mode is used, lines are appended to the file: > :call writefile(["foo"], "event.log", "a") :call writefile(["bar"], "event.log", "a") < - When {flags} contains "S" fsync() call is not used, with "s" - it is used, 'fsync' option applies by default. No fsync() - means that writefile() will finish faster, but writes may be - left in OS buffers and not yet written to disk. Such changes - will disappear if system crashes before OS does writing. + 'D' Delete the file when the current function ends. This + works like: > + :defer delete({fname}) +< Fails when not in a function. Also see |:defer|. + + 's' fsync() is called after writing the file. This flushes + the file to disk, if possible. This takes more time but + avoids losing the file if the system crashes. + + 'S' fsync() is not called, even when 'fsync' is set. + + When {flags} does not contain "S" or "s" then fsync() is + called if the 'fsync' option is set. - All NL characters are replaced with a NUL character. - Inserting CR characters needs to be done before passing {list} - to writefile(). An existing file is overwritten, if possible. + When the write fails -1 is returned, otherwise 0. There is an error message if the file can't be created or when writing fails. + Also see |readfile()|. To copy a file byte for byte: > :let fl = readfile("foo", "b") -- cgit From 335bef0c211dc962499814d248670ff17758a642 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 16 Apr 2023 10:51:27 +0800 Subject: vim-patch:9.0.0390: cannot use a partial with :defer Problem: Cannot use a partial with :defer. Solution: Add the partial arguments before the other arguments. Disallow using a dictionary. https://github.com/vim/vim/commit/86d87256c4005c6215da5af2597fbf6f6304421f Co-authored-by: Bram Moolenaar --- runtime/doc/userfunc.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/doc/userfunc.txt b/runtime/doc/userfunc.txt index 2c72f3d584..db0127df95 100644 --- a/runtime/doc/userfunc.txt +++ b/runtime/doc/userfunc.txt @@ -406,7 +406,8 @@ GetArg()->TheFunc()` does not work, it may work in a later version. Errors are reported but do not cause aborting execution of deferred functions. -No range is accepted. +No range is accepted. The function can be a partial with extra arguments, but +not with a dictionary. *E1300* ============================================================================== -- cgit From f39b33ee491a4a8d4b08425e582dd0dd53617edf Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 16 Apr 2023 11:46:17 +0800 Subject: vim-patch:9.0.0411: only created files can be cleaned up with one call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Only created files can be cleaned up with one call. Solution: Add flags to mkdir() to delete with a deferred function. Expand the writefile() name to a full path to handle changing directory. https://github.com/vim/vim/commit/6f14da15ac900589f2f413d77898b9bff3b31ece vim-patch:8.2.3742: dec mouse test fails without gnome terminfo entry Problem: Dec mouse test fails without gnome terminfo entry. Solution: Check if there is a gnome entry. Also fix 'acd' test on MS-Windows. (Dominique Pellé, closes vim/vim#9282) https://github.com/vim/vim/commit/f589fd3e1047cdf90566b68aaf9a13389e54d26a Cherry-pick test_autochdir.vim changes from patch 9.0.0313. Cherry-pick test_autocmd.vim changes from patch 9.0.0323. Co-authored-by: Bram Moolenaar --- runtime/doc/builtin.txt | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index adb3164429..15ccfd9b92 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -5572,8 +5572,24 @@ mkdir({name} [, {flags} [, {prot}]]) When {flags} is present it must be a string. An empty string has no effect. - If {flags} is "p" then intermediate directories are created as - necessary. + If {flags} contains "p" then intermediate directories are + created as necessary. + + If {flags} contains "D" then {name} is deleted at the end of + the current function, as with: > + defer delete({name}, 'd') +< + If {flags} contains "R" then {name} is deleted recursively at + the end of the current function, as with: > + defer delete({name}, 'rf') +< Note that when {name} has more than one part and "p" is used + some directories may already exist. Only the first one that + is created and what it contains is scheduled to be deleted. + E.g. when using: > + call mkdir('subdir/tmp/autoload', 'pR') +< and "subdir" already exists then "subdir/tmp" will be + scheduled for deletion, like with: > + defer delete('subdir/tmp', 'rf') If {prot} is given it is used to set the protection bits of the new directory. The default is 0o755 (rwxr-xr-x: r/w for -- cgit