{"id":20785303,"url":"https://github.com/coachshea/neo-pipe","last_synced_at":"2025-05-05T14:43:02.907Z","repository":{"id":54400144,"uuid":"120434952","full_name":"coachshea/neo-pipe","owner":"coachshea","description":"Pipe text through external commands and display the output in a scratch buffer","archived":false,"fork":false,"pushed_at":"2021-02-20T12:47:43.000Z","size":74,"stargazers_count":14,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-30T21:51:16.550Z","etag":null,"topics":["neovim","plugin","repl","vim"],"latest_commit_sha":null,"homepage":"","language":"Vim script","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/coachshea.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}},"created_at":"2018-02-06T09:55:39.000Z","updated_at":"2024-10-23T11:54:42.000Z","dependencies_parsed_at":"2022-08-13T14:31:01.237Z","dependency_job_id":null,"html_url":"https://github.com/coachshea/neo-pipe","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/coachshea%2Fneo-pipe","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coachshea%2Fneo-pipe/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coachshea%2Fneo-pipe/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coachshea%2Fneo-pipe/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/coachshea","download_url":"https://codeload.github.com/coachshea/neo-pipe/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252516279,"owners_count":21760749,"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":["neovim","plugin","repl","vim"],"created_at":"2024-11-17T14:44:41.445Z","updated_at":"2025-05-05T14:43:02.878Z","avatar_url":"https://github.com/coachshea.png","language":"Vim script","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n              #     # ####### ####### ######  ### ######  ####### \n              ##    # #       #     # #     #  #  #     # #       \n              # #   # #       #     # #     #  #  #     # #       \n              #  #  # #####   #     # ######   #  ######  #####   \n              #   # # #       #     # #        #  #       #       \n              #    ## #       #     # #        #  #       #       \n              #     # ####### ####### #       ### #       ####### \n                                                                  \nTable of Contents\n=================\n\n\u003c!-- vim-markdown-toc GFM --\u003e\n\n* [Introduction](#introduction)\n* [Dependencies](#dependencies)\n* [Setup](#setup)\n  * [npipe\\_com](#npipe_com)\n  * [npipe\\_type](#npipe_type)\n  * [npipe\\_append](#npipe_append)\n  * [npipe\\_ft](#npipe_ft)\n  * [npipe\\_split](#npipe_split)\n  * [npipe\\_sep](#npipe_sep)\n* [Projections](#projections)\n* [Commands](#commands)\n* [Mappings](#mappings)\n  * [Default Mappings](#default-mappings)\n  * [Mapping Repeatability](#mapping-repeatability)\n\n\u003c!-- vim-markdown-toc --\u003e\n\n[projectionist]: https://github.com/tpope/vim-projectionist\n[npipe_com]: #npipe_com\n[npipe_type]: #npipe_type\n[npipe_ft]: #npipe_ft\n[mappings]: #mappings\n[projections]: #projections\n[textobj-user]: https://github.com/kana/vim-textobj-user\n[textobj-entire]: https://github.com/kana/vim-textobj-entire\n[related plugins]: https://github.com/kana/vim-textobj-user/wiki\n\nIntroduction\n============\n\nNeoPipe allows users to send text through an external command and display the\noutput in the output buffer. The ':NeoPipe' command takes a range of text (the\nenter file by default) and pipes it through a user-defined command. NeoPipe\ndefines mappings which can take an operator, work on a visual selection, or\nwork on a line. This allows users to work in an ordinary vim buffer and use it\nas a repl, make changes to a database, etc.\n\nDependencies\n============\n\nNeoPipe has no dependencies, but can work with tpope's [projectionist] plugin.\nIf you are not familiar with [projectionist], I strongly encourage you to\ncheck it out. It excels at project-level configuration.\n\nNeoPipe also provides an operator-pending mapping (see [mappings] sections)\nwhich works well with [textobj-user] by kana and the [related plugins]. If\nyou are not yet familiar with these plugins, they are worth your time to check\nthem out.\n\nSetup\n=====\n\nNeopipe allows users to interact with their commands in one of four ways.\nFirst, users can define a long running command through which all subsequent\ntext will be piped. This can be as simple as opening a shell or it could open\na database, a repl, or any other command that the user wants to keep running\nfor the duration of the session. NeoPipe actually provides two ways to work\nwith long-running commands (see the section [npipe\\_type](#npipe_type) for\ndetails). One uses `jobstart()` behinds the scenes and one uses `termopen()`.\n\nThe second method available to NeoPipe users is to define a command that takes\neach batch of text through it's stdin and which rights it's output to stdout.\nIn this case, NeoPipe uses the `system()` command on each invocation.\n\nThe third options is to echo the selected lines in the output buffer. This\nfeature could come in handy if a user was testing a user-defined motion or\ntext object and wanted to make sure that the plugin selected the correct text.\n\nWhichever method the user chooses, the NeoPipe sends the output of the command\nto the output buffer. Users posses the ability to define the filetype, split,\nheight/width, etc. of the output buffer.\n\nUsers have the ability to set options at the buffer, projection (see\n[projectionist] by Tim Pope and the [projections] section of this document),\nor global level. NeoPipe will search for the options in that order and apply\nthe first one that it finds. The examples that follow demonstrate setting\neach option at the buffer and global level. For examples of setting options\nin projections, see the [projections] section of this document. The following\nare the available options that collectively determine the behavior of NeoPipe.\nTechnically, user do not need to set any of these options, but if none are\nset, the text is simply echoed to the output buffer that will be opened in a\nvertical split (evenly split) buffer with no filetype.\n\n**Important Note**\n\nOnce NeoPipe finds a variable at any level, that variable is set at the window\nlevel. This is important because it allows us to work with different buffers\nand pipe the commands to a single output buffer. Let's say for example, that I\nam working on a sql project and call ':NeoPipe'. Let's further say that this\ncommand opens a sql database in the background for use with all subsequent\ncommands. I send a few commands to ':NeoPipe', then I switch to a new buffer\nin the same window next to the output buffer and execute another ':NeoPipe'.\nIt would be jarring if another output buffer opened and a separate instance\nof sql (sqlite, mysql, etc.) was started. What I would most likely want is\nto send commands to the existing sql instance and see the output in the\nexisting output buffer. If, at any time, I desire to start fresh, I simply\ncall ':NeoPipeClose' and when I next call ':NeoPipe', I will be starting\nfresh. If I need to keep two (or more) separate 'command buffer / output\nbuffer' combinations going at once, I can open them in separate tabs.\n\nnpipe\\_com\n----------\n\nThe `npipe_com` command is the actual command through which the text will be\nsent, either continuously running or through a series of one-offs.\n\n```vim\nlet g:npipe_com = 'zsh'\nau filetype sql let b:npipe_com = 'sqlite3 ~/db/myDatabase.db'\nau fileytpe mongo let b:npipe_com = 'mongo'\n\n\" with b:npipe_type='s'\nau fileytpe livescript let b:npipe_com = 'lsc -cb'\n```\n\nnpipe\\_type\n-----------\n\nNeoPipe's most fundamental option. This options tells NeoPipe how to interact\nwith `npipe_com`. The 'c' option, which stands for \"continuous\" is one of two\ncommands in the continuous family. Behind the scenes this option tells NeoPipe\nto use nvim's `jobstart()` function to open a long running command and to\nthen pipe all subsequent text through this command and display the results to\nthe output buffer. The second option, which is also a continuously running\ncommand is the 't' option, which stands for \"terminal\". The options instructs\nNeoPipe to use the `termopen()` function. This is similar too the 'c' option\nbut the output buffer will simply be a terminal. NeoPipe user can still set\nthe [npipe\\_split](#npipe_split) option, but the [npipe\\_ft](#npipe_ft),\n[npipe\\_append](#npipe_append), and [npipe\\_sep](#npipe\\_sep) options are\nignored. Some repl's work better with this setting (`pry` for ruby developers\nis one program known to work better this way).\n\nThe 's' setting, which stands for \"single\", leads NeoPipe to send each batch\nof text through the command separately. In other words, each call to NeoPipe\nis a \"single\" invocation of the command.\n\nIf `npipe_type` is not set, NeoPipe will simply echo the text in the scratch\nbuffer. In this situation, any value set on `npipe_com` will be ignored. It\nis certainly fine to leave `npipe_type` unset if echoing is desired, but in\nmost cases NeoPipe's true power comes from the combination of `npipe_com` and\n`npipe_type`.\n\n```vim\n\" run the command continuously\nlet g:npipe_type='c'\n\n\" run command in a terminal buffer\nau filetype ruby let b:npipe_type='t'\n\n\" run the command each time\nau fileytpe coffe let b:npipe_type='s'\n\n\" echo the text in the scratch buffer\nau filetype txt let b:npipe_type=0\n```\n\nnpipe\\_append\n-------------\n\nThis variable informs NeoPipe of whether to clear the buffer for each\ninvocation, or to append each subsequent write of the output buffer. The text\ncan be appended to the top or bottom of the buffer by setting this to 'top' or\n'bottom' respectively. If `npipe_append` in not set, or set to anything other\nthan 'top' or 'bottom', then the buffer is cleared and rewritten on each\ninvocation. If `npipe_type` is set to 't' then this option is ignored.\n\n```vim\nlet g:npipe_type='bottom'\nau filetype mongo let b:npipe_append='top'\nau filetype javascript let b:npipe_append=0\n```\n\n**Important Note**\n\nIf a user always desired to clear the buffer on each invocation, then it is\nsimple enough not to set this variable at all. But remember, all variables are\nsearched at the buffer, then projection, then global levels. If somewhere up\nthe \"food chain\" this variable was set then it would affect all lower level\nbuffers until overwritten by a \"closer\" variable. Therefore, it is often a\ngood idea to be explicit. Because the actual value does not matter except for\nbeing other than 'top' or 'bottom', users can set it to number 0, to an empty\nstring, or to anything that helps them remember the intent (i.e. 'clear',\n'new', 'foobar', etc.)\n\nnpipe\\_ft\n-----------\n\nThe `npipe_ft` command sets the filetype of the output buffer. For example,\nif we are pumping text through a mongodb database, we would likely want the\noutput to have json syntax highlighting. If `npipe_type` is set to 't' then\nthis option is ignored.\n\n```javascript\n{\n  \"name\": \"john\"\n}\n```\n\nnpipe\\_split\n------------\n\nThe `npipe_split` option is `vnew` by default. Meaning, the output buffer\nwill be shown in a vertically and evenly split window. As with all options,\nthis can be set on the buffer, projection or global levels. The option can be\neither 'new', `vnew` (default), or `tabnew`. It's hard to imagine a use case\nfor `tabnew`, but it is available. When `npipe_type` is set to 't', the buffer\nis split before the call to `termopen()`, and therefore, this value is still\nrelevant.\n\n```vim\nlet g:npipe_split = 'new'\nau filetype vim let b:npipe_split = 'vnew'\n```\n\nIt is also possible to specify a height or width by supplying a number to the\nsplit or vsplit (respectively) command. By default Vim splits each window\nequally. Below are examples of more explicit splitting behavior.\n\n```vim\nlet g:npipe_split = '40vnew'\nau filetype coffee let b:npipe_split = '25new'\n```\n\nUser can set other options and commands on the window. Check the Vim\ndocumentation for further details:\n\n```vim\n:h vsplit\n:h split\n:h ++opt\n:h +cmd\n```\n\nnpipe\\_sep\n----------\n\nThe npipe\\_sep option defines an array of lines used to separate each group of\noutput lines in the scratch buffer. Only relevant if `npipe_append` is set to\n`top` or `bottom`. If `npipe_type` is set to 't' then this option is ignored.\n\n```vim\nlet g:npipe_sep=['', '---'] \" default value\nau filetype mongo let b:npipe_sep=[] \" no sep\n```\n\nProjections\n===========\n\nAs mentioned previously, this plugin has been specifically designed to integrate\nsmoothly with the [projectionist] plugin. If you are not familiar with\n[projectionist], I strongly encourage you to give it a look. It allows for a\nsimple and clean method of assigning values on a per-project (or global) basis.\nUsing [projectionist] can save you from a lot of unnecessary autocommands\ndesigned to handle per-project requirements. The following examples could be\nincluded in a '.projections.json' file in the root directory of a project.\n\n```Javascript\n// compile livescript\n\"*.ls\": {\n  \"npipe_com\": \"lsc -cbp\",\n  \"npipe_type\": \"s\",\n  \"npipe_ft\": \"javascript\",\n  \"npipe_split\": \"30new\",\n  \"npipe_append\": \"bottom\"\n}\n\n// work with ruby through pry\n\"*.rb\": {\n  \"npipe_com\": \"pry --no-pager\",\n  \"npipe_type\": \"t\",\n  \"npipe_split\": \"rightbelow split\"\n}\n\n//connect to a mongodb database\n\"*.mongo\": {\n  \"npipe_com\": \"mongo\",\n  \"npipe_type\": \"c\",\n  \"npipe_ft\": \"javascript\",\n  \"npipe_append\": \"top\"\n}\n\n// connect to a sqlite3 db\n\"*.sql\": {\n  \"npipe_com\": \"sqlite3 ~/mydb.db\",\n  \"npipe_type\": \"c\",\n  \"npipe_split\": \"new\",\n  \"npipe_append\": 0\n}\n\n// compile pug to html\n\"templates/*.pug\": {\n  \"npipe_com\": \"pug\",\n  \"npipe_type\": \"s\",\n  \"npipe_ft\": \"html\",\n  \"npipe_append\": 0\n}\n\n// compile pug to javascript\n\"src/*.pug\": {\n  \"npipe_com\": \"pug -c\",\n  \"npipe_type\": \"s\",\n  \"npipe_ft\": \"javascript\",\n  \"npipe_append\": 0\n}\n\n// connect to a mongodb instance with livescript\n\"*.mongo.ls\": {\n  \"npipe_com\": \"lsc -cpb | mongo\",\n  \"npipe_type\": \"s\",\n  \"npipe_ft\": \"javascript\",\n  \"npipe_split\": \"50vnew\",\n  \"npipe_append\": \"bottom\"\n}\n\n// view assembly for c files\n\"*.c\": {\n  \"npipe_com\": \"gcc -S -xc -c -o - -\",\n  \"npipe_type\": \"s\",\n  \"npipe_ft\": \"asm\",\n  \"npipe_append\": \"top\"\n}\n```\n\nCommands\n========\n\nThe most basic command that NeoPipe provides is the ':NeoPipe' command. This\nis the command that sends text from the current buffer to the output buffer.\nIf the output buffer is not yet created, it will automatically create it, and\nif the command type (i.e. `npipe_type`) is 'c' or 't' (i.e. \"continuous\" or\n\"terminal\"), the ':NeoPipe' command will take care of all of that for us. By\ndefault, `:NeoPipe` sends the current line of text through the `npipe_com`\ncommand, but `:NeoPipe` also accepts a range.\n\n```vim\n\" whole file\n:NeoPipe\n\n\" current line\n:.NeoPipe\n\n\" line 52\n:52NeoPipe\n\n\" line range (13-26)\n:13,26NeoPipe\n\n\" current line to bottom of the file\n:.,$NeoPipe\n```\n\nNeoPipe provides two additional commands - `:NeoPipeClear` and\n`:NeoPipeClose`. `:NeoPipeClear`, as the name implies, clears the output\nbuffer. This is only useful if `npipe_append` is one of 'top' or 'bottom',\notherwise it is done on every invocation of `:NeoPipe` anyway. ':NeoPipeClose'\ncloses the output buffer and if `npipe_type` is 'c', it will cancel the\ncommand. Any invocation of `:NeoPipe` following `:NeoPipeClose` will be run\nfrom scratch.\n\n```vim\n\" clear the output buffer\n:NeoPipeClear\n\n\" close the output buffer\n:NeoPipeClose\n```\n\nMappings\n========\n\nNeoPipe privies a 'Plug' mapping which allows users to attach the NeoPipe\ncommand to an operator-pending action. This allows us to very quickly send\narbitrary regions of text to the command.\n\n```vim\n\u003cPlug\u003e(npipe-operator)\n```\n\n\n**Important Note**\n\nNeopipe is inherently a line-based command. Therefore when using an operator\npending action it is important to realize that while character-wise motions are\nacceptable, any line that is touched by the motion or text object will be\nincluded -- in full -- in the command. This is also true for visual mode. You\ncan perform a character-wise visual mode selection, but any line touch by the\nselection is included in full.\n\nDefault Mappings\n----------------\n\nBy default, NeoPipe provides the following convenience mappings:\n\n\n```vim\n\" operator-pending\nnmap ,t \u003cPlug\u003e(npipe-operator)\n\n\" current line\nnmap ,tt \u003cPlug\u003e(npipe-operator)_\n\n\" whole file\nnmap ,tg :NeoPipe\u003ccr\u003e\n\n\" clear output buffer\nnmap ,tc :NeoPipeClear\n\n\" close output buffer\nnmap ,tq :NeoPipeClose\n\n\" visual selection\n\" would actually run as :'\u003c,'\u003eNeoPipe\nvmap ,t :NeoPipe\u003ccr\u003e\n```\n\nIf this is not desired simply include the following in your `init.vim`\n\n```vim\nlet g:neopipe_do_no_mappings=1\n```\n\nMapping Repeatability\n---------------------\n\nThe operator-pending mapping and the 'current line' mapping (as defined above)\nare naturally repeatable, no external plugins are required because they work\nwith Vim's natural motions. The other commands are not. A great deal of thought\nwent into this, but the rationale is as follows: it is perfectly understandable\nthat a user might be working with a pre-existing file and chose to move around\nand send various lines/ranges to the `:NeoPipe` command. However, it is doubtful\nthat a user would want to send an entire file to the command twice without some\nmodifications in between (what would be the point). It is equally doubtful that\nanyone would need to clear or close a buffer twice without intervening events.\nTherefore, the two mappings that have the most (and possibly only) utility for a\nrepeat action are the two that are repeatable.\n\nWorth noting, the operator-pending mode will, of course, work with any text\nobject, including user-defined text-objects. The [textobj-user] and [related\nplugins] provide many useful text-objects combine nicely with NeoPipe's\noperator mapping. If, for example, a user is looking for repeatability\nfor the entire file (still not sure what the use would be), I suggest the\n[textobj-entire] plugin also by kana. There are many other useful user-defined\ntext-objects available within that ecosystem. If you are not familiar with\nit, I encourage you to check out the [textobj-user] plugin and the [related\nplugins] page. Even if not for repeatability, the plugins can be a great\nbenefit.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcoachshea%2Fneo-pipe","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcoachshea%2Fneo-pipe","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcoachshea%2Fneo-pipe/lists"}