[
  {
    "path": ".gitignore",
    "content": ".DS_Store\ntags\ntest/\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2023 Michael Rommel\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# nvim-silicon\n\nPlugin to create code images using the external `silicon` tool.\n\nThis differs from `silicon.nvim` as that plugin uses a rust binding to call directly into the silicon rust library.\n\nThe plugin has been mentioned in a recent YouTube video by \"Dreams of Code\", titled [\"Create beautiful code screenshots in Neovim. Without damaging your wrists.\"](https://youtu.be/ig_HLrssAYE?si=R2OXs7EgcLZ8dj6r) Thank you, Elliott, for the kind words!\n\n## Features\n\nRight now, the plugin supports most options, that the original `silicon` tool offers. For watermarking an image, you can possibly use a background image with a watermark at the top/bottom edge.\n\nThis implementation supports selected line ranges, also highlighting of a line and removing superfluous indents and adding consistent padding or a separator between the numbers and the code. Also now it is possible to configure an output file AND the clipboard as destinations, the code will then call silicon possibly twice. For WSL2 users, a helper script is provided that copies the code screenshot to the Windows clipboard, see explanation below and in the separate document in the `docs` folder.\n\nExample code image:\n\n![Example code image](https://raw.githubusercontent.com/michaelrommel/nvim-silicon/main/assets/2024-03-01T20-33-20_code.png)\n\nThe typeface used in this example is called \"Victor Mono\" from Rune Bjørnerås. You can find it [here](https://rubjo.github.io/victor-mono/). Please consider using this typeface and sponsor Rune as well. Not visible in the screenshot, my go-to terminal emulator is [Wezterm](https://wezfurlong.org/wezterm/index.html). Please show him some love, too.\n\n### Ranges\n\nIf a range is visually selected it does not matter, whether it is block, line or normally selected. The range is then taken as complete lines: from the line in which the selection starts up to the line in which the selection ends.\nIf no selection is made, the whole file is taken as input. If you only want to select a single line, then you would have to manually select it with `Shift-V`.\n\n### Highlighting\n\nYou can mark a single line as to be highlighted using the standard vim `mark` command with the mark `h`, the default key combination would be `mh`.\n\n### Colours and background image\n\nYou can define either your own solid background colour or provide the path to a background image, setting both is not supported by `silicon` itself. The default colour setting for the shadow colour has also now been removed to let you consistently decide, which colour you want on both accounts.\n\n### Gobbling and padding\n\nWith the `gobble` parameter set to true, the longest common set of leading whitespace in each line is removed, making it easy to share screenshots of code fragments deep down in a nested structure. However, after removing all that whitespace, you now have the option to insert arbitrary characters between the line number rendered by `silicon` and the code fragment. Since you can use any string, you can - apart from padding blanks - also insert vertical bars etc.\n\n```lua\n    num_separator = \"\\u{258f} \",\n```\n\n### Language options\n\nThe underlying `silicon` command uses the rust `syntect` create for syntax detection and highlighting along with some heuristics. This plugin used the `vim.bo.filetype` as `--language` argument but users reported that some filetypes are not recognized, e.g. fortran.\n\nTherefore - in order not to break existing configs - now the following methods are used:\n- if the users set the `language` option in their config, this is used verbatim\n- if none is set, first the argument `--language <filetype>` is used as before, but if the `silicon` execution errors out, then\n- the file's extension is used as `--language <extension>` argument in a second attempt\n\nThis change hopefully does not break s.b. config but improves the chances of getting an image.\n\n### silicon's own config files\n\n`silicon` has the option of using an own config file, usually located at `${HOME}/.config/silicon/config`, but you can find out the location on your system with `silicon --config-file`. There common options can be defined, but the problem is, that command line arguments that `nvim-silicon` supplies and the same arguments in the config file lead to errors.\n\nNow in order to have both worlds, there is now a `disable_defaults` option. This will then only set the command argument. Nothing is added, so if a mandatory option like output destination selection or language is not given either in the config file or the options table, there likely is an error to be expected. So now you can split your arguments between the silicon config file and the neovim lua opts table, depending for instance on how you synchronize your configs across computers. Note that still conflicting arguments in both locations, like `background` and `background_image` still have to be avoided.\n\nExamples:\n\n`~/.config/silicon/config`:\n```text\n--output=\"./code.png\"\n--language=\"javascript\"\n--background=\"#00ff00\"\n--pad-horiz=10\n--pad-vert=5\n```\n\nwith\n\n`nvim-silicon.lua` for the `lazy` package manager:\n```lua\n-- create code images\nlocal opts = {\n\t\"michaelrommel/nvim-silicon\",\n\tlazy = true,\n\tcmd = \"Silicon\",\n\topts = {\n\t\tdisable_defaults = true\n\t}\n}\nreturn opts\n```\n\nwill render any file with `javascript` syntax highlighting in a file named `./code.png`.\n\n\n### Integrating with the Windows Subsystem for Linux\n\nThere are now two new options `wslclipboard` and `wslclipboardcopy`, which allow to send code images to the Windows clipboard, even though `nvim` runs inside the WSL, and without installing an X server on Windows and `xclip` on Linux.\n\nThe complete rabbit hole journey of this endeavor deserved it's own [description](./docs/wsl2-clipboard.md)\n\n\n\n### Lua Keybindings\n\nThere was the wish to be able to call directly lua functions for triggering the code images. There are now two entry points available\n\n- `.shoot()`: creates a code image with the default settings\n- `.file()`: saves the generated code image only into a file\n- `.clip()`: puts the generated code image only onto the clipboard\n\nThey can be used with `which-key`, for example, like this:\n\n```lua\nlocal wk = require(\"which-key\")\nwk.add({\n    mode = { \"v\" },\n    { \"<leader>s\",  group = \"Silicon\" },\n    { \"<leader>sc\", function() require(\"nvim-silicon\").clip() end, desc = \"Copy code screenshot to clipboard\" },\n    { \"<leader>sf\", function() require(\"nvim-silicon\").file() end,  desc = \"Save code screenshot as file\" },\n    { \"<leader>ss\", function() require(\"nvim-silicon\").shoot() end,  desc = \"Create code screenshot\" },\n})\n```\n\nCalling the `.shoot()` function behaves normal, just like calling the `:Silicon` vim command would behave, see the explanation of enabling both file and clipboard destinations below.\n\nOn the other hand the `.file()` and `.clip()` functions were thought of as additional, overriding functions, that would en-/disable the `--to-clipboard` and en-/disable the `--output` settings, respectively, for this one invocation.\n\nBecause here we manipulate the options table, these new functions may not work well with using a `silicon`-config file as described above. We do not know the settings in that file and the overriding mechanisms may cause conflicting command line and config file settings.\n\n\n### Setting multiple destinations\n\nThere was a request to have the option to generate an image file but also put the image onto the clipboard. Since the `silicon` command only supports an either/or setting, this means we need two calls to `silicon`. Up to now, the options were 1:1 converted into `silicon` arguments and if you had both `--to-clipboard` and `--output` set, `silicon` would throw an error.\n\nNow the plugin interprets this combination as \"I want both!\" and calls `silicon` twice, one time with the `--output` option and one time with the `--to-clipboard` option. In case the WSL integration is used, the `wslclipboardcopy` option steers, whether to keep or delete the possibly superfluous file.\n\n\n## Setup\n\nWith the `lazy.nvim` package manager:\n\n```lua\n{\n\t\"michaelrommel/nvim-silicon\",\n\tlazy = true,\n\tcmd = \"Silicon\",\n\tmain = \"nvim-silicon\",\n\topts = {\n\t\t-- Configuration here, or leave empty to use defaults\n\t\tline_offset = function(args)\n\t\t\treturn args.line1\n\t\tend,\n\t}\n},\n```\n\n**Please note:** When I created this plugin, I hadn't been fully aware of the namespaces that all plugins create. So I named the lua directory differently than the plugin name. In order to avoid name clashes with other modules, I have decided now to move from `require(\"silicon)` to `require(\"nvim-silicon)`. If you use the old name, a small message will be appended to the output of a successful call. (In a future version a deprecation warning will then show and when you look at `:messages` you should be able to find the place where the deprecated `require()` statements are and convert them. Most likely in the package manager or a key mappings configuration file.)\n\nThe `setup` function accepts the following table (shown with the builtin defaults, I have selected the defaults in a way, that they should work out of the box on most systems, please customize to your preference. In particular I removed the default output and font settings in order to enable using only the clipboard and make it work for users, who do not have Victor Mono NerdFont installed):\n\n```lua\n{\n\t-- disable_defaults will disable all builtin default settings apart\n\t-- from the base arguments, that are needed to call silicon at all, see\n\t-- mandatory_options below, also those options can be overridden\n\t-- all of the settings could be overridden in the lua setup call,\n\t-- but this clashes with the use of an external silicon --config=file,\n\t-- see issue #9\n\tdisable_defaults = false,\n\t-- turn on debug messages\n\tdebug = false,\n\t-- most of them could be overridden with other \n\t-- the font settings with size and fallback font\n\t-- Example: font = \"VictorMono NF=34;Noto Emoji\",\n\tfont = nil\n\t-- the theme to use, depends on themes available to silicon\n\ttheme = \"gruvbox-dark\",\n\t-- the background color outside the rendered os window\n\t-- (in hexcode string e.g \"#076678\")\n\tbackground = nil,\n\t-- a path to a background image\n\tbackground_image = nil,\n\t-- the paddings to either side\n\tpad_horiz = 100,\n\tpad_vert = 80,\n\t-- whether to have the os window rendered with rounded corners\n\tno_round_corner = false,\n\t-- whether to put the close, minimize, maximise traffic light \n\t-- controls on the border\n\tno_window_controls = false,\n\t-- whether to turn off the line numbers\n\tno_line_number = false,\n\t-- with which number the line numbering shall start\n\tline_offset = 1,\n\t-- here a function is used to return the actual source code line number\n\t-- line_offset = function(args)\n\t--     return args.line1\n\t-- end,\n\n\t-- the distance between lines of code\n\tline_pad = 0,\n\t-- the rendering of tab characters as so many space characters\n\ttab_width = 4,\n\t-- with which language the syntax highlighting shall be done, should be\n\t-- a function that returns either a language name or an extension like \"js\"\n\t-- it is set to nil, so you can override it, if you do not set it, we try the\n\t-- filetype first, and if that fails, the extension\n\tlanguage = nil\n\t-- language = function()\n\t-- \treturn vim.bo.filetype\n\t-- end,\n\t-- language = function()\n\t-- \treturn vim.fn.fnamemodify(\n\t-- \t\tvim.api.nvim_buf_get_name(vim.api.nvim_get_current_buf()),\n\t-- \t\t\":e\"\n\t-- \t)\n\t-- end,\n\n\t-- if the shadow below the os window should have be blurred\n\tshadow_blur_radius = 16,\n\t-- the offset of the shadow in x and y directions\n\tshadow_offset_x = 8,\n\tshadow_offset_y = 8,\n\t-- the color of the shadow (in hexcode string e.g \"#100808\")\n\tshadow_color = nil,\n\t-- whether to strip of superfluous leading whitespace\n\tgobble = true,\n\t-- a string to pad each line with after gobbling removed larger indents,\n\tnum_separator = nil,\n\t-- here a bar glyph is used to draw a vertial line and some space\n\t-- num_separator = \"\\u{258f} \",\n\n\t-- whether to put the image onto the clipboard, may produce an error,\n\t-- if run on WSL2\n\tto_clipboard = false,\n\t-- a string or function returning a string that defines the title\n\t-- showing in the image, only works in silicon versions greater than v0.5.1\n\twindow_title = nil,\n\t-- here a function is used to get the name of the current buffer\n\t-- window_title = function()\n\t--     return vim.fn.fnamemodify(\n\t--         vim.api.nvim_buf_get_name(vim.api.nvim_get_current_buf()),\n\t--         \":t\"\n\t--     )\n\t-- end,\n\n\t-- how to deal with the clipboard on WSL2\n\t-- possible values are: never, always, auto\n\twslclipboard = nil,\n\t-- what to do with the temporary screenshot image file when using the Windows\n\t-- clipboard from WSL2, possible values are: keep, delete\n\twslclipboardcopy = nil,\n\t-- the silicon command, put an absolute location here, if the\n\t-- command is not in your ${PATH}\n\tcommand = \"silicon\",\n\t-- a string or function that defines the path to the output image\n\toutput = nil\n\t-- here a function is used to create a file in the current directory\n\t-- output = function()\n\t-- \treturn \"./\" .. os.date(\"!%Y-%m-%dT%H-%M-%SZ\") .. \"_code.png\"\n\t-- end,\n}\n```\n\nThe mandatory options, that are used, even when the option `disable_defaults` is set to true are:\n\n```lua\n-- without that silicon cannot run. But you can override the command\n-- option in your lua config\nM.mandatory_options = {\n\tcommand = 'silicon',\n}\n```\n"
  },
  {
    "path": "docs/wsl2-clipboard.md",
    "content": "# WSL2 and the Windows Clipboard\n\n## Motivation\n\nWhen working inside the WSL2 it is sometimes preferable to get a code snapshot directly in the Windows clipboard to paste it in Teams or Mail or share it in another way. The `silicon` tool itself can directly copy a generated image to the clipboard on all platforms (Linux, Windows, Mac) but it can only do so when run on that platform itself, not in a subsystem like the WSL.\n\nBased on your feedback and my own desire to come up with a solution, I researched several ways to achieve this and here I outline my solution and the reasons behind several decisions. It may help others when facing similar problems.\n\n## Navigating the solution space\n\n### No-Go: direct copy to the Windows Clipboard\n\nSince WSL runs Linux, the `silicon` version on that platform tries to finde the `xclip` tool in order to copy the generated image to the X Clipboard. Which means running an X server on the Windows side and exposing that to the WSL. Which is pretty bloated and cumbersome to set up and probably not something a vi user wants to have. So I have pretty much decided that this is not the direction I would like to take.\n\n### Second choice: External 3rd Party Programs\n\nThere are numerous tools available including Windows' own `clip.exe` or `wslclip` programs that can put text onto the clipboard, but very few, that can actually handle images.\n\nIn my search, I found two tools:\n\n1. [NirCmd](http://www.nirsoft.net/utils/nircmd.html)\n2. [image-clipboard](https://github.com/bamontelucas/image-clipboard)\n\nBoth tools work as intended and can take an image file as input and put it onto the Windows clipboard.\n\nHowever that requires the user to download and install a binary program on their systems, which is always coming with an inherent risk, it is another step to perform and may be outright forbidden in your place of work.\n\n### Preferred: Powershell Script\n\nSince powershell provides a lot of commandlets and libraries as standard, there are native ways to put an image file on the Windows clipboard. The hurdles to overcome here are mainly two:\n\n1. the Script ExecutionPolicy that might be set\n2. the path semantics on Windows and Linux\n\nThe first issue arises, if the script that is called from neovim is part of the lua package installation and resides on the Linux side. Then - depending on the policy - loading a WSL-path script into the Windows powershell.exe, the WSL is seen as \"remote\" and might be blocked if the Policy is set to \"RemoteSigned\". It can be tackled by calling the `powershell.exe` interpreter with a script path that is local to the Windows side, but this is also then another step to copy/move that powershell script to Windows and adapt a Linux script with the new path to the installation. It for sure can be done, but to have to do it on every machine, where you sync your dotfiles to is a hassle, that I'd like to avoid.\n\nThe second issue is, that the generated image file from `silicon` most likely resides on the Linux side and the `powershell.exe` interpreter needs to see it from windows. Since the Windows perceived path contains the name of the distribution/installation of the WSL instance, it should not be a fixed value.\n\n## Solution\n\nThe solution I finally arrived at does also come with inconveniences, albeit hopefully some, that most users can live with, because they might already be fulfilled as part of a minimal installation.\n\n### Prerequisites\n\nYou need to have the `wslpath` tool available and in your path. It is by default a symlink from `/usr/bin/wslpath` to `/init` in a WSL2 installation, there should not be a need for any installation.\n\nAlso the `powershell.exe` interpreter should be in your path, which is typically taken over from the Windows System Environment Variables setting of your Windows installation (or you would need to adapt the helper script accordingly, see below).\n\n### How it works\n\nThe idea is to provide a new configuration option `wslclipboard` that steers, how `nvim-silicon` should interact with the clipboard if the clipboard has been selected as destination.\n\nSeveral values are available:\n\n- `never`: never try to make special provisions to copy to the Windows clipboard. This essentially means, that the usual Linux way used by `silicon` (via `xclip`) is always used. `nil` is regarded as `never`.\n- `always`: unconditionally use the provided mechanism to first create a file based screenshot on Linux and then push this image onto the Windows clipboard\n- `auto`: detect that nvim is running under WSL by looking for the string \"WSL\" in the output of the `uname -r` command, e.g. \"5.15.146.1-microsoft-standard-WSL2\". If WSL is detected, then use the provided mechanism, otherwise keep the `silicon` standard.\n\nSince we cannot access the Windows clipboard directly, we have to construct an image file first. This will be put in the location specified by the `output` opts key. If this is `nil` because you always only wanted the images to be placed on the clipboard or you called the new `.clip()` function, a temporary file will be created in `/tmp/<YYY-MM-DDTHH-MM-SSZ>_code.png`.\n\nThere is a second option `wslclipboardcopy` that defines, whether to keep these temporary files or not, the values are `keep` or `delete`. `nil` is regarded as `keep`.\n\nWhenever the Windows clipboard shall be used, first this (temporary) file is created in the usual manner. Then a script `wslclipimg` is called that resides in the `helper` directory of the plugin installation with the filename of that image file as parameter. This Linux bash script contains the powershell code needed to read that file and put the contents on the Windows clipboard. The original idea of using an EncodedCommand was misleading, because of a [bug in Powershell](https://github.com/PowerShell/PowerShell/issues/5912). Now the helper script passes the script as text, explicitly setting the ExecutionPolicy to Bypass. The path to the helper script is determined automatically based on the installation path of the plugin. If that breaks, please turn on `debug` in your config and let me know the output, so that it can be improved.\n\n```bash\n#! /usr/bin/env bash\n\n# we need to get the path to the WSL located file as it would be accessed\n# by the windows side. wslpath should be a symlink to /init on a standard WSL2\n# installation\nIMG=$(wslpath -w \"$1\")\n\nSCRIPT=$(cat << EOF\nAdd-Type -AssemblyName System.Drawing\nAdd-Type -AssemblyName System.Windows.Forms\n[Windows.Forms.Clipboard]::SetImage(\\$([System.Drawing.Image]::Fromfile(\\$(Get-Item \"${IMG}\"))));\nEOF\n)\n\n# powershell.exe should be on your path, otherwise specify the complete path to the\n# interpreter, like /mnt/c/Windows/system32/WindowsPowerShell/v1.0/powershell.exe\necho \"${SCRIPT}\" | powershell.exe -NoProfile -NoLogo -InputFormat text -OutputFormat text -NonInteractive -ExecutionPolicy Bypass -Command -\n```\n\nIf you have suggestions, how this could be even more simplified, please let me know.\n\n"
  },
  {
    "path": "helper/wslclipimg",
    "content": "#! /usr/bin/env bash\n\n# we need to get the path to the WSL located file as it would be accessed\n# by the windows side. wslpath should be a symlink to /init on a standard WSL2\n# installation\nIMG=$(wslpath -w \"$1\")\n\nSCRIPT=$(\n\tcat <<EOF\nAdd-Type -AssemblyName System.Drawing\nAdd-Type -AssemblyName System.Windows.Forms\n[Windows.Forms.Clipboard]::SetImage(\\$([System.Drawing.Image]::Fromfile(\\$(Get-Item \"${IMG}\"))));\nEOF\n)\n\n# powershell.exe should be on your path, otherwise specify the complete path to the\n# interpreter, like /mnt/c/Windows/system32/WindowsPowerShell/v1.0/powershell.exe\necho \"${SCRIPT}\" | powershell.exe -NoProfile -NoLogo -InputFormat text -OutputFormat text -NonInteractive -ExecutionPolicy Bypass -Command -\n"
  },
  {
    "path": "lua/nvim-silicon/init.lua",
    "content": "local M = {}\n\nM.utils = require(\"nvim-silicon.utils\")\n\nM.options = {}\n\n-- options, without silicon cannot be run\nM.mandatory_options = {\n\tcommand = 'silicon',\n}\n\n-- default options if nothing is provided by the user\nM.default_opts = {\n\tdebug = false,\n\tfont = nil,\n\ttheme = \"gruvbox-dark\",\n\tbackground = nil,\n\tbackground_image = nil,\n\tpad_horiz = 100,\n\tpad_vert = 80,\n\tno_round_corner = false,\n\tno_window_controls = false,\n\tno_line_number = false,\n\tline_offset = 1,\n\tline_pad = 0,\n\ttab_width = 4,\n\thighlight_lines = nil,\n\tlanguage = nil,\n\tshadow_blur_radius = 16,\n\tshadow_offset_x = 8,\n\tshadow_offset_y = 8,\n\tshadow_color = nil,\n\tgobble = true,\n\tto_clipboard = false,\n\twindow_title = nil,\n\tnum_separator = nil,\n\twslclipboard = nil,\n\tcommand = \"silicon\",\n\toutput = nil,\n}\n\nM.get_helper_path = function()\n\treturn debug.getinfo(2, \"S\").source:sub(2):match(\"(.*/).*/.*/\") .. \"helper/wslclipimg\"\nend\n\nM.parse_options = function(opts)\n\tlocal options\n\n\tvim.validate({\n\t\topts = { opts, \"table\" }\n\t})\n\n\tif opts and opts.disable_defaults then\n\t\toptions = vim.tbl_deep_extend(\n\t\t\t\"force\",\n\t\t\tM.mandatory_options,\n\t\t\topts or {}\n\t\t)\n\telse\n\t\toptions = vim.tbl_deep_extend(\n\t\t\t\"force\",\n\t\t\tM.default_opts,\n\t\t\topts or {}\n\t\t)\n\tend\n\n\treturn options\nend\n\nM.get_arguments = function(args, options)\n\tlocal cmdline = {}\n\tlocal value = nil\n\ttable.insert(cmdline, options.command)\n\tfor k, v in pairs(options) do\n\t\tif k == \"command\" or k == \"gobble\"\n\t\t\tor k == \"num_separator\" or k == \"disable_defaults\"\n\t\t\tor k == \"wslclipboard\" or k == \"wslclipboardcopy\"\n\t\t\tor k == \"debug\" or k == \"language\"\n\t\tthen\n\t\t\t-- no-op, since those are not silicon arguments or we deal with\n\t\t\t-- them dynamically later\n\t\telseif k == \"output\"\n\t\t\tor k == \"window_title\" or k == \"line_offset\" then\n\t\t\ttable.insert(cmdline, \"--\" .. string.gsub(k, \"_\", \"-\"))\n\t\t\tif type(v) == \"function\" then\n\t\t\t\tvalue = v(args)\n\t\t\telse\n\t\t\t\tvalue = v\n\t\t\tend\n\t\t\ttable.insert(cmdline, value)\n\t\t\tif k == \"output\" then M.filename = value end\n\t\telse\n\t\t\tif type(v) == \"boolean\" then\n\t\t\t\tif v then\n\t\t\t\t\ttable.insert(cmdline, \"--\" .. string.gsub(k, \"_\", \"-\"))\n\t\t\t\tend\n\t\t\telseif type(v) == \"nil\" then\n\t\t\t\t-- no-op\n\t\t\telse\n\t\t\t\ttable.insert(cmdline, \"--\" .. string.gsub(k, \"_\", \"-\"))\n\t\t\t\ttable.insert(cmdline, v)\n\t\t\tend\n\t\tend\n\tend\n\n\tif options.debug then\n\t\tprint(\"get_arguments cmdline: \" .. M.utils.dump(cmdline))\n\tend\n\treturn cmdline\nend\n\nM.format_lines = function(cmdline, args, options)\n\tlocal begin_line = args.line1 - 1\n\tlocal finish_line = args.line2\n\n\tif args.range == 0 then\n\t\tbegin_line = 0\n\t\tfinish_line = -1\n\tend\n\n\tlocal marks = vim.api.nvim_buf_get_mark(vim.api.nvim_win_get_buf(0), \"h\")[1]\n\tif marks > 0 then\n\t\tlocal hl\n\t\tif args.range == 0 or (args.line1 and marks >= begin_line and marks <= finish_line) then\n\t\t\thl = marks - begin_line\n\t\t\ttable.insert(cmdline, \"--highlight-lines\")\n\t\t\ttable.insert(cmdline, hl)\n\t\tend\n\tend\n\n\tlocal lines = vim.api.nvim_buf_get_lines(vim.api.nvim_win_get_buf(0), begin_line, finish_line, false)\n\n\tif options.gobble then\n\t\tlines = M.utils.gobble(lines)\n\tend\n\tif options.num_separator then\n\t\tlines = M.utils.separate(lines, options.num_separator)\n\tend\n\n\tif options.debug then\n\t\tprint(\"lines to shoot: \" .. M.utils.dump(lines))\n\tend\n\treturn lines, cmdline\nend\n\nM.cmd = function(args, options)\n\tlocal lines = nil\n\tlocal cmdline = nil\n\n\t-- build the commandline based on supplied options\n\tlocal base_cmdline = M.get_arguments(args, options)\n\t-- parse buffer into lines, based on arguments from neovim, reshapes cmdline\n\tlines, base_cmdline = M.format_lines(base_cmdline, args, options)\n\n\tlocal ret = {}\n\tlocal code\n\t-- if a language was supplied by the user, take that as argument directly\n\tif options.language then\n\t\tif type(options.language) == \"function\" then\n\t\t\tret.language = options.language()\n\t\telse\n\t\t\tret.language = options.language\n\t\tend\n\n\t\tcmdline = vim.tbl_extend(\"error\", base_cmdline, {})\n\t\ttable.insert(cmdline, '--language')\n\t\ttable.insert(cmdline, ret.language)\n\t\tif options.debug then\n\t\t\tprint(\"cmd cmdline: \" .. M.utils.dump(cmdline))\n\t\tend\n\t\tcode = vim.fn.system(cmdline, lines)\n\t\tcode = string.gsub(code, \"\\n\", \"\")\n\t\tret.code = code\n\telse\n\t\tif options.disable_defaults then\n\t\t\t-- run silicon as is, no supplement of anything\n\t\t\tif options.debug then\n\t\t\t\tprint(\"base_cmdline: \" .. M.utils.dump(base_cmdline))\n\t\t\tend\n\t\t\tcode = vim.fn.system(base_cmdline, lines)\n\t\t\tcode = string.gsub(code, \"\\n\", \"\")\n\t\t\tret.language = nil\n\t\t\tret.code = code\n\t\telse\n\t\t\t-- try first the language parameter derived from the buffer's filetype\n\t\t\tcmdline = vim.tbl_extend(\"error\", base_cmdline, {})\n\t\t\tret.language = vim.bo.filetype\n\t\t\ttable.insert(cmdline, '--language')\n\t\t\ttable.insert(cmdline, ret.language)\n\t\t\tif options.debug then\n\t\t\t\tprint(\"1. lang cmdline: \" .. M.utils.dump(cmdline))\n\t\t\tend\n\t\t\tcode = vim.fn.system(cmdline, lines)\n\t\t\tcode = string.gsub(code, \"\\n\", \"\")\n\t\t\tret.code = code\n\t\t\tif options.debug then\n\t\t\t\tprint(\"returncode: \" .. M.utils.dump(code))\n\t\t\tend\n\t\t\tif code ~= \"\" then\n\t\t\t\tvim.notify(\n\t\t\t\t\t\"silicon call with filetype error: \" .. code .. \", trying extension...\",\n\t\t\t\t\tvim.log.levels.WARN,\n\t\t\t\t\t{ title = \"nvim-silicon\" }\n\t\t\t\t)\n\t\t\t\t-- seems to have gone wrong, new try with extension\n\t\t\t\tcmdline = vim.tbl_extend(\"error\", base_cmdline, {})\n\t\t\t\tret.language = vim.fn.fnamemodify(\n\t\t\t\t\tvim.api.nvim_buf_get_name(vim.api.nvim_get_current_buf()),\n\t\t\t\t\t\":e\"\n\t\t\t\t)\n\t\t\t\ttable.insert(cmdline, '--language')\n\t\t\t\ttable.insert(cmdline, ret.language)\n\t\t\t\tif options.debug then\n\t\t\t\t\tprint(\"2. lang cmdline: \" .. M.utils.dump(cmdline))\n\t\t\t\tend\n\t\t\t\tcode = vim.fn.system(cmdline, lines)\n\t\t\t\tcode = string.gsub(code, \"\\n\", \"\")\n\t\t\t\tret.code = code\n\t\t\tend\n\t\tend\n\tend\n\n\t-- last, final attempt being evaluated\n\tif code ~= \"\" then\n\t\tvim.notify(\n\t\t\t\"silicon returned with: \" .. code,\n\t\t\tvim.log.levels.WARN,\n\t\t\t{ title = \"nvim-silicon\" }\n\t\t)\n\telse\n\t\tif not M.message then\n\t\t\tM.message = \"\"\n\t\tend\n\t\tif options.to_clipboard then\n\t\t\tvim.notify(\n\t\t\t\t\"silicon put the image on the clipboard.\" .. M.message,\n\t\t\t\tvim.log.levels.INFO,\n\t\t\t\t{ title = \"nvim-silicon\" }\n\t\t\t)\n\t\telse\n\t\t\tlocal get_location = function()\n\t\t\t\tlocal location = nil\n\t\t\t\tif not M.filename then\n\t\t\t\t\tlocation = \"the location specified in your config file\"\n\t\t\t\telseif string.sub(tostring(M.filename), 1, 1) == \"~\" then\n\t\t\t\t\tlocation = M.filename\n\t\t\t\telseif string.sub(tostring(M.filename), 1, 2) == \"./\" then\n\t\t\t\t\tlocation = vim.fn.getcwd() .. string.sub(tostring(M.filename), 2)\n\t\t\t\telse\n\t\t\t\t\t-- location = vim.fn.getcwd() .. \"/\" .. M.filename\n\t\t\t\t\tlocation = M.filename\n\t\t\t\tend\n\t\t\t\treturn location\n\t\t\tend\n\t\t\tret.location = get_location()\n\t\t\tvim.notify(\n\t\t\t\t\"silicon generated an image at \" .. ret.location .. \".\" .. M.message,\n\t\t\t\tvim.log.levels.INFO,\n\t\t\t\t{ title = \"nvim-silicon\" }\n\t\t\t)\n\t\tend\n\tend\n\treturn ret\nend\n\nM.start = function(args, opts)\n\tlocal options\n\t-- stores the error code in case silicon returns something\n\tlocal code\n\t-- stores the return value of the call to create a file\n\tlocal ret = nil\n\n\tif opts.debug then\n\t\tprint(\"Global options: \" .. M.utils.dump(M.options))\n\t\tprint(\"Local options: \" .. M.utils.dump(opts))\n\tend\n\n\t-- make a deep copy of the original options\n\toptions = vim.tbl_deep_extend(\n\t\t\"force\",\n\t\topts,\n\t\t{}\n\t)\n\n\tif (not opts.output) and (not opts.to_clipboard) and (not opts.disable_defaults) then\n\t\t-- the user has not supplied any valid destination and not disabled defaults\n\t\t-- so add the default output file destination function that we used before\n\t\tif opts.debug then\n\t\t\tprint(\"setting default output function\")\n\t\tend\n\t\t-- temporary marker to create a temporary output, note this assignment changes\n\t\t-- the supplied opts table for subsequent calls, so make sure not to submit the\n\t\t-- global options table to the function. Needs a refactor w/r to opts/options\n\t\topts.output = true\n\t\t-- the actual output destination\n\t\toptions.output = function()\n\t\t\treturn \"./\" .. os.date(\"!%Y-%m-%dT%H-%M-%SZ\") .. \"_code.png\"\n\t\tend\n\tend\n\n\t-- if wished for, let's create the file first\n\tif opts.output then\n\t\toptions.to_clipboard = false\n\t\tret = M.cmd(args, options)\n\tend\n\n\tlocal function shell_exists(name)\n\t\tlocal f = io.open(name, \"r\")\n\t\tif f ~= nil then\n\t\t\tio.close(f)\n\t\t\treturn true\n\t\telse\n\t\t\treturn false\n\t\tend\n\tend\n\n\tif opts.to_clipboard then\n\t\t-- check whether wsl detection shall be done\n\t\tif (opts.wslclipboard == \"auto\" and M.utils.is_wsl()) or\n\t\t\t(opts.wslclipboard == \"always\") then\n\t\t\t-- we want to use the WSL integration\n\t\t\tlocal cmdline = {}\n\t\t\t-- check which shell to use here\n\t\t\tif (shell_exists(\"/bin/bash\")) then\n\t\t\t\ttable.insert(cmdline, \"/bin/bash\")\n\t\t\telseif (shell_exists(\"/usr/bin/bash\")) then\n\t\t\t\ttable.insert(cmdline, \"/usr/bin/bash\")\n\t\t\telseif (shell_exists(\"/usr/local/bin/bash\")) then\n\t\t\t\ttable.insert(cmdline, \"/usr/local/bin/bash\")\n\t\t\telse\n\t\t\t\ttable.insert(cmdline, \"/bin/sh\")\n\t\t\tend\n\t\t\ttable.insert(cmdline, M.helper)\n\t\t\tif ret and ret.location then\n\t\t\t\t-- we have already a file, need to send it to the windows side\n\t\t\t\ttable.insert(cmdline, ret.location)\n\t\t\telse\n\t\t\t\t-- we need to create a temporary file\n\t\t\t\toptions.output = \"/tmp/\" .. os.date(\"!%Y-%m-%dT%H-%M-%SZ\") .. \"_code.png\"\n\t\t\t\toptions.to_clipboard = false\n\t\t\t\tret = M.cmd(args, options)\n\t\t\t\tif ret and ret.location then\n\t\t\t\t\t-- now we have a file, need to send it to the windows side\n\t\t\t\t\ttable.insert(cmdline, ret.location)\n\t\t\t\telse\n\t\t\t\t\t-- notify user that the tmp image generation failed\n\t\t\t\t\tvim.notify(\n\t\t\t\t\t\t\"silicon returned with: \" .. ret.code,\n\t\t\t\t\t\tvim.log.levels.WARN,\n\t\t\t\t\t\t{ title = \"nvim-silicon\" }\n\t\t\t\t\t)\n\t\t\t\t\treturn\n\t\t\t\tend\n\t\t\tend\n\t\t\tif opts.debug then\n\t\t\t\tprint(\"start cmdline: \" .. M.utils.dump(cmdline))\n\t\t\tend\n\t\t\tcode = vim.fn.system(cmdline)\n\t\t\tcode = string.gsub(code, \"\\n\", \"\")\n\t\t\tif code ~= \"\" then\n\t\t\t\tvim.notify(\n\t\t\t\t\t\"wslclipimg returned with: \" .. code,\n\t\t\t\t\tvim.log.levels.WARN,\n\t\t\t\t\t{ title = \"nvim-silicon\" }\n\t\t\t\t)\n\t\t\telse\n\t\t\t\tvim.notify(\n\t\t\t\t\t\"wslclipimg put the image at \" .. ret.location .. \" onto the clipboard\",\n\t\t\t\t\tvim.log.levels.INFO,\n\t\t\t\t\t{ title = \"nvim-silicon\" }\n\t\t\t\t)\n\t\t\tend\n\t\t\t-- file based outp[ut was not desired, so we created a tmp file\n\t\t\t-- we need to check opts and not options here\n\t\t\tif (not opts.output) and (opts.wslclipboardcopy == \"delete\") then\n\t\t\t\t-- we should clean that tmp file now\n\t\t\t\tlocal _, err = os.remove(ret.location)\n\t\t\t\tif err then\n\t\t\t\t\tvim.notify(\n\t\t\t\t\t\t\"wslclipimg could not delete the tmp file: \" .. err,\n\t\t\t\t\t\tvim.log.levels.WARN,\n\t\t\t\t\t\t{ title = \"nvim-silicon\" }\n\t\t\t\t\t)\n\t\t\t\tend\n\t\t\tend\n\t\telse\n\t\t\t-- we want the standard way of putting an image onto the clipboard\n\t\t\tif ret and ret.code == \"\" and ret.language then\n\t\t\t\t-- we already know which language works\n\t\t\t\toptions.language = ret.language\n\t\t\tend\n\t\t\toptions.output = nil\n\t\t\toptions.to_clipboard = true\n\t\t\tret = M.cmd(args, options)\n\t\tend\n\tend\nend\n\nM.shoot = function(opts)\n\tlocal options\n\t-- we get overridden options, if we are called from\n\t-- .clip() or .file()\n\tif opts then\n\t\t-- make a deep copy of the original options\n\t\toptions = vim.tbl_deep_extend(\n\t\t\t\"force\",\n\t\t\topts,\n\t\t\t{}\n\t\t)\n\telse\n\t\t-- make a deep copy of the original options\n\t\toptions = vim.tbl_deep_extend(\n\t\t\t\"force\",\n\t\t\tM.options,\n\t\t\t{}\n\t\t)\n\tend\n\n\tlocal args = nil\n\tlocal mode = vim.api.nvim_get_mode().mode\n\tif mode == \"n\" then\n\t\targs = {\n\t\t\tline1 = 0,\n\t\t\tline2 = -1,\n\t\t\trange = 0\n\t\t}\n\telseif mode == \"v\" or mode == \"V\" or mode == \"\\22\" then\n\t\tlocal line1 = vim.fn.getpos(\"v\")[2]\n\t\tlocal line2 = vim.api.nvim_win_get_cursor(0)[1]\n\t\tif line1 > line2 then\n\t\t\tlocal linetmp = line1\n\t\t\tline1 = line2\n\t\t\tline2 = linetmp\n\t\tend\n\t\targs = {\n\t\t\tline1 = line1,\n\t\t\tline2 = line2,\n\t\t\trange = 1\n\t\t}\n\tend\n\tif M.options.debug then\n\t\tprint(\"mode is: \" .. mode)\n\t\tprint(\"args: \" .. M.utils.dump(args))\n\tend\n\tM.start(args, options)\nend\n\nM.file = function()\n\tlocal options\n\t-- make a deep copy of the original options\n\toptions = vim.tbl_deep_extend(\n\t\t\"force\",\n\t\tM.options,\n\t\t{}\n\t)\n\toptions.to_clipboard = false\n\t-- if not options.output then\n\t-- \tprint(\"You triggered the .file() function, but forgot to set an output path\")\n\t-- \toptions.output = \"/tmp/\" .. os.date(\"!%Y-%m-%dT%H-%M-%SZ\") .. \"_code.png\"\n\t-- end\n\tM.shoot(options)\nend\n\nM.clip = function()\n\tlocal options\n\t-- make a deep copy of the original options\n\toptions = vim.tbl_deep_extend(\n\t\t\"force\",\n\t\tM.options,\n\t\t{}\n\t)\n\toptions.to_clipboard = true\n\toptions.output = nil\n\tM.shoot(options)\nend\n\nM.usercmd = function(args)\n\tlocal options\n\t-- make a deep copy of the original options\n\toptions = vim.tbl_deep_extend(\n\t\t\"force\",\n\t\tM.options,\n\t\t{}\n\t)\n\tM.start(args, options)\nend\n\nM.setup = function(opts)\n\t-- populate the global options table\n\tM.options = M.parse_options(opts)\n\t-- find my own path\n\tM.helper = M.get_helper_path()\n\tif M.options.debug then\n\t\tprint(\"helper is at: \" .. M.helper)\n\tend\n\n\t-- define commands for neovim\n\tvim.api.nvim_create_user_command(\"Silicon\", function(args)\n\t\tM.usercmd(args)\n\tend, {\n\t\tdesc = \"convert range to code image representation\",\n\t\tforce = false,\n\t\trange = true\n\t})\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-silicon/utils.lua",
    "content": "local M = {}\n\nM.gobble = function(lines)\n\tlocal shortest_whitespace = nil\n\tlocal whitespace = \"\"\n\tfor _, v in pairs(lines) do\n\t\t_, _, whitespace = string.find(v, \"^(%s*)\")\n\t\tif type(whitespace) ~= \"nil\" then\n\t\t\tif shortest_whitespace == nil or (#whitespace < #shortest_whitespace and v ~= \"\") then\n\t\t\t\tshortest_whitespace = whitespace\n\t\t\tend\n\t\tend\n\tend\n\tif #shortest_whitespace > 0 then\n\t\tlocal newlines = {}\n\t\tfor _, v in pairs(lines) do\n\t\t\tlocal newline = string.gsub(v, \"^\" .. shortest_whitespace, \"\", 1)\n\t\t\ttable.insert(newlines, newline)\n\t\tend\n\t\treturn newlines\n\telse\n\t\treturn lines\n\tend\nend\n\nM.separate = function(lines, num_separator)\n\tlocal newlines = {}\n\tfor _, v in pairs(lines) do\n\t\tlocal newline = string.gsub(v, \"^\", num_separator, 1)\n\t\ttable.insert(newlines, newline)\n\tend\n\treturn newlines\nend\n\nM.dump = function(t)\n\tlocal conv = {\n\t\t[\"nil\"] = function() return \"nil\" end,\n\t\t[\"number\"] = function(n) return tostring(n) end,\n\t\t[\"string\"] = function(s) return '\"' .. s .. '\"' end,\n\t\t[\"boolean\"] = function(b) return tostring(b) end,\n\t\t[\"function\"] = function(_) return \"function()\" end,\n\t}\n\tif type(t) == \"table\" then\n\t\tlocal s = \"{\"\n\t\tfor k, v in pairs(t) do\n\t\t\tif type(v) == \"table\" then\n\t\t\t\ts = s .. (s == \"{\" and \" \" or \", \") .. (k .. \" = \" .. M.dump(v))\n\t\t\telse\n\t\t\t\ts = s .. (s == \"{\" and \" \" or \", \") .. k .. \" = \" .. conv[type(v)](v)\n\t\t\tend\n\t\tend\n\t\treturn s .. \" }\"\n\telse\n\t\treturn conv[type(t)](t)\n\tend\nend\n\nM.is_wsl = function()\n\tlocal output = vim.fn.systemlist \"uname -r\"\n\treturn not not string.find(output[1] or \"\", \"WSL\")\nend\n\n\nreturn M\n"
  },
  {
    "path": "lua/silicon/init.lua",
    "content": "local M = {}\n\n-- In the next release use the deprecation function to\n-- alert users. For now a message addendum should suffice.\n-- vim.deprecate(\n-- \t\"require(\\\"silicon\\\")\",\n-- \t\"require(\\\"nvim-silicon\\\")\",\n-- \t\"v1.2\",\n-- \t\"nvim-silicon\",\n-- \ttrue\n-- )\n\nM = require('nvim-silicon')\nM.message = \" Please use 'require(\\\"nvim-silicon\\\")' now, see README!\"\nreturn M\n"
  }
]