{"id":20483105,"url":"https://github.com/rsrchboy/vim-ducttape","last_synced_at":"2025-04-13T14:34:29.115Z","repository":{"id":136214286,"uuid":"108334011","full_name":"rsrchboy/vim-ducttape","owner":"rsrchboy","description":"Perl and VimL: Perfect Together","archived":false,"fork":false,"pushed_at":"2022-10-27T18:33:45.000Z","size":472,"stargazers_count":4,"open_issues_count":1,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-27T05:34:24.033Z","etag":null,"topics":["embedded-perl","perl","viml-library"],"latest_commit_sha":null,"homepage":"","language":"Perl","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rsrchboy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2017-10-25T22:41:21.000Z","updated_at":"2022-12-06T11:29:48.000Z","dependencies_parsed_at":null,"dependency_job_id":"9841d415-a40b-441a-85c9-c110fb8f9d56","html_url":"https://github.com/rsrchboy/vim-ducttape","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rsrchboy%2Fvim-ducttape","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rsrchboy%2Fvim-ducttape/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rsrchboy%2Fvim-ducttape/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rsrchboy%2Fvim-ducttape/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rsrchboy","download_url":"https://codeload.github.com/rsrchboy/vim-ducttape/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248728678,"owners_count":21152271,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["embedded-perl","perl","viml-library"],"created_at":"2024-11-15T16:16:09.888Z","updated_at":"2025-04-13T14:34:29.106Z","avatar_url":"https://github.com/rsrchboy.png","language":"Perl","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![pipeline status](https://gitlab.com/rsrchboy/vim-ducttape/badges/master/pipeline.svg)](https://gitlab.com/rsrchboy/vim-ducttape/commits/master)\n\n# Vim and Perl: Perfect together\n\n\u003c!-- vim-markdown-toc GFM --\u003e\n\n* [Configuration](#configuration)\n    * [`g:ducttape_loaded`](#gducttape_loaded)\n    * [`g:ducttape_locallib`](#gducttape_locallib)\n    * [`g:ducttape_real_locallib`](#gducttape_real_locallib)\n    * [`g:ducttape_cpanm`](#gducttape_cpanm)\n* [Vim Functions](#vim-functions)\n    * [`ducttape#symbiont#autoload()`](#ducttapesymbiontautoload)\n    * [`ducttape#has(module)`](#ducttapehasmodule)\n    * [`ducttape#require(module)`](#ducttaperequiremodule)\n    * [`ducttape#use(module)`](#ducttapeusemodule)\n* [\"vim-bits from Perl\"](#vim-bits-from-perl)\n    * [`VIMx`](#vimx)\n        * [Export Groups](#export-groups)\n        * [`%b`, `%g`, `%a`, etc](#b-g-a-etc)\n        * [`%self`](#self)\n        * [`$BUFFER`](#buffer)\n        * [`%BUFFERS`](#buffers)\n        * [`$TAB`](#tab)\n        * [`@TABS`](#tabs)\n    * [`VIMx::Tie::Buffer`](#vimxtiebuffer)\n    * [`VIMx::Tie::Buffers`](#vimxtiebuffers)\n        * [FETCH](#fetch)\n        * [KEYS](#keys)\n        * [VALUES](#values)\n    * [`VIMx::Tie::Dict`](#vimxtiedict)\n    * [`VIMx::Tie::List`](#vimxtielist)\n    * [`VIMx::Symbiont`](#vimxsymbiont)\n    * [`VIMx::Out`](#vimxout)\n* [Essential CPAN packages](#essential-cpan-packages)\n* [Requirements](#requirements)\n    * [Vim Requirements](#vim-requirements)\n    * [Perl Requirements](#perl-requirements)\n* [Why \"ducttape\"?](#why-ducttape)\n* [COPYRIGHT AND LICENSE](#copyright-and-license)\n\n\u003c!-- vim-markdown-toc --\u003e\n\n`ducttape` is a VimL library to assist one in using Perl in vim; that is, not\nto help you with *writing* Perl in vim, but rather *using* vim's embedded Perl\n(`+perl`).\n\nThere are two main efforts here; the first to make loading Perl functions and\ngenerating \"glue\" viml functions trivial, the second to make interacting with\nvim-bits from Perl easier.\n\nYou may find the test suite (available under `vader/`) helpful, for examples\nand demonstrations of expected behaviour.\n\n# Codebase Notes\n\nThis project currently lives on GitHub, but CI is done via GitLab.\n\nhttps://gitlab.com/rsrchboy/vim-ducttape/pipelines?scope=branches\n\n# Configuration\n\nGenerally speaking, most of these take effect only when set prior to plugin\ninitialization.\n\n## `g:ducttape_loaded`\n\nIf this variable exists, the plugin will not be initialized.\n\nYou probably don't want to mess with this.\n\n## `g:ducttape_locallib`\n\n_Default_: `\u003cplugin directory\u003e/perl5`\n\n`ducttape` maintains a [local::lib](https://metacpan.org/pod/local::lib) for\nanything we install locally.  Usually this is in the `perl5` directory at the\ntop level of the plugin.\n\nYou probably don't want to mess with this.\n\n## `g:ducttape_real_locallib`\n\n_Default:_ 0\n\nDid I say we maintain a `local::lib`?  What I really meant was we fake it by\nmanipulating `@INC` in the same manner.  This allows `cpanm` to install (and\nus to use) the modules we need, without forcing everything we spawn to use our\n`local::lib` settings.\n\nIf you want a real `local::lib`, with all that implies, set this to 1.\n\nYou probably don't want to mess with this.\n\n## `g:ducttape_cpanm`\n\n_Default_: `\u003cplugin directory\u003e/cpanm`\n\nWe include copy of the `cpanm` executable to enable the installation of\nadditional CPAN packages to our local lib.  It's fatpacked, so won't require\nany additional dependencies on your system.\n\n# Vim Functions\n\n## `ducttape#symbiont#autoload()`\n\nThis is relatively straight-forward.  `VIMx::Symbiont` takes a package and\nfunctions, and generates viml that can be sourced to create glue functions.\n\nFirst, decide what you want the vim namespace of the glue functions to be and\ncreate a minimal autoload file; e.g. if you want your functions to live under\n`foo#bar`, create `autoload/foo/bar.vim` as:\n\n```vim\n\" For those following along at home:  the reason we can get away with\n\" autoloaded functions is that we execute the VimL that creates them from\n\" inside this script/file.  That's enough to convince vim that these functions\n\" belong in that namespace -- or perhaps just that we're determined so it may\n\" as well get out of the way.\n\nfor s:eval in ducttape#symbiont#autoload(expand('\u003csfile\u003e'))\n    execute s:eval\nendfor\n```\n\nThen, write your Perl as `autoload/foo/bar.pm`:\n\n```perl\npackage VIMx::autoload::foo::bar;\nuse strict;\nuse warnings;\nuse VIMx::Symbiont;\n\n# glue: foo#bar#thing(...) abort\nfunction thing =\u003e sub { ... };\n\n# glue: foo#bar#hello(name) abort\nfunction args =\u003e 'name', hello =\u003e sub {\n  return \"Hello, $a{name}!\";\n};\n\n# glue: foo#bar#coolness(factor, ...) abort\nfunction args =\u003e 'factor, ...', coolness =\u003e sub {\n  my $pony = shift // 'Rainbow_Dash';\n\n  $g{$pony} *= $a{factor};\n  return;\n};\n\n# glue: foo#bar#line5() abort\nfunction args =\u003e q{}, line5 =\u003e sub { print $BUFFER-\u003e[4]; return };\n```\n\nWhen calling the glue functions, any non-named parameters end up in `@_`, as\none might expect, while named parameters end up in the tied hash `%a`.\nAnything `return` works as you'd expect, including complex returns (e.g. a\nlist, hashref, etc, will be translated.)\n\nThe current buffer can be accessed via `$BUFFER`; all buffers can be accessed\nvia `%BUFFERS`.  Similarly, `%g` will get you `g:`, `%t` gets you `t:`, etc.\n\n## `ducttape#has(module)`\n\nGiven a module, returns true if the module is available; false otherwise.\n\n## `ducttape#require(module)`\n\nGiven a module, `require`'s the module.  Essentially equivalent to:\n\n  require Some::Module;\n\n## `ducttape#use(module)`\n\nGiven a module, `use`'s the module.  Essentially equivalent to:\n\n  use Some::Module;\n\nNote: any exports the module's `inport()` function performs will end up in the\n`main` namespace.\n\n# \"vim-bits from Perl\"\n\n## `VIMx`\n\nThe `VIMx` package exports a number of useful routines and variables designed\nto make interfacing with Vim easier.  You can choose to not export any (or\nall) of them and access them via their fully-qualified names (e.g.\n`%VIMx::b`).\n\nWhere exported by default, we'll refer to variables by their unqualified name.\n\n### Export Groups\n\n`VIMx` recognizes the following export groups:\n\n* `:variables`\n  * `%a %b %g %l %s %t %v %w %self`\n* `:buffers`\n  * `$BUFFER %BUFFERS`\n* `:options`\n  * `%GOPTIONS %LOPTIONS %OPTIONS`\n* `:tabs`\n  * `$TAB @TABS`\n\n### `%b`, `%g`, `%a`, etc\n\n`%b`, `%g`, `%a`, etc, are provided to access `b:`, `g:`, `a:`, etc.  Complex\ndata structures are supported on both sides; e.g.:\n\n```perl\n$b{eep} = [ 1, 2, { three =\u003e 3 } ];\n```\n\n...equates to:\n\n```vim\nlet b:eep = [ 1, 2, { 'three': 3 } ]\n```\n\n...and the other way around.\n\n### `%self`\n\nAdditionally, `%self` is provided for use in dict functions; this corresponds\nto `l:self`. See `VIMx` for more information.\n\n### `$BUFFER`\n\nThe current buffer can be accessed via `$VIMx::BUFFER`, a blessed reference to a\ntied variable.  The contents of the buffer can be read or modified by\naccessing the underlying array:\n\n```perl\nmy $len = @$VIMx::BUFFER;\nmy $first_line = $VIMx::BUFFER-\u003e[0];\nmy $line5 = delete $VIMx::BUFFER-\u003e[4];\npush @$VIMx::BUFFER, 'remember gems for spike';\n```\n\nNote that Vim's functions (e.g. `VIM::Buffer(...)-\u003eGet(10)`) are 1-based,\nwhile our array is the expected 0-based.\n\nYou can also call any method you can call on a `VIBUF`:\n\n```perl\nmy $name = $VIMx::BUFFER-\u003eName;\n## ...etc.  see `:h perl` for more\n```\n\nYou can delete lines by using the Perl built-in `delete`, a la:\n\n```perl\nmy $line5 = delete $VIMx::BUFFER-\u003e[4];\n```\n\n`$VIMx::BUFFER` stringifies to its name in string context, and to its number\nin numeric context.\n\n[`VIMx::Tie::Buffer`](#vimxtiebuffer)\n\n### `%BUFFERS`\n\nSimilarly, all buffers can be accessed via the `%VIMx::BUFFERS` hash.  The\nkeys are the names of the buffers, while the values are a reference tied and\nblessed in the same way as the current buffer variable (`$VIMx::BUFFER`).\n\nThis hash behaves in the expected fashion, e.g.\n\n```perl\nsay 'it exists!'\n    if exists $VIMx::BUFFERS{'hippograph-relations.txt'};\nsay \"buf: $_\"\n    for keys %VIMx::BUFFERS;\n```\n\n### `$TAB`\n\n### `@TABS`\n\n## `VIMx::Tie::Buffer`\n\nTies an array to a `VIBUF`, allowing the buffer contents to be accessed\nthrough the tied array.  It also contains additional \"methods\" that the\nblessed references in `$VIMx::BUFFER` and `%VIMx::BUFFERS` will look for if\ninvoked on those references; e.g.\n\n```perl\nsay $BUFFER-\u003eName; # AKA $curbuf-\u003eName\n```\n\nThe references also support overloading, e.g.\n```perl\nsay \"$BUFFER\"; # AKA $curbuf-\u003eName\nsay 0+$BUFFER; # AKA $curbuf-\u003eNumber\n```\n\n## `VIMx::Tie::Buffers`\n\nThe magic behind `%VIMx::BUFFERS`, this is a `Tie::Hash`.\n\nThis tie allows one to access all buffers known to vim: listed, unlisted, etc,\netc.  Use a map if you need to narrow something down:\n\n```perl\nmy @listed = grep { $_-\u003eoptions-\u003e{buflisted} } values %BUFFERS;\n```\n\nIt's unlikely you'll ever need to tinker with this directly, as\n`%VIMx::BUFFERS` is already tied to it.\n\nHere's how the different hash operations are implemented; not all of them are,\ne.g. trying to clear the hash (e.g. `%BUFFERS = ()`) is unimplemented, as is\ncreating a buffer (currently).\n\n### FETCH\n\nReturns a reference to a blessed/tied `VIMx::Tie::Buffer`; `undef` if a\nbuffer with a matching name/number is not found.\n\n```perl\n# fetches buffer number 42\nmy $bufA = $BUFFERS{42};\n# fetches buffer named meaning.txt\nmy $also_bufA = $buffers{'meaning.txt'};\n# fetches buffer _named_ 42\nmy $bufB = $buffers{'42'};\n```\n\n### KEYS\n\nWhile all buffer names are returned, we don't also return the buffer number.\n(If you're looking for a specific number, just fetch it directly.)\n\n```perl\n# I know, terribly, shockingly surprising\nmy @names = keys %BUFFERS;\n```\n\n### VALUES\n\nJust as expected, all the buffers.\n\n```perl\n# again, shocking\nmy @bufs = values %BUFFERS;\n```\n\n## `VIMx::Tie::Dict`\n\nThis package allows one to create tied hashes that access vim Dictionaries\ntransparently.  e.g., you could access global variables by:\n\n```perl\ntie our %g, 'VIMx::Tie::Dict', 'g:';\nprint \"$g{some_string_variable}\"; # g:some_string_variable\n\n# or even something like\ntie my %self, 'VIMx::Tie::Dict', 'l:self';\n```\n\nDicts and Lists are mapped to hashrefs and arrayrefs automatically, so the\nfollowing will work:\n\n```perl\n$g{a_dict} = { 'rainbow dash' =\u003e '120%' };\nmy $coolness = $g{a_dict}; # hashref\n```\n\n## `VIMx::Tie::List`\n\nAs with `VIMx::Tie::Dict`, but for Lists.\n\n## `VIMx::Symbiont`\n\nThis package starts tying things together.  When used, it exports a number of\nthings, e.g. `%g`, `%b`, `%s`, etc, tied to `g:`, `b:`, `s:`, etc.  It also\nexports a `%self` tied to `l:self` for use in `dict` functions.\n\n`function()` is also exported: this takes a coderef, wraps it appropriately,\ninstalls it, then generates a glue viml function.\n\n...\n\n## `VIMx::Out`\n\nIf you're using a version of vim older than 7.4.1729, writing to `STDOUT` or\n`STDERR` will fail silently.  We work around that by playing with the\nfile-handles until they use `VIM::Msg()` and company.  (Yes, this is probably\nnot the best way to do it, but as people upgrade it'll be moot anyways.)\n\n# Essential CPAN packages\n\nWe include a minimal subset of CPAN packages to assist us in our efforts\nwhile keeping things as simple as possible, mainly `*::Tiny` packages I'd\nreally rather not reimplement here.  They're pulled in as submodules, and\ntheir source repositories should be viewed to learn more about those packages,\nincluding their authors, maintainers, and licenses.\n\n* [Data::Section::Simple](https://metacpan.org/pod/Data::Section::Simple)\n* [HTTP::Tiny](https://metacpan.org/pod/HTTP::Tiny)\n* [JSON::Tiny](https://metacpan.org/pod/JSON::Tiny)\n* [Module::Runtime](https://metacpan.org/pod/Module::Runtime)\n* [Path::Tiny](https://metacpan.org/pod/Path::Tiny)\n* [Role::Tiny](https://metacpan.org/pod/Role::Tiny)\n* [Template::Tiny](https://metacpan.org/pod/Template::Tiny) (*in `lib/`)\n* [Try::Tiny](https://metacpan.org/pod/Try::Tiny)\n\n`JSON::Tiny` in particular is key, as we lean heavily on vim's `json_encode()`\nand `json_decode()` to make bits like `VIMx::Tie::Dict` and\n`VIMx::Symbiont::function()` work.\n\n# Requirements\n\n`vim` compiled with (`+perl`) Perl (v5.10+) support.\n\n[vim v7.4.2273](https://github.com/vim/vim/tree/v7.4.2273) for full\nfunctionality, v7.4.2204 for everything except the ability to get and\nset buffer-local options of non-current buffers, v7.4.1304 for the above\nexcept the buffer/win/tab `info()` methods.\n\n\n## Vim Requirements\n\nYour vim must be compiled with Perl support.  (Just in case that wasn't 120%\nclear.)  Past that, it needs to be compiled against at least Perl v5.10.\n\nIf the vim you're using lacks `json_encode()` and `json_decode()` this isn't\ngoing to work -- we make extensive use of those functions when passing data\nback and forth from Perl-space to VIM-space.\n\nHere's a list of the newer VimL bits we're using. ...that I'm aware of, at any\nrate.\n\n* [v7.4.1304](https://github.com/vim/vim/tree/v7.4.1304) The\n    `json_encode()` and decode functions (introduced as `jsonencode()` /\n    `jsondecode()` in [v7.4.1154](https://github.com/vim/vim/tree/v7.4.1154))\n* [v7.4.2204](https://github.com/vim/vim/tree/v7.4.2204) introduces\n    `get{buf,tab,win}info()`.\n* [v7.4.2273](https://github.com/vim/vim/tree/v7.4.2273) provides for\n    buffer-local option reading/setting.\n\nHonorable mentions go to:\n\n* [v8.0.0654](https://github.com/vim/vim/tree/v8.0.0654), changes to how\n    `:endfunction` is handled.  This would allow us to consolidate certain\n    `:execute` calls, but is too new for most vim installations.\n* [v7.4.1729](https://github.com/vim/vim/tree/v7.4.1729) allows a\n    `print()`/etc from Perl to actually work.  We work around this for older\n    vim.\n* [v7.4.1125](https://github.com/vim/vim/tree/v7.4.1125) gives us `perleval()`, which we do not currently use.\n\n## Perl Requirements\n\nPerl v5.10+.\n\nWe do not depend on anything past core modules and those included here as\nsubmodules.\n\n# Why \"ducttape\"?\n\n![Honestly, we just hacked it all together](https://imgs.xkcd.com/comics/lisp.jpg)\n\n\n# COPYRIGHT AND LICENSE\n\nThis software is Copyright (c) 2017, 2018 by Chris Weyl\n\u003ccweyl@alumni.drew.edu\u003e.\n\nThis is free software, licensed under:\n\n    The GNU Lesser General Public License, Version 2.1, February 1999\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frsrchboy%2Fvim-ducttape","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frsrchboy%2Fvim-ducttape","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frsrchboy%2Fvim-ducttape/lists"}