diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c4c4ffc
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+*.zip
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..f62b98d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,5 @@
+# Micro 2.0 Plugins
+
+This repository contains plugins that have been updated for micro 2.0. The goal is to
+have these changes merged back into the original plugin repositories, but in the meantime the updated
+versions will be available here.
diff --git a/editorconfig-micro/.editorconfig b/editorconfig-micro/.editorconfig
new file mode 100644
index 0000000..d7794b5
--- /dev/null
+++ b/editorconfig-micro/.editorconfig
@@ -0,0 +1,15 @@
+root = true
+
+[*]
+
+indent_style = space
+indent_size = 4
+end_of_line = lf
+charset = utf-8
+
+trim_trailing_whitespace = true
+insert_final_newline = true
+max_line_length = 80
+
+[*.json]
+indent_size = 2
diff --git a/editorconfig-micro/LICENSE b/editorconfig-micro/LICENSE
new file mode 100644
index 0000000..1e3d5d0
--- /dev/null
+++ b/editorconfig-micro/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018 github.com/10sr + contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/editorconfig-micro/README.md b/editorconfig-micro/README.md
new file mode 100644
index 0000000..b81020b
--- /dev/null
+++ b/editorconfig-micro/README.md
@@ -0,0 +1,47 @@
+# editorconfig-micro
+
+[EditorConfig] plugin for the [`micro`] editor. Works with `micro` v2.
+
+
+### Prerequisites
+
+You'll need an `editorconfig` core executable, like [EditorConfig C Core], installed and on your PATH.
+
+
+### Installation
+
+ git clone https://github.com/10sr/editorconfig-micro "${XDG_CONFIG_HOME:-~/.config}/micro/plug/editorconfig-micro"
+
+That's all! This plugin will be automatically enabled after you restart [`micro`]. It will automatically apply the appropriate editorconfig properties on files when they are opened or saved.
+
+For more information, use `help editorconfig` in command mode or view `help/editorconfig.md` in this repo.
+
+
+### Supported Properties
+
+* `root` (only used by EditorConfig Core)
+* `indent_style`
+* `indent_size`
+* `tab_width`
+* `charset`
+ * Currently, [`micro`] only [supports][EditorConfig Options] the `utf-8` charset.
+* `end_of_line`
+ * Currently, [`micro`] only [supports][EditorConfig Options] `lf` and `crlf`.
+* `insert_final_newline`
+* `trim_trailing_whitespace`
+
+
+### Unsupported Properties
+
+* `max_line_length`
+
+
+### License
+
+This software is licensed under MIT License.
+See [LICENSE](LICENSE) for details.
+
+[`micro`]: https://micro-editor.github.io
+[EditorConfig]: http://editorconfig.org
+[EditorConfig Options]: https://github.com/zyedidia/micro/blob/master/runtime/help/options.md
+[EditorConfig C Core]: https://github.com/editorconfig/editorconfig-core-c
diff --git a/editorconfig-micro/editorconfig.lua b/editorconfig-micro/editorconfig.lua
new file mode 100644
index 0000000..0a18209
--- /dev/null
+++ b/editorconfig-micro/editorconfig.lua
@@ -0,0 +1,166 @@
+VERSION = "0.3.0"
+
+local micro = import("micro")
+local microBuffer = import("micro/buffer")
+local config = import("micro/config")
+local shell = import("micro/shell")
+
+local verbose = config.GetGlobalOption("editorconfigverbose") or false
+
+local function errlog(msg)
+ -- TODO: automatically open the log buffer like plugin list
+ microBuffer.Log(("editorconfig error: %s\n"):format(msg))
+end
+
+-- for debugging; use micro -debug, and then inspect log.txt
+local function log(msg)
+ micro.Log(("editorconfig debug: %s"):format(msg))
+end
+
+local function setSafely(key, value, buffer)
+ if value == nil then
+ -- log(("Ignore nil for %s"):format(key))
+ else
+ if config.GetGlobalOption(key) ~= value then
+ log(("Set %s = %s"):format(key, value))
+ buffer:SetOptionNative(key, value)
+ end
+ end
+end
+
+local function setIndentation(properties, buffer)
+ local indent_size_str = properties["indent_size"]
+ local tab_width_str = properties["tab_width"]
+ local indent_style = properties["indent_style"]
+
+ local indent_size = tonumber(indent_size_str, 10)
+ local tab_width = tonumber(tab_width_str, 10)
+
+ if indent_size_str == "tab" then
+ indent_size = tab_width
+ elseif tab_width == nil then
+ tab_width = indent_size
+ end
+
+ if indent_style == "space" then
+ setSafely("tabstospaces", true, buffer)
+ setSafely("tabsize", indent_size, buffer)
+ elseif indent_style == "tab" then
+ setSafely("tabstospaces", false, buffer)
+ setSafely("tabsize", tab_width, buffer)
+ elseif indent_style ~= nil then
+ errlog(("Unknown value for editorconfig property indent_style: %s"):format(indent_style or "nil"))
+ end
+end
+
+local function setEndOfLine(properties, buffer)
+ local end_of_line = properties["end_of_line"]
+ if end_of_line == "lf" then
+ setSafely("fileformat", "unix", buffer)
+ elseif end_of_line == "crlf" then
+ setSafely("fileformat", "dos", buffer)
+ elseif end_of_line == "cr" then
+ -- See https://github.com/zyedidia/micro/blob/master/runtime/help/options.md for supported runtime options.
+ errlog(("Value %s for editorconfig property end_of_line is not currently supported by micro."):format(end_of_line))
+ elseif end_of_line ~= nil then
+ errlog(("Unknown value for editorconfig property end_of_line: %s"):format(end_of_line))
+ end
+end
+
+local function setCharset(properties, buffer)
+ local charset = properties["charset"]
+ if charset ~= "utf-8" and charset ~= nil then
+ -- TODO: I believe micro 2.0 added support for more charsets, so this is gonna have to be updated accordingly.
+ -- Also now we need to actually set the charset since it isn't just utf-8.
+ errlog(("Value %s for editorconfig property charset is not currently supported by micro."):format(charset))
+ end
+end
+
+local function setTrimTrailingWhitespace(properties, buffer)
+ local val = properties["trim_trailing_whitespace"]
+ if val == "true" then
+ setSafely("rmtrailingws", true, buffer)
+ elseif val == "false" then
+ setSafely("rmtrailingws", false, buffer)
+ elseif val ~= nil then
+ errlog(("Unknown value for editorconfig property trim_trailing_whitespace: %s"):format(val))
+ end
+end
+
+local function setInsertFinalNewline(properties, buffer)
+ local val = properties["insert_final_newline"]
+ if val == "true" then
+ setSafely("eofnewline", true, buffer)
+ elseif val == "false" then
+ setSafely("eofnewline", false, buffer)
+ elseif val ~= nil then
+ errlog(("Unknown value for editorconfig property insert_final_newline: %s"):format(val))
+ end
+end
+
+local function applyProperties(properties, buffer)
+ setIndentation(properties, buffer)
+ setEndOfLine(properties, buffer)
+ setCharset(properties, buffer)
+ setTrimTrailingWhitespace(properties, buffer)
+ setInsertFinalNewline(properties, buffer)
+end
+
+function onEditorConfigExit(output, args)
+ if verbose then
+ log(("Output: \n%s"):format(output))
+ end
+
+ local properties = {}
+ for line in output:gmatch('([^\n]+)') do
+ local key, value = line:match('([^=]*)=(.*)')
+ if key == nil or value == nil then
+ errlog(("Failed to parse editorconfig output: %s"):format(line))
+ return
+ end
+ key = key:gsub('^%s(.-)%s*$', '%1')
+ value = value:gsub('^%s(.-)%s*$', '%1')
+ properties[key] = value
+ end
+
+ local buffer = args[1]
+ applyProperties(properties, buffer)
+
+ if verbose then
+ log("Running editorconfig done")
+ end
+end
+
+function getApplyProperties(bufpane)
+ buffer = bufpane.Buf
+ if (buffer.Path or "") == "" then
+ -- Current buffer does not visit any file
+ return
+ end
+
+ local fullpath = buffer.AbsPath
+ if fullpath == nil then
+ messenger:Message("editorconfig: AbsPath is empty")
+ return
+ end
+
+ if verbose then;
+ log(("Running editorconfig %s"):format(fullpath))
+ end
+
+ shell.JobSpawn("editorconfig", {fullpath}, "", "", "editorconfig.onEditorConfigExit", buffer)
+end
+
+function onBufPaneOpen(bp)
+ getApplyProperties(bp)
+end
+
+function onSave(bp)
+ getApplyProperties(bp)
+ return true
+end
+
+function init()
+ config.MakeCommand("editorconfig", getApplyProperties, config.NoComplete)
+ config.AddRuntimeFile("editorconfig", config.RTHelp, "help/editorconfig.md")
+end
diff --git a/editorconfig-micro/help/editorconfig.md b/editorconfig-micro/help/editorconfig.md
new file mode 100644
index 0000000..b45863c
--- /dev/null
+++ b/editorconfig-micro/help/editorconfig.md
@@ -0,0 +1,33 @@
+editorconfig-micro
+==================
+
+[EditorConfig][] helps developers define and maintain
+consistent coding styles between different editors and IDEs.
+This is the EditorConfig plugin for the `micro` editor.
+
+This plugin requires an editorconfig core executable to be installed.
+For example, download the [EditorConfig C Core][] and follow the instructions in
+the README and INSTALL files to install it.
+
+
+Usage
+-----
+
+Once installed, this plugin will automatically execute `editorconfig.getApplyProperties`
+on files when they are opened or saved.
+
+You can also explicitly use the `editorconfig` command in command mode, or bind it to
+a keystroke. For example:
+
+```json
+{
+ "Alt-e": "editorconfig.getApplyProperties"
+}
+```
+
+If any editorconfig properties have been changed, they will be logged, which can be viewed
+with `log` in command mode. If you want to see verbose logs, you must manually add `"editorconfigverbose": true,` to your user settings in `~/.config/micro/settings.json`.
+
+
+[EditorConfig]: http://editorconfig.org
+[EditorConfig C Core]: https://github.com/editorconfig/editorconfig-core-c
diff --git a/editorconfig-micro/repo.json b/editorconfig-micro/repo.json
new file mode 100644
index 0000000..e81f780
--- /dev/null
+++ b/editorconfig-micro/repo.json
@@ -0,0 +1,22 @@
+[{
+ "Name": "editorconfig",
+ "Description": "EditorConfig plugin for micro",
+ "Tags": ["editorconfig", "utility", "format"],
+ "Website": "https://github.com/10sr/editorconfig-micro",
+ "Versions": [
+ {
+ "Version": "0.3.0",
+ "Url": "https://github.com/10sr/editorconfig-micro/archive/v0.3.0.zip",
+ "Require": {
+ "micro": ">=2.0.0-1"
+ }
+ },
+ {
+ "Version": "0.2.3",
+ "Url": "https://github.com/10sr/editorconfig-micro/archive/v0.2.3.zip",
+ "Require": {
+ "micro": ">=1.3.2"
+ }
+ }
+ ]
+}]
diff --git a/filemanager-plugin/.editorconfig b/filemanager-plugin/.editorconfig
new file mode 100644
index 0000000..a34d753
--- /dev/null
+++ b/filemanager-plugin/.editorconfig
@@ -0,0 +1,17 @@
+# EditorConfig is awesome: http://EditorConfig.org
+
+# top-most EditorConfig file
+root = true
+
+# Unix-style newlines with a newline ending every file
+[*]
+charset = utf-8
+end_of_line = lf
+indent_style = tab
+indent_size = 2
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+# Ignore .md files, because 2 spaces at end-of-line has meaning
+[*.md]
+trim_trailing_whitespace = false
diff --git a/filemanager-plugin/CHANGELOG.md b/filemanager-plugin/CHANGELOG.md
new file mode 100644
index 0000000..46d51c4
--- /dev/null
+++ b/filemanager-plugin/CHANGELOG.md
@@ -0,0 +1,109 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
+
+## [Unreleased]
+
+## [3.4.0] - 2018-10-22
+
+### Fixed
+
+- Issues with Lua's `io.popen` on some systems by using Micro's built-in `RunShellCommand` instead, [thanks to @scottbilas](https://github.com/NicolaiSoeborg/filemanager-plugin/pull/38)
+
+### Added
+
+- Adds the option `filemanager-openonstart` to allow auto-opening the file tree when Micro is started (default OFF)
+
+### Changed
+
+- Update README's option's documentation
+
+## [3.3.1] - 2018-10-03
+
+### Changed
+
+- Performance improvement by removing unnecessary refresh of the opened file, [thanks to @jackwilsdon](https://github.com/NicolaiSoeborg/filemanager-plugin/pull/37)
+
+## [3.3.0] - 2018-09-13
+
+### Added
+
+- The ability to sort folders above files, [thanks to @cbrown1](https://github.com/NicolaiSoeborg/filemanager-plugin/pull/33)
+
+### Fixed
+
+- The displayed filenames are now correctly only showing their "basename" on Windows
+
+## [3.2.0] - 2018-02-15
+
+### Added
+
+- The ability to go to parent directory with left arrow (when not minimizing). Thanks @avently
+- The ability to jump to the `..` as a "parent directory". Thanks @avently
+
+## [3.1.2] - 2018-02-07
+
+### Fixed
+
+- The minimum Micro version, which was incorrectly set to v1.4.0. Ref [issue #28](https://github.com/NicolaiSoeborg/filemanager-plugin/issues/28)
+
+## [3.1.1] - 2018-02-04
+
+### Fixed
+
+Ref https://github.com/zyedidia/micro/issues/992 for both of these fixes.
+
+- The syntax parser not loading correctly (mostly block comments) on opened files. **Requires Micro >= v1.4.0**
+- An errant tab being inserted into the newly opened file.
+
+## [3.1.0] - 2018-01-30
+
+### Added
+
+- The ability to hide dotfiles using the `filemanager-showdotfiles` option.
+- The ability to hide files ignored in your VCS (aka `.gitignore`'d) using the `filemanager-showignored` option. Only works with Git at the moment.
+- This `CHANGELOG.md`
+
+### Fixed
+
+- A bug with the `rm` command that caused weird, undefined behaviour to contents within the same dir as the file/dir deleted.
+- Issue [#24](https://github.com/NicolaiSoeborg/filemanager-plugin/issues/24)
+
+## [3.0.0] - 2018-01-10
+
+### Fixed
+
+- Issues [#13](https://github.com/NicolaiSoeborg/filemanager-plugin/issues/13), [#14](https://github.com/NicolaiSoeborg/filemanager-plugin/issues/14), [#15](https://github.com/NicolaiSoeborg/filemanager-plugin/issues/15), [#19](https://github.com/NicolaiSoeborg/filemanager-plugin/issues/19), [#20](https://github.com/NicolaiSoeborg/filemanager-plugin/issues/20)
+- The broken syntax highlighting
+
+### Added
+
+- Directory expansion/compression below itself for viewing more akin to a file tree.
+- The `rm` command, which deletes the file/directory under the cursor.
+- The `touch` command, which creates a file with the passed filename.
+- The `mkdir` command, which creates a directory with the passed filename.
+- An API, of sorts, for the user to rebind their keys to if they dislike the defaults.
+- An [editorconfig](http://editorconfig.org/) file.
+
+### Changed
+
+- The view that it spawns in to read-only, which requires Micro version >= 1.3.5
+- The functionality of some keybindings (when in the view) so they work safetly, or at all, with the plugin.
+- From the `enter` key to `tab` for opening/going into files/dirs (a side-effect of using the read-only setting)
+
+### Removed
+
+- The ability to use a lot of keybindings that would otherwise mess with the view, and have no benifit.
+- The pointless `.gitignore` file.
+
+[unreleased]: https://github.com/NicolaiSoeborg/filemanager-plugin/compare/v3.4.0...HEAD
+[3.4.0]: https://github.com/NicolaiSoeborg/filemanager-plugin/compare/v3.3.1...v3.4.0
+[3.3.1]: https://github.com/NicolaiSoeborg/filemanager-plugin/compare/v3.3.0...v3.3.1
+[3.3.0]: https://github.com/NicolaiSoeborg/filemanager-plugin/compare/v3.2.0...v3.3.0
+[3.2.0]: https://github.com/NicolaiSoeborg/filemanager-plugin/compare/v3.1.2...v3.2.0
+[3.1.2]: https://github.com/NicolaiSoeborg/filemanager-plugin/compare/v3.1.1...v3.1.2
+[3.1.1]: https://github.com/NicolaiSoeborg/filemanager-plugin/compare/v3.1.0...v3.1.1
+[3.1.0]: https://github.com/NicolaiSoeborg/filemanager-plugin/compare/v3.0.0...v3.1.0
+[3.0.0]: https://github.com/NicolaiSoeborg/filemanager-plugin/compare/v2.1.1...v3.0.0
diff --git a/filemanager-plugin/LICENSE b/filemanager-plugin/LICENSE
new file mode 100644
index 0000000..ae6fb2f
--- /dev/null
+++ b/filemanager-plugin/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 Nicolai Søborg
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/filemanager-plugin/README.md b/filemanager-plugin/README.md
new file mode 100644
index 0000000..79d983d
--- /dev/null
+++ b/filemanager-plugin/README.md
@@ -0,0 +1,56 @@
+# Filemanager Plugin
+
+A simple plugin that allows for easy navigation of a file tree.
+
+![Example picture](./example.jpg?raw=true "Example")
+
+**Installation:** run `plugin install filemanager` and restart Micro.
+
+## Basics
+
+The top line always has the current directory's path to show you where you are.\
+The `..` near the top is used to move back a directory, from your current position.
+
+All directories have a `/` added to the end of it, and are syntax-highlighted as a `special` character.\
+If the directory is expanded, there will be a `+` to the left of it. If it is collapsed there will be a `-` instead.
+
+**NOTE:** If you change files without using the plugin, it can't know what you did. The only fix is to close and open the tree.
+
+### Options
+
+| Option | Purpose | Default |
+| :--------------------------- | :----------------------------------------------------------- | :------ |
+| `filemanager-showdotfiles` | Show dotfiles (hidden if false) | `true` |
+| `filemanager-showignored` | Show gitignore'd files (hidden if false) | `true` |
+| `filemanager-compressparent` | Collapse the parent dir when left is pressed on a child file | `true` |
+| `filemanager-foldersfirst` | Sorts folders above any files | `true` |
+| `filemanager-openonstart` | Automatically open the file tree when starting Micro | `false` |
+
+### Commands and Keybindings
+
+The keybindings below are the equivalent to Micro's defaults, and not actually set by the plugin. If you've changed any of those keybindings, then that key is used instead.
+
+If you want to [keybind](https://github.com/zyedidia/micro/blob/master/runtime/help/keybindings.md#rebinding-keys) any of the operations/commands, bind to the labeled API in the table below.
+
+| Command | Keybinding(s) | What it does | API for `bindings.json` |
+| :------- | :------------------------- | :------------------------------------------------------------------------------------------ | :------------------------------------ |
+| `tree` | - | Open/close the tree | `filemanager.toggle_tree` |
+| - | Tab & MouseLeft | Open a file, or go into the directory. Goes back a dir if on `..` | `filemanager.try_open_at_cursor` |
+| - | → | Expand directory in tree listing | `filemanager.uncompress_at_cursor` |
+| - | ← | Collapse directory listing | `filemanager.compress_at_cursor` |
+| - | Shift ⬆ | Go to the target's parent directory | `filemanager.goto_parent_dir` |
+| - | Alt Shift { | Jump to the previous directory in the view | `filemanager.goto_next_dir` |
+| - | Alt Shift } | Jump to the next directory in the view | `filemanager.goto_prev_dir` |
+| `rm` | - | Prompt to delete the target file/directory your cursor is on | `filemanager.prompt_delete_at_cursor` |
+| `rename` | - | Rename the file/directory your cursor is on, using the passed name | `filemanager.rename_at_cursor` |
+| `touch` | - | Make a new file under/into the file/directory your cursor is on, using the passed name | `filemanager.new_file` |
+| `mkdir` | - | Make a new directory under/into the file/directory your cursor is on, using the passed name | `filemanager.new_dir` |
+
+#### Notes
+
+- `rename`, `touch`, and `mkdir` require a name to be passed when calling.\
+ Example: `rename newnamehere`, `touch filenamehere`, `mkdir dirnamehere`.\
+ If the passed name already exists in the current dir, it will cancel instead of overwriting (for safety).
+
+- The Ctrl w keybinding is to switch which buffer your cursor is on.\
+ This isn't specific to the plugin, it's just part of Micro, but many people seem to not know this.
diff --git a/filemanager-plugin/example.jpg b/filemanager-plugin/example.jpg
new file mode 100644
index 0000000..ab5bf1c
Binary files /dev/null and b/filemanager-plugin/example.jpg differ
diff --git a/filemanager-plugin/filemanager.lua b/filemanager-plugin/filemanager.lua
new file mode 100644
index 0000000..9f3b4fc
--- /dev/null
+++ b/filemanager-plugin/filemanager.lua
@@ -0,0 +1,1366 @@
+VERSION = "3.5.0"
+
+local micro = import("micro")
+local config = import("micro/config")
+local shell = import("micro/shell")
+local buffer = import("micro/buffer")
+local os = import("os")
+local filepath = import("path/filepath")
+
+-- Clear out all stuff in Micro's messenger
+local function clear_messenger()
+ -- messenger:Reset()
+ -- messenger:Clear()
+end
+
+-- Holds the micro.CurPane() we're manipulating
+local tree_view = nil
+-- Keeps track of the current working directory
+local current_dir = os.Getwd()
+-- Keep track of current highest visible indent to resize width appropriately
+local highest_visible_indent = 0
+-- Holds a table of paths -- objects from new_listobj() calls
+local scanlist = {}
+
+-- Get a new object used when adding to scanlist
+local function new_listobj(p, d, o, i)
+ return {
+ ["abspath"] = p,
+ ["dirmsg"] = d,
+ ["owner"] = o,
+ ["indent"] = i,
+ -- Since decreasing/increasing is common, we include these with the object
+ ["decrease_owner"] = function(self, minus_num)
+ self.owner = self.owner - minus_num
+ end,
+ ["increase_owner"] = function(self, plus_num)
+ self.owner = self.owner + plus_num
+ end
+ }
+end
+
+-- Repeats a string x times, then returns it concatenated into one string
+local function repeat_str(str, len)
+ -- Do NOT try to concat in a loop, it freezes micro...
+ -- instead, use a temporary table to hold values
+ local string_table = {}
+ for i = 1, len do
+ string_table[i] = str
+ end
+ -- Return the single string of repeated characters
+ return table.concat(string_table)
+end
+
+-- A check for if a path is a dir
+local function is_dir(path)
+ -- Used for checking if dir
+ local golib_os = import("os")
+ -- Returns a FileInfo on the current file/path
+ local file_info, stat_error = golib_os.Stat(path)
+ -- Wrap in nil check for file/dirs without read permissions
+ if file_info ~= nil then
+ -- Returns true/false if it's a dir
+ return file_info:IsDir()
+ else
+ -- Couldn't stat the file/dir, usually because no read permissions
+ micro.InfoBar():Error("Error checking if is dir: ", stat_error)
+ -- Nil since we can't read the path
+ return nil
+ end
+end
+
+-- Returns a list of files (in the target dir) that are ignored by the VCS system (if exists)
+-- aka this returns a list of gitignored files (but for whatever VCS is found)
+local function get_ignored_files(tar_dir)
+ -- True/false if the target dir returns a non-fatal error when checked with 'git status'
+ local function has_git()
+ local git_rp_results = RunShellCommand('git -C "' .. tar_dir .. '" rev-parse --is-inside-work-tree')
+ return git_rp_results:match("^true%s*$")
+ end
+ local readout_results = {}
+ -- TODO: Support more than just Git, such as Mercurial or SVN
+ if has_git() then
+ -- If the dir is a git dir, get all ignored in the dir
+ local git_ls_results =
+ RunShellCommand('git -C "' .. tar_dir .. '" ls-files . --ignored --exclude-standard --others --directory')
+ -- Cut off the newline that is at the end of each result
+ for split_results in string.gmatch(git_ls_results, "([^\r\n]+)") do
+ -- git ls-files adds a trailing slash if it's a dir, so we remove it (if it is one)
+ readout_results[#readout_results + 1] =
+ (string.sub(split_results, -1) == "/" and string.sub(split_results, 1, -2) or split_results)
+ end
+ end
+
+ -- Make sure we return a table
+ return readout_results
+end
+
+-- Returns the basename of a path (aka a name without leading path)
+local function get_basename(path)
+ if path == nil then
+ micro.Log("Bad path passed to get_basename")
+ return nil
+ else
+ -- Get Go's path lib for a basename callback
+ local golib_path = import("filepath")
+ return golib_path.Base(path)
+ end
+end
+
+-- Returns true/false if the file is a dotfile
+local function is_dotfile(file_name)
+ -- Check if the filename starts with a dot
+ if string.sub(file_name, 1, 1) == "." then
+ return true
+ else
+ return false
+ end
+end
+
+-- Structures the output of the scanned directory content to be used in the scanlist table
+-- This is useful for both initial creation of the tree, and when nesting with uncompress_target()
+local function get_scanlist(dir, ownership, indent_n)
+ local golib_ioutil = import("ioutil")
+ -- Gets a list of all the files in the current dir
+ local dir_scan, scan_error = golib_ioutil.ReadDir(dir)
+
+ -- dir_scan will be nil if the directory is read-protected (no permissions)
+ if dir_scan == nil then
+ micro.InfoBar():Error("Error scanning dir: ", scan_error)
+ return nil
+ end
+
+ -- The list of files to be returned (and eventually put in the view)
+ local results = {}
+ local files = {}
+
+ local function get_results_object(file_name)
+ local abs_path = filepath.Join(dir, file_name)
+ -- Use "+" for dir's, "" for files
+ local dirmsg = (is_dir(abs_path) and "+" or "")
+ return new_listobj(abs_path, dirmsg, ownership, indent_n)
+ end
+
+ -- Save so we don't have to rerun GetOption a bunch
+ local show_dotfiles = config.GetGlobalOption("filemanager.showdotfiles")
+ local show_ignored = config.GetGlobalOption("filemanager.showignored")
+ local folders_first = config.GetGlobalOption("filemanager.foldersfirst")
+
+ -- The list of VCS-ignored files (if any)
+ -- Only bother getting ignored files if we're not showing ignored
+ local ignored_files = (not show_ignored and get_ignored_files(dir) or {})
+ -- True/false if the file is an ignored file
+ local function is_ignored_file(filename)
+ for i = 1, #ignored_files do
+ if ignored_files[i] == filename then
+ return true
+ end
+ end
+ return false
+ end
+
+ -- Hold the current scan's filename in most of the loops below
+ local filename
+
+ for i = 1, #dir_scan do
+ local showfile = true
+ filename = dir_scan[i]:Name()
+ -- If we should not show dotfiles, and this is a dotfile, don't show
+ if not show_dotfiles and is_dotfile(filename) then
+ showfile = false
+ end
+ -- If we should not show ignored files, and this is an ignored file, don't show
+ if not show_ignored and is_ignored_file(filename) then
+ showfile = false
+ end
+ if showfile then
+ -- This file is good to show, proceed
+ if folders_first and not is_dir(filepath.Join(dir, filename)) then
+ -- If folders_first and this is a file, add it to (temporary) files
+ files[#files + 1] = get_results_object(filename)
+ else
+ -- Otherwise, add to results
+ results[#results + 1] = get_results_object(filename)
+ end
+ end
+ end
+ if #files > 0 then
+ -- Append any files to results, now that all folders have been added
+ -- files will be > 0 only if folders_first and there are files
+ for i = 1, #files do
+ results[#results + 1] = files[i]
+ end
+ end
+
+ -- Return the list of scanned files
+ return results
+end
+
+-- A short "get y" for when acting on the scanlist
+-- Needed since we don't store the first 3 visible indicies in scanlist
+local function get_safe_y(optional_y)
+ -- Default to 0 so we can check against and see if it's bad
+ local y = 0
+ -- Make the passed y optional
+ if optional_y == nil then
+ -- Default to cursor's Y loc if nothing was passed, instead of declaring another y
+ optional_y = tree_view.Cursor.Loc.Y
+ end
+ -- 0/1/2 would be the top "dir, separator, .." so check if it's past
+ if optional_y > 2 then
+ -- -2 to conform to our scanlist, since zero-based Go index & Lua's one-based
+ y = tree_view.Cursor.Loc.Y - 2
+ end
+ return y
+end
+
+-- Joins the target dir's leading path to the passed name
+local function dirname_and_join(path, join_name)
+ -- The leading path to the dir we're in
+ local leading_path = DirectoryName(path)
+ -- Joins with OS-specific slashes
+ return filepath.Join(leading_path, join_name)
+end
+
+-- Hightlights the line when you move the cursor up/down
+local function select_line(last_y)
+ -- Make last_y optional
+ if last_y ~= nil then
+ -- Don't let them move past ".." by checking the result first
+ if last_y > 1 then
+ -- If the last position was valid, move back to it
+ tree_view.Cursor.Loc.Y = last_y
+ end
+ elseif tree_view.Cursor.Loc.Y < 2 then
+ -- Put the cursor on the ".." if it's above it
+ tree_view.Cursor.Loc.Y = 2
+ end
+
+ -- Puts the cursor back in bounds (if it isn't) for safety
+ tree_view.Cursor:Relocate()
+
+ -- Makes sure the cursor is visible (if it isn't)
+ -- (false) means no callback
+ tree_view:Center()
+
+ -- Highlight the current line where the cursor is
+ tree_view.Cursor:SelectLine()
+end
+
+-- Simple true/false if scanlist is currently empty
+local function scanlist_is_empty()
+ if next(scanlist) == nil then
+ return true
+ else
+ return false
+ end
+end
+
+local function refresh_view()
+ clear_messenger()
+
+ -- If it's less than 30, just use 30 for width. Don't want it too small
+
+ if tree_view:GetView().Width < 30 then
+ tree_view:ResizePane(30)
+ end
+
+ -- Delete everything in the view/buffer
+ tree_view.Buf.EventHandler:Remove(tree_view.Buf:Start(), tree_view.Buf:End())
+
+ -- Insert the top 3 things that are always there
+ -- Current dir
+ tree_view.Buf.EventHandler:Insert(buffer.Loc(0, 0), current_dir .. "\n")
+ -- An ASCII separator
+ tree_view.Buf.EventHandler:Insert(buffer.Loc(0, 1), repeat_str("─", tree_view:GetView().Width) .. "\n")
+ -- The ".." and use a newline if there are things in the current dir
+ tree_view.Buf.EventHandler:Insert(buffer.Loc(0, 2), (#scanlist > 0 and "..\n" or ".."))
+
+ -- Holds the current basename of the path (purely for display)
+ local display_content
+
+ -- NOTE: might want to not do all these concats in the loop, it can get slow
+ for i = 1, #scanlist do
+ -- The first 3 indicies are the dir/separator/"..", so skip them
+ if scanlist[i].dirmsg ~= "" then
+ -- Add the + or - to the left to signify if it's compressed or not
+ -- Add a forward slash to the right to signify it's a dir
+ display_content = scanlist[i].dirmsg .. " " .. get_basename(scanlist[i].abspath) .. "/"
+ else
+ -- Use the basename from the full path for display
+ -- Two spaces to align with any directories, instead of being "off"
+ display_content = " " .. get_basename(scanlist[i].abspath)
+ end
+
+ if scanlist[i].owner > 0 then
+ -- Add a space and repeat it * the indent number
+ display_content = repeat_str(" ", 2 * scanlist[i].indent) .. display_content
+ end
+
+ -- Newlines are needed for all inserts except the last
+ -- If you insert a newline on the last, it leaves a blank spot at the bottom
+ if i < #scanlist then
+ display_content = display_content .. "\n"
+ end
+
+ -- Insert line-by-line to avoid out-of-bounds on big folders
+ -- +2 so we skip the 0/1/2 positions that hold the top dir/separator/..
+ tree_view.Buf.EventHandler:Insert(buffer.Loc(0, i + 2), display_content)
+ end
+
+ -- Resizes all views after messing with ours
+ tree_view:Tab():Resize()
+end
+
+-- Moves the cursor to the ".." in tree_view
+local function move_cursor_top()
+ -- 2 is the position of the ".."
+ tree_view.Cursor.Loc.Y = 2
+
+ -- select the line after moving
+ select_line()
+end
+
+local function refresh_and_select()
+ -- Save the cursor position before messing with the view..
+ -- because changing contents in the view causes the Y loc to move
+ local last_y = tree_view.Cursor.Loc.Y
+ -- Actually refresh
+ refresh_view()
+ -- Moves the cursor back to it's original position
+ select_line(last_y)
+end
+
+-- Find everything nested under the target, and remove it from the scanlist
+local function compress_target(y, delete_y)
+ -- Can't compress the top stuff, or if there's nothing there, so exit early
+ if y == 0 or scanlist_is_empty() then
+ return
+ end
+ -- Check if the target is a dir, since files don't have anything to compress
+ -- Also make sure it's actually an uncompressed dir by checking the gutter message
+ if scanlist[y].dirmsg == "-" then
+ local target_index, delete_index
+ -- Add the original target y to stuff to delete
+ local delete_under = {[1] = y}
+ local new_table = {}
+ local del_count = 0
+ -- Loop through the whole table, looking for nested content, or stuff with ownership == y...
+ -- and delete matches. y+1 because we want to start under y, without actually touching y itself.
+ for i = 1, #scanlist do
+ delete_index = false
+ -- Don't run on y, since we don't always delete y
+ if i ~= y then
+ -- On each loop, check if the ownership matches
+ for x = 1, #delete_under do
+ -- Check for something belonging to a thing to delete
+ if scanlist[i].owner == delete_under[x] then
+ -- Delete the target if it has an ownership to our delete target
+ delete_index = true
+ -- Keep count of total deleted (can't use #delete_under because it's for deleted dir count)
+ del_count = del_count + 1
+ -- Check if an uncompressed dir
+ if scanlist[i].dirmsg == "-" then
+ -- Add the index to stuff to delete, since it holds nested content
+ delete_under[#delete_under + 1] = i
+ end
+ -- See if we're on the "deepest" nested content
+ if scanlist[i].indent == highest_visible_indent and scanlist[i].indent > 0 then
+ -- Save the lower indent, since we're minimizing/deleting nested dirs
+ highest_visible_indent = highest_visible_indent - 1
+ end
+ -- Nothing else to do, so break this inner loop
+ break
+ end
+ end
+ end
+ if not delete_index then
+ -- Save the index in our new table
+ new_table[#new_table + 1] = scanlist[i]
+ end
+ end
+
+ scanlist = new_table
+
+ if del_count > 0 then
+ -- Ownership adjusting since we're deleting an index
+ for i = y + 1, #scanlist do
+ -- Don't touch root file/dirs
+ if scanlist[i].owner > y then
+ -- Minus ownership, on everything below i, the number deleted
+ scanlist[i]:decrease_owner(del_count)
+ end
+ end
+ end
+
+ -- If not deleting, then update the gutter message to be + to signify compressed
+ if not delete_y then
+ -- Update the dir message
+ scanlist[y].dirmsg = "+"
+ end
+ elseif config.GetGlobalOption("filemanager.compressparent") and not delete_y then
+ goto_parent_dir()
+ -- Prevent a pointless refresh of the view
+ return
+ end
+
+ -- Put outside check above because we call this to delete targets as well
+ if delete_y then
+ local second_table = {}
+ -- Quickly remove y
+ for i = 1, #scanlist do
+ if i == y then
+ -- Reduce everything's ownership by 1 after y
+ for x = i + 1, #scanlist do
+ -- Don't touch root file/dirs
+ if scanlist[x].owner > y then
+ -- Minus 1 since we're just deleting y
+ scanlist[x]:decrease_owner(1)
+ end
+ end
+ else
+ -- Put everything but y into the temporary table
+ second_table[#second_table + 1] = scanlist[i]
+ end
+ end
+ -- Put everything (but y) back into scanlist, with adjusted ownership values
+ scanlist = second_table
+ end
+
+ if tree_view:GetView().Width > (30 + highest_visible_indent) then
+ -- Shave off some width
+ tree_view:ResizePane(30 + highest_visible_indent)
+ end
+
+ refresh_and_select()
+end
+
+-- Prompts the user for deletion of a file/dir when triggered
+-- Not local so Micro can access it
+function prompt_delete_at_cursor()
+ local y = get_safe_y()
+ -- Don't let them delete the top 3 index dir/separator/..
+ if y == 0 or scanlist_is_empty() then
+ micro.InfoBar():Error("You can't delete that")
+ -- Exit early if there's nothing to delete
+ return
+ end
+
+ micro.InfoBar():YNPrompt("Do you want to delete the " .. (scanlist[y].dirmsg ~= "" and "dir" or "file") .. ' "' .. scanlist[y].abspath .. '"? ', function(yes, canceled)
+ if yes and not canceled then
+ -- Use Go's os.Remove to delete the file
+ local go_os = import("os")
+ -- Delete the target (if its a dir then the children too)
+ local remove_log = go_os.RemoveAll(scanlist[y].abspath)
+ if remove_log == nil then
+ micro.InfoBar():Message("Filemanager deleted: ", scanlist[y].abspath)
+ -- Remove the target (and all nested) from scanlist[y + 1]
+ -- true to delete y
+ compress_target(get_safe_y(), true)
+ else
+ micro.InfoBar():Error("Failed deleting file/dir: ", remove_log)
+ end
+ else
+ micro.InfoBar():Message("Nothing was deleted")
+ end
+ end)
+end
+
+-- Changes the current dir in the top of the tree..
+-- then scans that dir, and prints it to the view
+local function update_current_dir(path)
+ -- Clear the highest since this is a full refresh
+ highest_visible_indent = 0
+ -- Set the width back to 30
+ tree_view:ResizePane(30)
+ -- Update the current dir to the new path
+ current_dir = path
+
+ -- Get the current working dir's files into our list of files
+ -- 0 ownership because this is a scan of the base dir
+ -- 0 indent because this is the base dir
+ local scan_results = get_scanlist(path, 0, 0)
+ -- Safety check with not-nil
+ if scan_results ~= nil then
+ -- Put in the new scan stuff
+ scanlist = scan_results
+ else
+ -- If nil, just empty it
+ scanlist = {}
+ end
+
+ refresh_view()
+ -- Since we're going into a new dir, move cursor to the ".." by default
+ move_cursor_top()
+end
+
+-- (Tries to) go back one "step" from the current directory
+local function go_back_dir()
+ -- Use Micro's dirname to get everything but the current dir's path
+ local one_back_dir = filepath.Dir(current_dir)
+ -- Try opening, assuming they aren't at "root", by checking if it matches last dir
+ if one_back_dir ~= current_dir then
+ -- If DirectoryName returns different, then they can move back..
+ -- so we update the current dir and refresh
+ update_current_dir(one_back_dir)
+ end
+end
+
+-- Tries to open the current index
+-- If it's the top dir indicator, or separator, nothing happens
+-- If it's ".." then it tries to go back a dir
+-- If it's a dir then it moves into the dir and refreshes
+-- If it's actually a file, open it in a new vsplit
+-- THIS EXPECTS ZERO-BASED Y
+local function try_open_at_y(y)
+ -- 2 is the zero-based index of ".."
+ if y == 2 then
+ go_back_dir()
+ elseif y > 2 and not scanlist_is_empty() then
+ -- -2 to conform to our scanlist "missing" first 3 indicies
+ y = y - 2
+ if scanlist[y].dirmsg ~= "" then
+ -- if passed path is a directory, update the current dir to be one deeper..
+ update_current_dir(scanlist[y].abspath)
+ else
+ -- If it's a file, then open it
+ micro.InfoBar():Message("Filemanager opened ", scanlist[y].abspath)
+ -- Opens the absolute path in new vertical view
+ micro.CurPane():VSplitIndex(buffer.NewBufferFromFile(scanlist[y].abspath), true)
+ -- Resizes all views after opening a file
+ -- tabs[curTab + 1]:Resize()
+ end
+ else
+ micro.InfoBar():Error("Can't open that")
+ end
+end
+
+-- Opens the dir's contents nested under itself
+local function uncompress_target(y)
+ -- Exit early if on the top 3 non-list items
+ if y == 0 or scanlist_is_empty() then
+ return
+ end
+ -- Only uncompress if it's a dir and it's not already uncompressed
+ if scanlist[y].dirmsg == "+" then
+ -- Get a new scanlist with results from the scan in the target dir
+ local scan_results = get_scanlist(scanlist[y].abspath, y, scanlist[y].indent + 1)
+ -- Don't run any of this if there's nothing in the dir we scanned, pointless
+ if scan_results ~= nil then
+ -- Will hold all the old values + new scan results
+ local new_table = {}
+ -- By not inserting in-place, some unexpected results can be avoided
+ -- Also, table.insert actually moves values up (???) instead of down
+ for i = 1, #scanlist do
+ -- Put the current val into our new table
+ new_table[#new_table + 1] = scanlist[i]
+ if i == y then
+ -- Fill in the scan results under y
+ for x = 1, #scan_results do
+ new_table[#new_table + 1] = scan_results[x]
+ end
+ -- Basically "moving down" everything below y, so ownership needs to increase on everything
+ for inner_i = y + 1, #scanlist do
+ -- When root not pushed by inserting, don't change its ownership
+ -- This also has a dual-purpose to make it not effect root file/dirs
+ -- since y is always >= 3
+ if scanlist[inner_i].owner > y then
+ -- Increase each indicies ownership by the number of scan results inserted
+ scanlist[inner_i]:increase_owner(#scan_results)
+ end
+ end
+ end
+ end
+
+ -- Update our scanlist with the new values
+ scanlist = new_table
+ end
+
+ -- Change to minus to signify it's uncompressed
+ scanlist[y].dirmsg = "-"
+
+ -- Check if we actually need to resize, or if we're nesting at the same indent
+ -- Also check if there's anything in the dir, as we don't need to expand on an empty dir
+ if scan_results ~= nil then
+ if scanlist[y].indent > highest_visible_indent and #scan_results >= 1 then
+ -- Save the new highest indent
+ highest_visible_indent = scanlist[y].indent
+ -- Increase the width to fit the new nested content
+ tree_view:ResizePane(tree_view:GetView().Width + scanlist[y].indent)
+ end
+ end
+
+ refresh_and_select()
+ end
+end
+
+-- Stat a path to check if it exists, returning true/false
+local function path_exists(path)
+ local go_os = import("os")
+ -- Stat the file/dir path we created
+ -- file_stat should be non-nil, and stat_err should be nil on success
+ local file_stat, stat_err = go_os.Stat(path)
+ -- Check if what we tried to create exists
+ if stat_err ~= nil then
+ -- true/false if the file/dir exists
+ return go_os.IsExist(stat_err)
+ elseif file_stat ~= nil then
+ -- Assume it exists if no errors
+ return true
+ end
+ return false
+end
+
+-- Prompts for a new name, then renames the file/dir at the cursor's position
+-- Not local so Micro can use it
+function rename_at_cursor(new_name)
+ if micro.CurPane() ~= tree_view then
+ micro.InfoBar():Message("Rename only works with the cursor in the tree!")
+ return
+ end
+
+ -- Safety check they actually passed a name
+ if new_name == nil then
+ micro.InfoBar():Error('When using "rename" you need to input a new name')
+ return
+ end
+
+ -- +1 since Go uses zero-based indices
+ local y = get_safe_y()
+ -- Check if they're trying to rename the top stuff
+ if y == 0 then
+ -- Error since they tried to rename the top stuff
+ micro.InfoBar():Message("You can't rename that!")
+ return
+ end
+
+ -- The old file/dir's path
+ local old_path = scanlist[y].abspath
+ -- Join the path into their supplied rename, so that we have an absolute path
+ local new_path = dirname_and_join(old_path, new_name)
+ -- Use Go's os package for renaming the file/dir
+ local golib_os = import("os")
+ -- Actually rename the file
+ local log_out = golib_os.Rename(old_path, new_path)
+ -- Output the log, if any, of the rename
+ if log_out ~= nil then
+ micro.Log("Rename log: ", log_out)
+ end
+
+ -- Check if the rename worked
+ if not path_exists(new_path) then
+ micro.InfoBar():Error("Path doesn't exist after rename!")
+ return
+ end
+
+ -- NOTE: doesn't alphabetically sort after refresh, but it probably should
+ -- Replace the old path with the new path
+ scanlist[y].abspath = new_path
+ -- Refresh the tree with our new name
+ refresh_and_select()
+end
+
+-- Prompts the user for the file/dir name, then creates the file/dir using Go's os package
+local function create_filedir(filedir_name, make_dir)
+ if micro.CurPane() ~= tree_view then
+ micro.InfoBar():Message("You can't create a file/dir if your cursor isn't in the tree!")
+ return
+ end
+
+ -- Safety check they passed a name
+ if filedir_name == nil then
+ micro.InfoBar():Error('You need to input a name when using "touch" or "mkdir"!')
+ return
+ end
+
+ -- The target they're trying to create on top of/in/at/whatever
+ local y = get_safe_y()
+ -- Holds the path passed to Go for the eventual new file/dir
+ local filedir_path
+ -- A true/false if scanlist is empty
+ local scanlist_empty = scanlist_is_empty()
+
+ -- Check there's actually anything in the list, and that they're not on the ".."
+ if not scanlist_empty and y ~= 0 then
+ -- If they're inserting on a folder, don't strip its path
+ if scanlist[y].dirmsg ~= "" then
+ -- Join our new file/dir onto the dir
+ filedir_path = filepath.Join(scanlist[y].abspath, filedir_name)
+ else
+ -- The current index is a file, so strip its name and join ours onto it
+ filedir_path = dirname_and_join(scanlist[y].abspath, filedir_name)
+ end
+ else
+ -- if nothing in the list, or cursor is on top of "..", use the current dir
+ filedir_path = filepath.Join(current_dir, filedir_name)
+ end
+
+ -- Check if the name is already taken by a file/dir
+ if path_exists(filedir_path) then
+ micro.InfoBar():Error("You can't create a file/dir with a pre-existing name")
+ return
+ end
+
+ -- Use Go's os package for creating the files
+ local golib_os = import("os")
+ -- Create the dir or file
+ if make_dir then
+ -- Creates the dir
+ golib_os.Mkdir(filedir_path, golib_os.ModePerm)
+ micro.Log("Filemanager created directory: " .. filedir_path)
+ else
+ -- Creates the file
+ golib_os.Create(filedir_path)
+ micro.Log("Filemanager created file: " .. filedir_path)
+ end
+
+ -- If the file we tried to make doesn't exist, fail
+ if not path_exists(filedir_path) then
+ micro.InfoBar():Error("The file/dir creation failed")
+
+ return
+ end
+
+ -- Creates a sort of default object, to be modified below
+ -- If creating a dir, use a "+"
+ local new_filedir = new_listobj(filedir_path, (make_dir and "+" or ""), 0, 0)
+
+ -- Refresh with our new value(s)
+ local last_y
+
+ -- Only insert to scanlist if not created into a compressed dir, since it'd be hidden if it was
+ -- Wrap the below checks so a y=0 doesn't break something
+ if not scanlist_empty and y ~= 0 then
+ -- +1 so it's highlighting the new file/dir
+ last_y = tree_view.Cursor.Loc.Y + 1
+
+ -- Only actually add the object to the list if it's not created on an uncompressed folder
+ if scanlist[y].dirmsg == "+" then
+ -- Exit early, since it was created into an uncompressed folder
+
+ return
+ elseif scanlist[y].dirmsg == "-" then
+ -- Check if created on top of an uncompressed folder
+ -- Change ownership to the folder it was created on top of..
+ -- otherwise, the ownership would be incorrect
+ new_filedir.owner = y
+ -- We insert under the folder, so increment the indent
+ new_filedir.indent = scanlist[y].indent + 1
+ else
+ -- This triggers if the cursor is on top of a file...
+ -- so we copy the properties of it
+ new_filedir.owner = scanlist[y].owner
+ new_filedir.indent = scanlist[y].indent
+ end
+
+ -- A temporary table for adding our new object, and manipulation
+ local new_table = {}
+ -- Insert the new file/dir, and update ownership of everything below it
+ for i = 1, #scanlist do
+ -- Don't use i as index, as it will be off by one on the next pass after below "i == y"
+ new_table[#new_table + 1] = scanlist[i]
+ if i == y then
+ -- Insert our new file/dir (below the last item)
+ new_table[#new_table + 1] = new_filedir
+ -- Increase ownership of everything below it, since we're inserting
+ -- Basically "moving down" everything below y, so ownership needs to increase on everything
+ for inner_i = y + 1, #scanlist do
+ -- When root not pushed by inserting, don't change its ownership
+ -- This also has a dual-purpose to make it not effect root file/dirs
+ -- since y is always >= 3
+ if scanlist[inner_i].owner > y then
+ -- Increase each indicies ownership by 1 since we're only inserting 1 file/dir
+ scanlist[inner_i]:increase_owner(1)
+ end
+ end
+ end
+ end
+ -- Update the scanlist with the new object & updated ownerships
+ scanlist = new_table
+ else
+ -- The scanlist is empty (or cursor is on ".."), so we add on our new file/dir at the bottom
+ scanlist[#scanlist + 1] = new_filedir
+ -- Add current position so it takes into account where we are
+ last_y = #scanlist + tree_view.Cursor.Loc.Y
+ end
+
+ refresh_view()
+ select_line(last_y)
+end
+
+-- Triggered with "touch filename"
+function new_file(input_name)
+ -- False because not a dir
+ create_filedir(input_name, false)
+end
+
+-- Triggered with "mkdir dirname"
+function new_dir(input_name)
+ -- True because dir
+ create_filedir(input_name, true)
+end
+
+-- open_tree setup's the view
+local function open_tree()
+ -- Open a new Vsplit (on the very left)
+ micro.CurPane():VSplitIndex(buffer.NewBuffer("", "filemanager"), false)
+ -- Save the new view so we can access it later
+ tree_view = micro.CurPane()
+
+ -- Set the width of tree_view to 30% & lock it
+ tree_view:ResizePane(30)
+ -- Set the type to unsavable
+ -- tree_view.Buf.Type = buffer.BTLog
+ tree_view.Buf.Type.Scratch = true
+ tree_view.Buf.Type.Readonly = true
+
+ -- Set the various display settings, but only on our view (by using SetLocalOption instead of SetOption)
+ -- NOTE: Micro requires the true/false to be a string
+ -- Softwrap long strings (the file/dir paths)
+ tree_view.Buf:SetOptionNative("softwrap", true)
+ -- No line numbering
+ tree_view.Buf:SetOptionNative("ruler", false)
+ -- Is this needed with new non-savable settings from being "vtLog"?
+ tree_view.Buf:SetOptionNative("autosave", false)
+ -- Don't show the statusline to differentiate the view from normal views
+ tree_view.Buf:SetOptionNative("statusformatr", "")
+ tree_view.Buf:SetOptionNative("statusformatl", "filemanager")
+ tree_view.Buf:SetOptionNative("scrollbar", false)
+
+ -- Fill the scanlist, and then print its contents to tree_view
+ update_current_dir(os.Getwd())
+end
+
+-- close_tree will close the tree plugin view and release memory.
+local function close_tree()
+ if tree_view ~= nil then
+ tree_view:Quit()
+ tree_view = nil
+ clear_messenger()
+ end
+end
+
+-- toggle_tree will toggle the tree view visible (create) and hide (delete).
+function toggle_tree()
+ if tree_view == nil then
+ open_tree()
+ else
+ close_tree()
+ end
+end
+
+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+-- Functions exposed specifically for the user to bind
+-- Some are used in callbacks as well
+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+function uncompress_at_cursor()
+ if micro.CurPane() == tree_view then
+ uncompress_target(get_safe_y())
+ end
+end
+
+function compress_at_cursor()
+ if micro.CurPane() == tree_view then
+ -- False to not delete y
+ compress_target(get_safe_y(), false)
+ end
+end
+
+-- Goes up 1 visible directory (if any)
+-- Not local so it can be bound
+function goto_prev_dir()
+ if micro.CurPane() ~= tree_view or scanlist_is_empty() then
+ return
+ end
+
+ local cur_y = get_safe_y()
+ -- If they try to run it on the ".." do nothing
+ if cur_y ~= 0 then
+ local move_count = 0
+ for i = cur_y - 1, 1, -1 do
+ move_count = move_count + 1
+ -- If a dir, stop counting
+ if scanlist[i].dirmsg ~= "" then
+ -- Jump to its parent (the ownership)
+ tree_view.Cursor:UpN(move_count)
+ select_line()
+ break
+ end
+ end
+ end
+end
+
+-- Goes down 1 visible directory (if any)
+-- Not local so it can be bound
+function goto_next_dir()
+ if micro.CurPane() ~= tree_view or scanlist_is_empty() then
+ return
+ end
+
+ local cur_y = get_safe_y()
+ local move_count = 0
+ -- If they try to goto_next on "..", pretends the cursor is valid
+ if cur_y == 0 then
+ cur_y = 1
+ move_count = 1
+ end
+ -- Only do anything if it's even possible for there to be another dir
+ if cur_y < #scanlist then
+ for i = cur_y + 1, #scanlist do
+ move_count = move_count + 1
+ -- If a dir, stop counting
+ if scanlist[i].dirmsg ~= "" then
+ -- Jump to its parent (the ownership)
+ tree_view.Cursor:DownN(move_count)
+ select_line()
+ break
+ end
+ end
+ end
+end
+
+-- Goes to the parent directory (if any)
+-- Not local so it can be keybound
+function goto_parent_dir()
+ if micro.CurPane() ~= tree_view or scanlist_is_empty() then
+ return
+ end
+
+ local cur_y = get_safe_y()
+ -- Check if the cursor is even in a valid location for jumping to the owner
+ if cur_y > 0 then
+ -- Jump to its parent (the ownership)
+ tree_view.Cursor:UpN(cur_y - scanlist[cur_y].owner)
+ select_line()
+ end
+end
+
+function try_open_at_cursor()
+ if micro.CurPane() ~= tree_view or scanlist_is_empty() then
+ return
+ end
+
+ try_open_at_y(tree_view.Cursor.Loc.Y)
+end
+
+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+-- Shorthand functions for actions to reduce repeat code
+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+-- Used to fail certain actions that we shouldn't allow on the tree_view
+local function false_if_tree(view)
+ if view == tree_view then
+ return false
+ end
+end
+
+-- Select the line at the cursor
+local function selectline_if_tree(view)
+ if view == tree_view then
+ select_line()
+ end
+end
+
+-- Move the cursor to the top, but don't allow the action
+local function aftermove_if_tree(view)
+ if view == tree_view then
+ if tree_view.Cursor.Loc.Y < 2 then
+ -- If it went past the "..", move back onto it
+ tree_view.Cursor:DownN(2 - tree_view.Cursor.Loc.Y)
+ end
+ select_line()
+ end
+end
+
+local function clearselection_if_tree(view)
+ if view == tree_view then
+ -- Clear the selection when doing a find, so it doesn't copy the current line
+ tree_view.Cursor:ResetSelection()
+ end
+end
+
+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+-- All the events for certain Micro keys go below here
+-- Other than things we flat-out fail
+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+-- Close current
+function preQuit(view)
+ if view == tree_view then
+ -- A fake quit function
+ close_tree()
+ -- Don't actually "quit", otherwise it closes everything without saving for some reason
+ return false
+ end
+end
+
+-- Close all
+function preQuitAll(view)
+ close_tree()
+end
+
+-- FIXME: Workaround for the weird 2-index movement on cursordown
+function preCursorDown(view)
+ if view == tree_view then
+ tree_view.Cursor:Down()
+ select_line()
+ -- Don't actually go down, as it moves 2 indicies for some reason
+ return false
+ end
+end
+
+-- Up
+function onCursorUp(view)
+ selectline_if_tree(view)
+end
+
+-- Alt-Shift-{
+-- Go to target's parent directory (if exists)
+function preParagraphPrevious(view)
+ if view == tree_view then
+ goto_prev_dir()
+ -- Don't actually do the action
+ return false
+ end
+end
+
+-- Alt-Shift-}
+-- Go to next dir (if exists)
+function preParagraphNext(view)
+ if view == tree_view then
+ goto_next_dir()
+ -- Don't actually do the action
+ return false
+ end
+end
+
+-- PageUp
+function onCursorPageUp(view)
+ aftermove_if_tree(view)
+end
+
+-- Ctrl-Up
+function onCursorStart(view)
+ aftermove_if_tree(view)
+end
+
+-- PageDown
+function onCursorPageDown(view)
+ selectline_if_tree(view)
+end
+
+-- Ctrl-Down
+function onCursorEnd(view)
+ selectline_if_tree(view)
+end
+
+function onNextSplit(view)
+ selectline_if_tree(view)
+end
+
+function onPreviousSplit(view)
+ selectline_if_tree(view)
+end
+
+-- On click, open at the click's y
+function preMousePress(view, event)
+ if view == tree_view then
+ local x, y = event:Position()
+ -- Fixes the y because softwrap messes with it
+ local new_x, new_y = tree_view:GetMouseClickLocation(x, y)
+ -- Try to open whatever is at the click's y index
+ -- Will go into/back dirs based on what's clicked, nothing gets expanded
+ try_open_at_y(new_y)
+ -- Don't actually allow the mousepress to trigger, so we avoid highlighting stuff
+ return false
+ end
+end
+
+-- Up
+function preCursorUp(view)
+ if view == tree_view then
+ -- Disallow selecting past the ".." in the tree
+ if tree_view.Cursor.Loc.Y == 2 then
+ return false
+ end
+ end
+end
+
+-- Left
+function preCursorLeft(view)
+ if view == tree_view then
+ -- +1 because of Go's zero-based index
+ -- False to not delete y
+ compress_target(get_safe_y(), false)
+ -- Don't actually move the cursor, as it messes with selection
+ return false
+ end
+end
+
+-- Right
+function preCursorRight(view)
+ if view == tree_view then
+ -- +1 because of Go's zero-based index
+ uncompress_target(get_safe_y())
+ -- Don't actually move the cursor, as it messes with selection
+ return false
+ end
+end
+
+-- Workaround for tab getting inserted into opened files
+-- Ref https://github.com/zyedidia/micro/issues/992
+local tab_pressed = false
+
+-- Tab
+function preIndentSelection(view)
+ if view == tree_view then
+ tab_pressed = true
+ -- Open the file
+ -- Using tab instead of enter, since enter won't work with Readonly
+ try_open_at_y(tree_view.Cursor.Loc.Y)
+ -- Don't actually insert a tab
+ return false
+ end
+end
+
+-- Workaround for tab getting inserted into opened files
+-- Ref https://github.com/zyedidia/micro/issues/992
+function preInsertTab(view)
+ if tab_pressed then
+ tab_pressed = false
+ return false
+ end
+end
+function preInsertNewline(view)
+ if view == tree_view then
+ return false
+ end
+ return true
+end
+-- CtrlL
+function onJumpLine(view)
+ -- Highlight the line after jumping to it
+ -- Also moves you to index 3 (2 in zero-base) if you went to the first 2 lines
+ aftermove_if_tree(view)
+end
+
+-- ShiftUp
+function preSelectUp(view)
+ if view == tree_view then
+ -- Go to the file/dir's parent dir (if any)
+ goto_parent_dir()
+ -- Don't actually selectup
+ return false
+ end
+end
+
+-- CtrlF
+function preFind(view)
+ -- Since something is always selected, clear before a find
+ -- Prevents copying the selection into the find input
+ clearselection_if_tree(view)
+end
+
+-- FIXME: doesn't work for whatever reason
+function onFind(view)
+ -- Select the whole line after a find, instead of just the input txt
+ selectline_if_tree(view)
+end
+
+-- CtrlN after CtrlF
+function onFindNext(view)
+ selectline_if_tree(view)
+end
+
+-- CtrlP after CtrlF
+function onFindPrevious(view)
+ selectline_if_tree(view)
+end
+
+-- NOTE: This is a workaround for "cd" not having its own callback
+local precmd_dir
+
+function preCommandMode(view)
+ precmd_dir = os.Getwd()
+end
+
+-- Update the current dir when using "cd"
+function onCommandMode(view)
+ local new_dir = os.Getwd()
+ -- Only do anything if the tree is open, and they didn't cd to nothing
+ if tree_view ~= nil and new_dir ~= precmd_dir and new_dir ~= current_dir then
+ update_current_dir(new_dir)
+ end
+end
+
+------------------------------------------------------------------
+-- Fail a bunch of useless actions
+-- Some of these need to be removed (read-only makes some useless)
+------------------------------------------------------------------
+
+function preStartOfLine(view)
+ return false_if_tree(view)
+end
+
+function preStartOfText(view)
+ return false_if_tree(view)
+end
+
+function preEndOfLine(view)
+ return false_if_tree(view)
+end
+
+function preMoveLinesDown(view)
+ return false_if_tree(view)
+end
+
+function preMoveLinesUp(view)
+ return false_if_tree(view)
+end
+
+function preWordRight(view)
+ return false_if_tree(view)
+end
+
+function preWordLeft(view)
+ return false_if_tree(view)
+end
+
+function preSelectDown(view)
+ return false_if_tree(view)
+end
+
+function preSelectLeft(view)
+ return false_if_tree(view)
+end
+
+function preSelectRight(view)
+ return false_if_tree(view)
+end
+
+function preSelectWordRight(view)
+ return false_if_tree(view)
+end
+
+function preSelectWordLeft(view)
+ return false_if_tree(view)
+end
+
+function preSelectToStartOfLine(view)
+ return false_if_tree(view)
+end
+
+function preSelectToStartOfText(view)
+ return false_if_tree(view)
+end
+
+function preSelectToEndOfLine(view)
+ return false_if_tree(view)
+end
+
+function preSelectToStart(view)
+ return false_if_tree(view)
+end
+
+function preSelectToEnd(view)
+ return false_if_tree(view)
+end
+
+function preDeleteWordLeft(view)
+ return false_if_tree(view)
+end
+
+function preDeleteWordRight(view)
+ return false_if_tree(view)
+end
+
+function preOutdentSelection(view)
+ return false_if_tree(view)
+end
+
+function preOutdentLine(view)
+ return false_if_tree(view)
+end
+
+function preSave(view)
+ return false_if_tree(view)
+end
+
+function preCut(view)
+ return false_if_tree(view)
+end
+
+function preCutLine(view)
+ return false_if_tree(view)
+end
+
+function preDuplicateLine(view)
+ return false_if_tree(view)
+end
+
+function prePaste(view)
+ return false_if_tree(view)
+end
+
+function prePastePrimary(view)
+ return false_if_tree(view)
+end
+
+function preMouseMultiCursor(view)
+ return false_if_tree(view)
+end
+
+function preSpawnMultiCursor(view)
+ return false_if_tree(view)
+end
+
+function preSelectAll(view)
+ return false_if_tree(view)
+end
+
+function init()
+ -- Let the user disable showing of dotfiles like ".editorconfig" or ".DS_STORE"
+ config.RegisterCommonOption("filemanager", "showdotfiles", true)
+ -- Let the user disable showing files ignored by the VCS (i.e. gitignored)
+ config.RegisterCommonOption("filemanager", "showignored", true)
+ -- Let the user disable going to parent directory via left arrow key when file selected (not directory)
+ config.RegisterCommonOption("filemanager", "compressparent", true)
+ -- Let the user choose to list sub-folders first when listing the contents of a folder
+ config.RegisterCommonOption("filemanager", "foldersfirst", true)
+ -- Lets the user have the filetree auto-open any time Micro is opened
+ -- false by default, as it's a rather noticable user-facing change
+ config.RegisterCommonOption("filemanager", "openonstart", false)
+
+ -- Open/close the tree view
+ config.MakeCommand("tree", toggle_tree, config.NoComplete)
+ -- Rename the file/dir under the cursor
+ config.MakeCommand("rename", rename_at_cursor, config.NoComplete)
+ -- Create a new file
+ config.MakeCommand("touch", new_file, config.NoComplete)
+ -- Create a new dir
+ config.MakeCommand("mkdir", new_dir, config.NoComplete)
+ -- Delete a file/dir, and anything contained in it if it's a dir
+ config.MakeCommand("rm", prompt_delete_at_cursor, config.NoComplete)
+ -- Adds colors to the ".." and any dir's in the tree view via syntax highlighting
+ -- TODO: Change it to work with git, based on untracked/changed/added/whatever
+ config.AddRuntimeFile("filemanager", config.RTSyntax, "syntax.yaml")
+
+ -- NOTE: This must be below the syntax load command or coloring won't work
+ -- Just auto-open if the option is enabled
+ -- This will run when the plugin first loads
+ if config.GetGlobalOption("filemanager.openonstart") then
+ -- Check for safety on the off-chance someone's init.lua breaks this
+ if tree_view == nil then
+ open_tree()
+ -- Puts the cursor back in the empty view that initially spawns
+ -- This is so the cursor isn't sitting in the tree view at startup
+ micro.CurPane():NextSplit()
+ else
+ -- Log error so they can fix it
+ micro.Log(
+ "Warning: filemanager.openonstart was enabled, but somehow the tree was already open so the option was ignored."
+ )
+ end
+ end
+end
diff --git a/filemanager-plugin/repo.json b/filemanager-plugin/repo.json
new file mode 100644
index 0000000..eb814d4
--- /dev/null
+++ b/filemanager-plugin/repo.json
@@ -0,0 +1,38 @@
+[
+ {
+ "Name": "filemanager",
+ "Description": "File manager for Micro",
+ "Tags": ["filetree", "filemanager", "file", "manager"],
+ "Website": "",
+ "Versions": [
+ {
+ "Version": "2.1.1",
+ "Url": "https://github.com/NicolaiSoeborg/filemanager-plugin/archive/v2.1.1.zip",
+ "Require": {
+ "micro": ">=1.3.2"
+ }
+ },
+ {
+ "Version": "3.1.0",
+ "Url": "https://github.com/NicolaiSoeborg/filemanager-plugin/archive/v3.1.0.zip",
+ "Require": {
+ "micro": ">=1.3.5"
+ }
+ },
+ {
+ "Version": "3.4.0",
+ "Url": "https://github.com/NicolaiSoeborg/filemanager-plugin/archive/v3.4.0.zip",
+ "Require": {
+ "micro": ">=1.4.1"
+ }
+ },
+ {
+ "Version": "3.5.0",
+ "Url": "https://github.com/NicolaiSoeborg/filemanager-plugin/archive/v3.4.0.zip",
+ "Require": {
+ "micro": ">=2.0.0-1"
+ }
+ }
+ ]
+ }
+]
diff --git a/filemanager-plugin/syntax.yaml b/filemanager-plugin/syntax.yaml
new file mode 100644
index 0000000..da001cf
--- /dev/null
+++ b/filemanager-plugin/syntax.yaml
@@ -0,0 +1,9 @@
+filetype: filemanager
+
+detect:
+ filename: "^filemanager$"
+
+rules:
+ # The "..", the separator line thing, and directories
+ # Optionally, add this below to highlight the ascii line: "^─*$"
+ - special: "^\\.\\.$|\\-\\s.*|\\+\\s.*"
diff --git a/fzf/README.md b/fzf/README.md
new file mode 100644
index 0000000..2f03afd
--- /dev/null
+++ b/fzf/README.md
@@ -0,0 +1 @@
+Provides a `fzf` command in micro to open a file in the current pane using fzf.
diff --git a/fzf/main.lua b/fzf/main.lua
new file mode 100644
index 0000000..ba04dd0
--- /dev/null
+++ b/fzf/main.lua
@@ -0,0 +1,38 @@
+VERSION = "1.1.0"
+
+local micro = import("micro")
+local shell = import("micro/shell")
+local config = import("micro/config")
+local buffer = import("micro/buffer")
+
+function fzf(bp)
+ if shell.TermEmuSupported then
+ local err = shell.RunTermEmulator(bp, "fzf", false, true, fzfOutput, {bp})
+ if err ~= nil then
+ micro.InfoBar():Error(err)
+ end
+ else
+ local output, err = RunInteractiveShell("fzf", false, true)
+ if err ~= nil then
+ micro.InfoBar():Error(err)
+ else
+ fzfOutput(output)
+ end
+ end
+end
+
+function fzfOutput(output, args)
+ local bp = args[1]
+ local strings = import("strings")
+ output = strings.TrimSpace(output)
+ if output ~= "" then
+ local buf, err = buffer.NewBufferFromFile(output)
+ if err == nil then
+ bp:OpenBuffer(buf)
+ end
+ end
+end
+
+function init()
+ config.MakeCommand("fzf", fzf, config.NoComplete)
+end
diff --git a/fzf/repo.json b/fzf/repo.json
new file mode 100644
index 0000000..b6bab1c
--- /dev/null
+++ b/fzf/repo.json
@@ -0,0 +1,25 @@
+[
+ {
+ "Name": "fzf",
+ "Description": "adds support to opening files via fzf",
+ "Website": "https://github.com/samdmarshall/micro-fzf-plugin",
+ "Tags": ["fzf"],
+ "Versions": [
+ {
+ "Version": "1.1.0",
+ "Url": "https://github.com/samdmarshall/micro-fzf-plugin/archive/v1.1.0.zip",
+ "Require": {
+ "micro": ">=2.0.0-1"
+ }
+ },
+ {
+ "Version": "1.0.5",
+ "Url": "https://github.com/samdmarshall/micro-fzf-plugin/archive/v1.0.5.zip",
+ "Require": {
+ "micro": ">=1.1.2"
+ }
+ }
+ ]
+ }
+]
+
diff --git a/go-plugin/README.md b/go-plugin/README.md
new file mode 100644
index 0000000..8002e0e
--- /dev/null
+++ b/go-plugin/README.md
@@ -0,0 +1,10 @@
+# Go Plugin for Micro
+
+This repository holds the Go plugin for micro. This plugin
+has been updated for use with micro 2.0.
+
+Install by cloning this into your plug directory:
+
+```
+git clone https://github.com/micro-editor/go-plugin ~/.config/micro/plug/go-plugin
+```
diff --git a/go-plugin/go.lua b/go-plugin/go.lua
new file mode 100644
index 0000000..24f201d
--- /dev/null
+++ b/go-plugin/go.lua
@@ -0,0 +1,82 @@
+VERSION = "2.0.0"
+
+local micro = import("micro")
+local config = import("micro/config")
+local shell = import("micro/shell")
+local buffer = import("micro/buffer")
+
+function init()
+ config.RegisterCommonOption("go", "goimports", false)
+ config.RegisterCommonOption("go", "gofmt", true)
+
+ config.MakeCommand("goimports", goimports, config.NoComplete)
+ config.MakeCommand("gofmt", gofmt, config.NoComplete)
+ config.MakeCommand("gorename", gorenameCmd, config.NoComplete)
+
+ config.AddRuntimeFile("go", config.RTHelp, "help/go-plugin.md")
+ config.TryBindKey("F6", "command-edit:gorename ", false)
+end
+
+function onSave(bp)
+ if bp.Buf:FileType() == "go" then
+ if bp.Buf.Settings["goimports"] then
+ goimports(bp)
+ elseif bp.Buf.Settings["gofmt"] then
+ gofmt(bp)
+ end
+ end
+ return true
+end
+
+function gofmt(bp)
+ bp:Save()
+ local _, err = shell.RunCommand("gofmt -w " .. bp.Buf.Path)
+ if err ~= nil then
+ micro.InfoBar():Error(err)
+ return
+ end
+
+ bp.Buf:ReOpen()
+end
+
+function gorenameCmd(bp, args)
+ micro.Log(args)
+ if #args == 0 then
+ micro.InfoBar():Message("Not enough arguments")
+ else
+ bp:Save()
+ local buf = bp.Buf
+ if #args == 1 then
+ local c = bp.Cursor
+ local loc = buffer.Loc(c.X, c.Y)
+ local offset = buffer.ByteOffset(loc, buf)
+ local cmdargs = {"--offset", buf.Path .. ":#" .. tostring(offset), "--to", args[1]}
+ shell.JobSpawn("gorename", cmdargs, nil, renameStderr, renameExit, bp)
+ else
+ local cmdargs = {"--from", args[1], "--to", args[2]}
+ shell.JobSpawn("gorename", cmdargs, nil, renameStderr, renameExit, bp)
+ end
+ micro.InfoBar():Message("Renaming...")
+ end
+end
+
+function renameStderr(err)
+ micro.Log(err)
+ micro.InfoBar():Message(err)
+end
+
+function renameExit(output, args)
+ local bp = args[1]
+ bp.Buf:ReOpen()
+end
+
+function goimports(bp)
+ bp:Save()
+ local _, err = shell.RunCommand("goimports -w " .. bp.Buf.Path)
+ if err ~= nil then
+ micro.InfoBar():Error(err)
+ return
+ end
+
+ bp.Buf:ReOpen()
+end
diff --git a/go-plugin/help/go-plugin.md b/go-plugin/help/go-plugin.md
new file mode 100644
index 0000000..a4cdb33
--- /dev/null
+++ b/go-plugin/help/go-plugin.md
@@ -0,0 +1,42 @@
+# Go Plugin
+
+The go plugin provides some extra niceties for using micro with
+the Go programming language. The main thing this plugin does is
+run `gofmt` and `goimports` for you automatically. If you would also
+like automatically error linting, check out the `linter` plugin.
+The plugin also provides `gorename` functionality.
+
+You can run
+
+```
+> gofmt
+```
+
+or
+
+```
+> goimports
+```
+
+To automatically run these when you save the file, use the following
+options:
+
+* `gofmt`: run gofmt on file saved. Default value: `on`
+* `goimports`: run goimports on file saved. Default value: `off`
+
+To use `gorename`, place your cursor over the variable you would like
+to rename and enter the command `> gorename newName`.
+
+You also press F6 (the default binding) to open a prompt to rename. You
+can rebind this in your `bindings.json` file with the action `go.gorename`.
+
+The go plugin also provides an interface for using `gorename` if you have
+it installed. The gorename command behaves in two ways:
+
+* With one argument: `> gorename newname` will rename the identifier
+ under the cursor with the new name `newname`.
+
+* With two arguments: `> gorename oldname newname` will rename the old
+ identifier with the new one. This uses gorename's syntax for specifying
+ the old identifier, so please see `gorename --help` for the details
+ for how to specify the variable.
diff --git a/go-plugin/repo.json b/go-plugin/repo.json
new file mode 100644
index 0000000..793e2b3
--- /dev/null
+++ b/go-plugin/repo.json
@@ -0,0 +1,23 @@
+[{
+ "Name": "go",
+ "Description": "Go language support (gofmt and goimports)",
+ "Tags": ["go", "golang"],
+ "Website": "https://github.com/micro-editor/go-plugin",
+ "Versions": [
+ {
+ "Version": "2.0.0",
+ "Url": "https://github.com/micro-editor/go-plugin/archive/v2.0.0.zip",
+ "Require": {
+ "micro": ">=2.0.0-1"
+ }
+ },
+ {
+ "Version": "1.0.1",
+ "Url": "https://github.com/micro-editor/go-plugin/archive/v1.0.1.zip",
+ "Require": {
+ "micro": ">=1.0.3 <2.0.0-1"
+ }
+ }
+ ]
+}]
+
diff --git a/join-lines-plugin/README.md b/join-lines-plugin/README.md
new file mode 100644
index 0000000..c3e4c7b
--- /dev/null
+++ b/join-lines-plugin/README.md
@@ -0,0 +1,5 @@
+# Join Lines Plugin for Micro
+
+This plugin provides the ability to join selected lines in micro.
+
+Install with `> plugin install join-lines`.
diff --git a/join-lines-plugin/help/join-lines-plugin.md b/join-lines-plugin/help/join-lines-plugin.md
new file mode 100644
index 0000000..47cb4b3
--- /dev/null
+++ b/join-lines-plugin/help/join-lines-plugin.md
@@ -0,0 +1,10 @@
+# Join Lines Plugin
+
+The default binding to join a selected line is `Alt-j`,
+but you can easily modify that in your `bindings.json` file:
+
+```json
+{
+ "Alt-k": "joinLines.joinLines"
+}
+```
\ No newline at end of file
diff --git a/join-lines-plugin/joinLines.lua b/join-lines-plugin/joinLines.lua
new file mode 100644
index 0000000..c111d96
--- /dev/null
+++ b/join-lines-plugin/joinLines.lua
@@ -0,0 +1,48 @@
+VERSION = "1.1.0"
+
+local micro = import("micro")
+local config = import("micro/config")
+local buffer = import("micro/buffer")
+local util = import("micro/util")
+
+function joinLines(v)
+ local a, b, c = nil, nil, v.Cursor
+ local selection = c:GetSelection()
+
+ if c:HasSelection() then
+ if c.CurSelection[1]:GreaterThan(-c.CurSelection[2]) then
+ a, b = c.CurSelection[2], c.CurSelection[1]
+ else
+ a, b = c.CurSelection[1], c.CurSelection[2]
+ end
+ a = buffer.Loc(a.X, a.Y)
+ b = buffer.Loc(b.X, b.Y)
+ selection = c:GetSelection()
+ else
+ -- get beginning of curent line
+ local startLoc = buffer.Loc(0, c.Loc.Y)
+ -- get the last position of the next line
+ local xNext = string.len(v.Buf:Line(c.Loc.Y+1))
+
+ a = startLoc
+ b = buffer.Loc(xNext, c.Loc.Y+1)
+
+ c:SetSelectionStart(startLoc)
+ c:SetSelectionEnd(b)
+ selection = c:GetSelection()
+ end
+
+ selection = util.String(selection)
+
+ -- swap all whitespaces with a single space
+ local modifiedSelection = string.gsub(selection, "%s+", " ")
+ -- write modified selection to buffer
+ micro.Log(a, b)
+ v.Buf:Replace(a, b, modifiedSelection)
+end
+
+function init()
+ config.MakeCommand("joinLines", joinLines, config.NoComplete)
+ config.TryBindKey("Alt-j", "lua:joinLines.joinLines", false)
+ config.AddRuntimeFile("joinLines", config.RTHelp, "help/join-lines-plugin.md")
+end
diff --git a/join-lines-plugin/repo.json b/join-lines-plugin/repo.json
new file mode 100644
index 0000000..e685ffa
--- /dev/null
+++ b/join-lines-plugin/repo.json
@@ -0,0 +1,22 @@
+[{
+ "Name": "joinLines",
+ "Description": "Plugin to join selected lines or join the following line with the current one",
+ "Website": "https://github.com/Lisiadito/join-lines-plugin",
+ "Tags": ["join lines"],
+ "Versions": [
+ {
+ "Version": "1.1.0",
+ "Url": "https://github.com/Lisiadito/join-lines-plugin/archive/v1.1.0.zip",
+ "Require": {
+ "micro": ">=2.0.0-1"
+ }
+ },
+ {
+ "Version": "1.0.0",
+ "Url": "https://github.com/Lisiadito/join-lines-plugin/archive/v1.0.0.zip",
+ "Require": {
+ "micro": ">=1.1.0"
+ }
+ }
+ ]
+}]
diff --git a/manipulator-plugin/.gitignore b/manipulator-plugin/.gitignore
new file mode 100644
index 0000000..6fd0a37
--- /dev/null
+++ b/manipulator-plugin/.gitignore
@@ -0,0 +1,41 @@
+# Compiled Lua sources
+luac.out
+
+# luarocks build files
+*.src.rock
+*.zip
+*.tar.gz
+
+# Object files
+*.o
+*.os
+*.ko
+*.obj
+*.elf
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Libraries
+*.lib
+*.a
+*.la
+*.lo
+*.def
+*.exp
+
+# Shared objects (inc. Windows DLLs)
+*.dll
+*.so
+*.so.*
+*.dylib
+
+# Executables
+*.exe
+*.out
+*.app
+*.i*86
+*.x86_64
+*.hex
+
diff --git a/manipulator-plugin/LICENSE b/manipulator-plugin/LICENSE
new file mode 100644
index 0000000..ae6fb2f
--- /dev/null
+++ b/manipulator-plugin/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 Nicolai Søborg
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/manipulator-plugin/README.md b/manipulator-plugin/README.md
new file mode 100644
index 0000000..caf35b8
--- /dev/null
+++ b/manipulator-plugin/README.md
@@ -0,0 +1,50 @@
+# Manipulator Plugin
+
+This is an simple plugin to extend text manipulation in Micro.
+
+## Keybindings
+By default no keybindings exist, but you can easily modify that
+in your `bindings.json` file:
+
+```json
+{
+ "Ctrl-L": "manipulator.lower"
+}
+```
+
+You can also execute a command which will do the same thing as
+the binding:
+
+```
+> lower
+```
+
+If you have a selection, the plugin will change all of the
+selection.
+
+The following commands currently exists:
+ * `upper`: UPPERCASE
+ * `lower`: lowercase
+ * `reverse`: Reverses
+ * `base64enc`: Base64 encodes
+ * `base64dec`: Base64 decodes
+ * `rot13`: ROT-13
+ * `incNum`: Increase number by one
+ * `decNum`: Decrease number by one
+ * `capital`: Capitalize First Letter
+ * `brace`: Adds brackets around selection
+ * `curly`: Curly brackets (`{ }`)
+ * `square`: Square brackets (`[ ]`)
+ * `angle`: Angle brackets (`< >`)
+ * `dquote`: Double quotes (`" "`)
+ * `squote`: Single quotes (`' '`)
+
+## Issues
+
+Please use the issue tracker if you have any issues or
+feature requests!
+
+
+## Demo
+
+![Demo](demo.gif "Demo: Using a few of the commands")
diff --git a/manipulator-plugin/demo.gif b/manipulator-plugin/demo.gif
new file mode 100644
index 0000000..e46aa74
Binary files /dev/null and b/manipulator-plugin/demo.gif differ
diff --git a/manipulator-plugin/demo.txt b/manipulator-plugin/demo.txt
new file mode 100644
index 0000000..fd6ed33
--- /dev/null
+++ b/manipulator-plugin/demo.txt
@@ -0,0 +1,6 @@
+THIS TEXT SHOULDN'T BE ALL UPPERCASE
+Write const all uppercase, e.g. max_connections
+This should be two: 1 <== you can also decrease!
+
+base64(rot13(txt)):
+ZmhjcmUgZnJwZXJn
diff --git a/manipulator-plugin/help/manipulator.md b/manipulator-plugin/help/manipulator.md
new file mode 100644
index 0000000..caf35b8
--- /dev/null
+++ b/manipulator-plugin/help/manipulator.md
@@ -0,0 +1,50 @@
+# Manipulator Plugin
+
+This is an simple plugin to extend text manipulation in Micro.
+
+## Keybindings
+By default no keybindings exist, but you can easily modify that
+in your `bindings.json` file:
+
+```json
+{
+ "Ctrl-L": "manipulator.lower"
+}
+```
+
+You can also execute a command which will do the same thing as
+the binding:
+
+```
+> lower
+```
+
+If you have a selection, the plugin will change all of the
+selection.
+
+The following commands currently exists:
+ * `upper`: UPPERCASE
+ * `lower`: lowercase
+ * `reverse`: Reverses
+ * `base64enc`: Base64 encodes
+ * `base64dec`: Base64 decodes
+ * `rot13`: ROT-13
+ * `incNum`: Increase number by one
+ * `decNum`: Decrease number by one
+ * `capital`: Capitalize First Letter
+ * `brace`: Adds brackets around selection
+ * `curly`: Curly brackets (`{ }`)
+ * `square`: Square brackets (`[ ]`)
+ * `angle`: Angle brackets (`< >`)
+ * `dquote`: Double quotes (`" "`)
+ * `squote`: Single quotes (`' '`)
+
+## Issues
+
+Please use the issue tracker if you have any issues or
+feature requests!
+
+
+## Demo
+
+![Demo](demo.gif "Demo: Using a few of the commands")
diff --git a/manipulator-plugin/manipulator.lua b/manipulator-plugin/manipulator.lua
new file mode 100644
index 0000000..835a148
--- /dev/null
+++ b/manipulator-plugin/manipulator.lua
@@ -0,0 +1,159 @@
+VERSION = "1.4.0"
+
+local micro = import("micro")
+local config = import("micro/config")
+local buffer = import("micro/buffer")
+
+-- Returns Loc-tuple w/ current marked text or whole line (begin, end)
+function getTextLoc()
+ local v = micro.CurPane()
+ local a, b, c = nil, nil, v.Cursor
+ if c:HasSelection() then
+ if c.CurSelection[1]:GreaterThan(-c.CurSelection[2]) then
+ a, b = c.CurSelection[2], c.CurSelection[1]
+ else
+ a, b = c.CurSelection[1], c.CurSelection[2]
+ end
+ else
+ local eol = string.len(v.Buf:Line(c.Loc.Y))
+ a, b = c.Loc, buffer.Loc(eol, c.Y)
+ end
+ return buffer.Loc(a.X, a.Y), buffer.Loc(b.X, b.Y)
+end
+
+-- Returns the current marked text or whole line
+function getText(a, b)
+ local txt, buf = {}, micro.CurPane().Buf
+
+ -- Editing a single line?
+ if a.Y == b.Y then
+ return buf:Line(a.Y):sub(a.X+1, b.X)
+ end
+
+ -- Add first part of text selection (a.X+1 as Lua is 1-indexed)
+ table.insert(txt, buf:Line(a.Y):sub(a.X+1))
+
+ -- Stuff in the middle
+ for lineNo = a.Y+1, b.Y-1 do
+ table.insert(txt, buf:Line(lineNo))
+ end
+
+ -- Insert last part of selection
+ table.insert(txt, buf:Line(b.Y):sub(1, b.X))
+
+ return table.concat(txt, "\n")
+end
+
+-- Calls 'manipulator'-function on text matching 'regex'
+function manipulate(regex, manipulator, num)
+ local num = math.inf or num
+ local v = micro.CurPane()
+ local a, b = getTextLoc()
+
+ local oldTxt = getText(a,b)
+
+ local newTxt = string.gsub(oldTxt, regex, manipulator, num)
+ v.Buf:Replace(a, b, newTxt)
+
+ -- Fix selection, if transformation changes text length (e.g. base64)
+ local d = string.len(newTxt) - string.len(oldTxt)
+ if d ~= 0 then
+ local c = v.Cursor
+ if c:HasSelection() then
+ if c.CurSelection[1]:GreaterThan(-c.CurSelection[2]) then
+ c.CurSelection[1].X = c.CurSelection[1].X - d
+ else
+ c.CurSelection[2].X = c.CurSelection[2].X + d
+ end
+ end
+ end
+
+ --v.Cursor:Relocate()
+ --v.Cursor.LastVisualX = v.Cursor:GetVisualX()
+end
+
+
+--[[ DEFINE FUNCTIONS ]]--
+
+function rot13() manipulate("[%a]",
+ function (txt)
+ local result, lower, upper = {}, string.byte('a'), string.byte('A')
+ for c in txt:gmatch(".") do
+ local offset = string.lower(c) == c and lower or upper
+ local p = ((c:byte() - offset + 13) % 26) + offset
+ table.insert(result, string.char(p))
+ end
+ return table.concat(result, "")
+ end
+) end
+
+function incNum() manipulate("[%d]",
+ function (txt) return tonumber(txt)+1 end
+) end
+
+function decNum() manipulate("[%d]",
+ function (txt) return tonumber(txt)-1 end
+) end
+
+-- Credit: http://lua-users.org/wiki/BaseSixtyFour
+function base64enc() manipulate(".*",
+ function (data)
+ local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
+ return ((data:gsub('.', function(x)
+ local r,b='',x:byte()
+ for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end
+ return r;
+ end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)
+ if (#x < 6) then return '' end
+ local c=0
+ for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end
+ return b:sub(c+1,c+1)
+ end)..({ '', '==', '=' })[#data%3+1])
+ end
+) end
+
+function base64dec() manipulate(".*",
+ function (data)
+ local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
+ data = string.gsub(data, '[^'..b..'=]', '')
+ return (data:gsub('.', function(x)
+ if (x == '=') then return '' end
+ local r,f='',(b:find(x)-1)
+ for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end
+ return r;
+ end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
+ if (#x ~= 8) then return '' end
+ local c=0
+ for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(8-i) or 0) end
+ return string.char(c)
+ end))
+ end
+) end
+
+-- Credit: http://lua-users.org/wiki/StringRecipes
+function capital() manipulate("(%a)([%w_']*)",
+ function (first, rest)
+ return first:upper() .. rest:lower()
+ end
+) end
+
+function init()
+ config.MakeCommand("capital", capital, config.NoComplete)
+ -- Thanks marinopposite
+ config.MakeCommand("brace", function() manipulate(".*", "(%1)", 1) end, config.NoComplete)
+ config.MakeCommand("curly", function() manipulate(".*", "{%1}", 1) end, config.NoComplete)
+ config.MakeCommand("square", function() manipulate(".*", "[%1]", 1) end, config.NoComplete)
+ config.MakeCommand("dquote", function() manipulate(".*", '"%1"', 1) end, config.NoComplete)
+ config.MakeCommand("squote", function() manipulate(".*", "'%1'", 1) end, config.NoComplete)
+ config.MakeCommand("angle", function() manipulate(".*", "<%1>", 1) end, config.NoComplete)
+ config.MakeCommand("base64dec", base64dec, config.NoComplete)
+ config.MakeCommand("base64enc", base64enc, config.NoComplete)
+ config.MakeCommand("decNum", decNum, config.NoComplete)
+ config.MakeCommand("incNum", incNum, config.NoComplete)
+ config.MakeCommand("rot13", rot13, config.NoComplete)
+ config.MakeCommand("upper", function() manipulate("[%a]", string.upper) end, config.NoComplete)
+ config.MakeCommand("lower", function() manipulate("[%a]", string.lower) end, config.NoComplete)
+ config.MakeCommand("reverse", function() manipulate(".*", string.reverse) end, config.NoComplete)
+
+ config.AddRuntimeFile("manipulator", config.RTHelp, "help/manipulator.md")
+end
diff --git a/manipulator-plugin/repo.json b/manipulator-plugin/repo.json
new file mode 100644
index 0000000..fcf4eb6
--- /dev/null
+++ b/manipulator-plugin/repo.json
@@ -0,0 +1,22 @@
+[{
+ "Name": "manipulator",
+ "Description": "Plugin to do various kind of modifications to text in Micro",
+ "Website": "https://github.com/NicolaiSoeborg/manipulator-plugin",
+ "Tags": ["text", "manipulation", "base64", "rot13", "bracket", "Capital", "uppercase", "lowercase"],
+ "Versions": [
+ {
+ "Version": "1.4.0",
+ "Url": "https://github.com/NicolaiSoeborg/manipulator-plugin/archive/v1.4.0.zip",
+ "Require": {
+ "micro": ">=2.0.0-1"
+ }
+ },
+ {
+ "Version": "1.3.2",
+ "Url": "https://github.com/NicolaiSoeborg/manipulator-plugin/archive/v1.3.2.zip",
+ "Require": {
+ "micro": ">=1.0.0"
+ }
+ }
+ ]
+}]
diff --git a/micro-crystal/LICENSE b/micro-crystal/LICENSE
new file mode 100644
index 0000000..ddab670
--- /dev/null
+++ b/micro-crystal/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2016 Colin Rioux
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/micro-crystal/README.md b/micro-crystal/README.md
new file mode 100644
index 0000000..8803c62
--- /dev/null
+++ b/micro-crystal/README.md
@@ -0,0 +1,4 @@
+# micro-crystal
+Various crystal tools for micro.
+
+Install with `> plugin install crystal`.
diff --git a/micro-crystal/crystal.lua b/micro-crystal/crystal.lua
new file mode 100644
index 0000000..373e962
--- /dev/null
+++ b/micro-crystal/crystal.lua
@@ -0,0 +1,92 @@
+VERSION = "1.1.0"
+
+local micro = import("micro")
+local config = import("micro/config")
+local shell = import("micro/shell")
+
+function init()
+ config.RegisterCommonOption("crystal", "fmt", true)
+ config.MakeCommand("crystal", crystal, config.NoComplete)
+ config.AddRuntimeFile("crystal", config.RTHelp, "help/crystal-plugin.md")
+end
+
+function onBufferOpen(b)
+ -- Set tabsize by default for crystal files to 2
+ if b:FileType() == "crystal" then
+ b:SetOption("tabsize", "2")
+ end
+end
+
+function onSave(bp)
+ if bp.Buf:FileType() == "crystal" then
+ if bp.Buf.Settings["crystal.fmt"] then
+ format()
+ end
+ end
+end
+
+function crystal(bp, args)
+ if #args < 1 then
+ return
+ end
+ local a = args[1]
+ local ft = bp.Buf:FileType()
+ local file = bp.Buf.Path
+ if a == "version" or a == "--version" or a == "-v" then
+ version(a)
+ elseif a == "format" then
+ format(bp)
+ elseif a == "run" then
+ run(bp)
+ elseif a == "build" then
+ build(bp)
+ elseif a == "eval" then
+ eval(bp)
+ end
+end
+
+function version(v)
+ shell.JobSpawn("crystal", {v}, nil, nil, function(output)
+ micro.InfoBar():Message(output)
+ end)
+end
+
+function format(bp)
+ local ft = bp.Buf:FileType()
+ local file = bp.Buf.Path
+ bp:Save()
+ shell.JobSpawn("crystal", {"tool", "format", file}, nil, nil, function(output)
+ bp.Buf:ReOpen()
+ end)
+end
+
+function run(bp)
+ local file = bp.Buf.Path
+ shell.JobSpawn("crystal", {"run", file}, nil, nil, function(output)
+ micro.InfoBar():Message(output)
+ end)
+end
+
+function build(bp)
+ local file = bp.Buf.Path
+ shell.JobSpawn("crystal", {"build", file}, nil, nil, function(output)
+ micro.InfoBar():Message(output)
+ end)
+end
+
+function eval(bp)
+ local ft = bp.Buf:FileType()
+ local file = bp.Buf.Path
+ local line = bp.Buf:Line(bp.Cursor.Y)
+ shell.JobSpawn("crystal", {"eval", line}, nil, nil, function(output)
+ micro.InfoBar():Message(output)
+ end)
+end
+
+function string.contains(str, word)
+ return string.match(' '..str..' ', '%A'..word..'%A') ~= nil
+end
+
+function string.starts(str,start)
+ return string.sub(str, 1, string.len(start)) == start
+end
diff --git a/micro-crystal/help/crystal-plugin.md b/micro-crystal/help/crystal-plugin.md
new file mode 100644
index 0000000..ae2853a
--- /dev/null
+++ b/micro-crystal/help/crystal-plugin.md
@@ -0,0 +1,17 @@
+# Crystal plugin
+
+This crystal-lang plugin is similar and based off of the go plugin available for micro.
+
+There are a few features of this plugin that makes developing / debugging in micro using crystal really easy.
+
+* `> crystal format` formats the file
+* `> crystal [version, --version, -v]` lists the current crystal version
+* `> crystal build` builds the current file and compiles into an executable
+* `> crystal run` runs the current file and prints the first line of output.
+* `> crystal eval` evaluates the current line (where your cursor is)
+
+Options:
+* `crystalfmt`, default: `On`
+If set to `true`, `crystal format` is run on each save.
+
+This plugin also sets the local crystal default tab size to 2 for convenience.
diff --git a/micro-crystal/repo.json b/micro-crystal/repo.json
new file mode 100644
index 0000000..b5074de
--- /dev/null
+++ b/micro-crystal/repo.json
@@ -0,0 +1,22 @@
+[{
+ "Name": "crystal",
+ "Description": "adds helpful crystal tools",
+ "Tags": ["crystal-lang", "crystal"],
+ "Website": "https://github.com/ColinRioux/micro-crystal",
+ "Versions": [
+ {
+ "Version": "1.1.0",
+ "Url": "https://github.com/ColinRioux/micro-crystal/archive/1.1.0.zip",
+ "Require": {
+ "micro": ">=2.0.0-1"
+ }
+ },
+ {
+ "Version": "1.0.0",
+ "Url": "https://github.com/ColinRioux/micro-crystal/archive/1.0.0.zip",
+ "Require": {
+ "micro": ">=1.0.3"
+ }
+ }
+ ]
+}]
diff --git a/micro-crystal/test.md b/micro-crystal/test.md
new file mode 100644
index 0000000..b24b52b
--- /dev/null
+++ b/micro-crystal/test.md
@@ -0,0 +1,2 @@
+test test
+
diff --git a/micro-fish-plugin/README.md b/micro-fish-plugin/README.md
new file mode 100644
index 0000000..c4103d7
--- /dev/null
+++ b/micro-fish-plugin/README.md
@@ -0,0 +1,6 @@
+# Fish Plugin for Micro
+
+This repository holds the fish plugin for micro.
+
+Install with `> plugin install fish`. This plugin will let you automatically run
+`fish_indent` from within micro.
diff --git a/micro-fish-plugin/fish.lua b/micro-fish-plugin/fish.lua
new file mode 100644
index 0000000..0dba5f6
--- /dev/null
+++ b/micro-fish-plugin/fish.lua
@@ -0,0 +1,30 @@
+VERSION = "0.2.0"
+
+local micro = import("micro")
+local config = import("micro/config")
+local shell = import("micro/shell")
+
+function init()
+ config.RegisterCommonOption("fish", "fmt", true)
+ config.MakeCommand("fishfmt", fishfmt, config.NoComplete)
+ config.AddRuntimeFile("fish", config.RTHelp, "help/fish-plugin.md")
+end
+
+function onSave(bp)
+ if bp.Buf:FileType() == "fish" then
+ if bp.Buf.Settings["fish.fmt"] then
+ fishfmt(bp)
+ end
+ end
+end
+
+function fishfmt(bp)
+ bp:Save()
+ local _, err = shell.RunCommand("fish_indent -w " .. bp.Buf.Path)
+ if err ~= nil then
+ micro.InfoBar():Error(err)
+ return
+ end
+
+ bp.Buf:ReOpen()
+end
diff --git a/micro-fish-plugin/help/fish-plugin.md b/micro-fish-plugin/help/fish-plugin.md
new file mode 100644
index 0000000..02c2206
--- /dev/null
+++ b/micro-fish-plugin/help/fish-plugin.md
@@ -0,0 +1,15 @@
+# Fish Plugin
+
+The fish plugin provides some extra niceties for using micro with the fish
+scripting language. The main thing this plugin does is run `fish_indent` for you
+automatically.
+
+You can run
+
+```
+> fishfmt
+```
+
+To automatically run it when you save the file, use the following option:
+
+* `fishfmt`: run fish_indent on file saved. Default value: `on`
diff --git a/micro-fish-plugin/repo.json b/micro-fish-plugin/repo.json
new file mode 100644
index 0000000..0cd9647
--- /dev/null
+++ b/micro-fish-plugin/repo.json
@@ -0,0 +1,22 @@
+[{
+ "Name": "fish",
+ "Description": "plugin to make scripting in fish more pleasant",
+ "Tags": ["fish"],
+ "Website": "https://github.com/onodera-punpun/micro-fish-plugin",
+ "Versions": [
+ {
+ "Version": "0.2.0",
+ "Url": "https://github.com/onodera-punpun/micro-fish-plugin/archive/0.2.0.zip",
+ "Require": {
+ "micro": ">=2.0.0-1"
+ }
+ },
+ {
+ "Version": "0.1.1",
+ "Url": "https://github.com/onodera-punpun/micro-fish-plugin/archive/0.1.1.zip",
+ "Require": {
+ "micro": ">=1.0.3"
+ }
+ }
+ ]
+}]
diff --git a/micro-misspell-plugin/README.md b/micro-misspell-plugin/README.md
new file mode 100644
index 0000000..7c452c3
--- /dev/null
+++ b/micro-misspell-plugin/README.md
@@ -0,0 +1,7 @@
+# Misspell Plugin for Micro
+
+This repository holds the misspell plugin for micro.
+
+Install with `> plugin install misspell`,
+https://github.com/client9/misspell needs to be in your PATH.
+This plugin will lint text for spelling mistakes.
diff --git a/micro-misspell-plugin/misspell.lua b/micro-misspell-plugin/misspell.lua
new file mode 100644
index 0000000..9247189
--- /dev/null
+++ b/micro-misspell-plugin/misspell.lua
@@ -0,0 +1,13 @@
+VERSION = "0.2.0"
+
+local config = import("micro/config")
+
+function init()
+ -- uses the default linter plugin
+ -- matches any filetype
+ linter.makeLinter("misspell", "", "misspell", {"%f"}, "%f:%l:%c: %m", {}, false, true, 0, 0, hasMisspell)
+end
+
+function hasMisspell(buf)
+ return buf.Settings["misspell"]
+end
diff --git a/micro-misspell-plugin/repo.json b/micro-misspell-plugin/repo.json
new file mode 100644
index 0000000..a18f74f
--- /dev/null
+++ b/micro-misspell-plugin/repo.json
@@ -0,0 +1,22 @@
+[{
+ "Name": "misspell",
+ "Description": "plugin that corrects commonly misspelled words",
+ "Tags": ["spell", "check", "misspell"],
+ "Website": "https://github.com/onodera-punpun/micro-misspell-plugin",
+ "Versions": [
+ {
+ "Version": "0.2.0",
+ "Url": "https://github.com/onodera-punpun/micro-misspell-plugin/archive/0.2.0.zip",
+ "Require": {
+ "micro": ">=2.0.0-1"
+ }
+ },
+ {
+ "Version": "0.1.0",
+ "Url": "https://github.com/onodera-punpun/micro-misspell-plugin/archive/0.1.0.zip",
+ "Require": {
+ "micro": ">=1.0.3"
+ }
+ }
+ ]
+}]
diff --git a/micro-pony-plugin/README.md b/micro-pony-plugin/README.md
new file mode 100644
index 0000000..6530a66
--- /dev/null
+++ b/micro-pony-plugin/README.md
@@ -0,0 +1,4 @@
+# micro-pony-plugin
+Pony plugin for the Micro text editor
+
+Install with `> plugin install pony`.
diff --git a/micro-pony-plugin/help/pony-plugin.md b/micro-pony-plugin/help/pony-plugin.md
new file mode 100644
index 0000000..eed3236
--- /dev/null
+++ b/micro-pony-plugin/help/pony-plugin.md
@@ -0,0 +1,3 @@
+# Pony Plugin
+
+The Pony plugin provides auto-indentation for Pony syntax. More features will likely be added in the future.
diff --git a/micro-pony-plugin/log.txt b/micro-pony-plugin/log.txt
new file mode 100644
index 0000000..557c1d9
--- /dev/null
+++ b/micro-pony-plugin/log.txt
@@ -0,0 +1,17 @@
+2020/02/08 21:45:44 Micro started
+2020/02/08 21:45:44 .git is not a plugin
+2020/02/08 21:45:44 json: cannot unmarshal object into Go value of type []config.PluginInfo
+2020/02/08 21:45:44 json: cannot unmarshal object into Go value of type []config.PluginInfo
+2020/02/08 21:45:44
+2020/02/08 21:45:45 hi map[autoindent:true backup:true basename:false colorcolumn:0 commenttype:# %s cursorline:true encoding:utf-8 eofnewline:false fastdirty:false fileformat:unix filetype:unknown ignorecase:false indentchar: keepautoindent:false matchbrace:true mkparents:true readonly:false rmtrailingws:false ruler:true savecursor:true saveundo:true scrollbar:false scrollmargin:3 scrollspeed:2 smartpaste:true softwrap:true splitbottom:true splitright:true statusformatl:$(filename) $(modified)($(line),$(col)) $(status.paste)| ft:$(opt:filetype) | $(opt:fileformat) | $(opt:encoding) statusformatr:$(bind:ToggleKeyMenu): bindings, $(bind:ToggleHelp): help statusline:true syntax:true tabmovement:false tabsize:4 tabstospaces:true useprimary:true]
+2020/02/08 21:45:45 snippets-plugin -> function onBeforeTextEvent(ev) = userdata: 0xc000667620
+2020/02/08 21:45:45 snippets-plugin -> function onBeforeTextEvent(ev) = userdata: 0xc0006676e0
+2020/02/08 21:45:45 snippets-plugin -> function onBeforeTextEvent(ev) = userdata: 0xc000667740
+2020/02/08 21:45:45 hi map[autoindent:true backup:true basename:false colorcolumn:0 commenttype:# %s cursorline:true encoding:utf-8 eofnewline:false fastdirty:false fileformat:unix filetype:unknown ignorecase:false indentchar: keepautoindent:false matchbrace:true mkparents:true readonly:false rmtrailingws:false ruler:true savecursor:true saveundo:true scrollbar:false scrollmargin:3 scrollspeed:2 smartpaste:true softwrap:true splitbottom:true splitright:true statusformatl:$(filename) $(modified)($(line),$(col)) $(status.paste)| ft:$(opt:filetype) | $(opt:fileformat) | $(opt:encoding) statusformatr:$(bind:ToggleKeyMenu): bindings, $(bind:ToggleHelp): help statusline:true syntax:true tabmovement:false tabsize:4 tabstospaces:true useprimary:true]
+2020/02/08 21:45:45 snippets-plugin -> function onBeforeTextEvent(ev) = userdata: 0xc0006b0480
+2020/02/08 21:45:45 snippets-plugin -> function onBeforeTextEvent(ev) = userdata: 0xc0006b04e0
+2020/02/08 21:45:45 snippets-plugin -> function onBeforeTextEvent(ev) = userdata: 0xc0006b0540
+2020/02/08 21:45:45 hi map[autoindent:true backup:true basename:false colorcolumn:0 commenttype:# %s cursorline:true encoding:utf-8 eofnewline:false fastdirty:false fileformat:unix filetype:unknown ignorecase:false indentchar: keepautoindent:false matchbrace:true mkparents:true readonly:false rmtrailingws:false ruler:true savecursor:true saveundo:true scrollbar:false scrollmargin:3 scrollspeed:2 smartpaste:true softwrap:true splitbottom:true splitright:true statusformatl:$(filename) $(modified)($(line),$(col)) $(status.paste)| ft:$(opt:filetype) | $(opt:fileformat) | $(opt:encoding) statusformatr:$(bind:ToggleKeyMenu): bindings, $(bind:ToggleHelp): help statusline:true syntax:true tabmovement:false tabsize:4 tabstospaces:true useprimary:true]
+2020/02/08 21:45:45 snippets-plugin -> function onBeforeTextEvent(ev) = userdata: 0xc000718480
+2020/02/08 21:45:45 snippets-plugin -> function onBeforeTextEvent(ev) = userdata: 0xc0007184e0
+2020/02/08 21:45:45 snippets-plugin -> function onBeforeTextEvent(ev) = userdata: 0xc000718540
diff --git a/micro-pony-plugin/pony.lua b/micro-pony-plugin/pony.lua
new file mode 100644
index 0000000..5e11b94
--- /dev/null
+++ b/micro-pony-plugin/pony.lua
@@ -0,0 +1,68 @@
+VERSION = "0.4.0"
+
+local micro = import("micro")
+local util = import("micro/util")
+
+local indent = {
+ "actor",
+ "be",
+ "class",
+ "do",
+ "else",
+ "elseif",
+ "for",
+ "fun",
+ "if",
+ "in",
+ "interface",
+ "new",
+ "object",
+ "primitive",
+ "recover",
+ "ref",
+ "repeat",
+ "struct",
+ "tag",
+ "then",
+ "trait",
+ "try",
+ "type",
+ "until",
+ "while",
+ "with",
+ "=>"
+}
+
+function preInsertNewline(bp)
+ if not (bp.Buf:FileType() == "pony") then
+ return
+ end
+
+ local line = bp.Buf:Line(bp.Cursor.Y)
+ local ws = util.GetLeadingWhitespace(line)
+ local x = bp.Cursor.X
+
+ if
+ (string.sub(line, x+1, x+1) == "}") and
+ (string.find(string.sub(line, x+1), "{") == nil)
+ then
+ bp:InsertNewline()
+ bp:CursorUp()
+ bp:EndOfLine()
+ bp:InsertNewline()
+ bp:InsertTab()
+ return false
+ end
+
+ for _, key in pairs(indent) do
+ for word in string.gmatch(string.sub(line, 1, x), "%S+") do
+ if word == key then
+ bp:InsertNewline()
+ if string.find(string.sub(line, 1, x+1), "end") == nil then
+ bp:InsertTab()
+ end
+ return false
+ end
+ end
+ end
+end
diff --git a/micro-pony-plugin/repo.json b/micro-pony-plugin/repo.json
new file mode 100644
index 0000000..91d9da9
--- /dev/null
+++ b/micro-pony-plugin/repo.json
@@ -0,0 +1,22 @@
+[{
+ "Name": "pony",
+ "Description": "plugin for the Pony Programming Language",
+ "Website": "https://github.com/Theodus/micro-pony-plugin",
+ "Tags": ["pony"],
+ "Versions": [
+ {
+ "Version": "0.4.0",
+ "Url": "https://github.com/theodus/micro-pony-plugin/archive/0.4.0.zip",
+ "Require": {
+ "micro": ">=2.0.0-1"
+ }
+ },
+ {
+ "Version": "0.3.0",
+ "Url": "https://github.com/theodus/micro-pony-plugin/archive/0.3.0.zip",
+ "Require": {
+ "micro": ">=1.1.3"
+ }
+ }
+ ]
+}]
diff --git a/micro-snippets-plugin/LICENSE b/micro-snippets-plugin/LICENSE
new file mode 100644
index 0000000..11b0566
--- /dev/null
+++ b/micro-snippets-plugin/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2016: Florian Sundermann.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/micro-snippets-plugin/README.md b/micro-snippets-plugin/README.md
new file mode 100644
index 0000000..54cf3b9
--- /dev/null
+++ b/micro-snippets-plugin/README.md
@@ -0,0 +1,26 @@
+This project was originally created by [boombuler](https://github.com/boombuler)
+
+## Introduction
+
+This is a simple snippet plugin for the [Micro](https://github.com/zyedidia/micro) editor.
+
+The plugin comes with some snippet files which are taken from [vim-snippets](https://github.com/honza/vim-snippets)
+
+Have a look at those repositories for their license and additional information!
+
+Other [plugins](https://github.com/micro-editor/plugin-channel) for Micro editor
+
+## Install Snippet Plugin
+
+### From Micro Version 1
+
+'ctrl-e'
+
+'plugin install snippets'
+
+### From Micro Version 2
+
+```bash
+cd ~/.config/micro/plug
+git clone https://github.com/tommyshem/micro-snippets-plugin.git snippets
+```
diff --git a/micro-snippets-plugin/help/snippets.md b/micro-snippets-plugin/help/snippets.md
new file mode 100644
index 0000000..aeeaab3
--- /dev/null
+++ b/micro-snippets-plugin/help/snippets.md
@@ -0,0 +1,82 @@
+# About the snippet plugin
+
+This plugin is designed to work with simple VIM snippets like the once from
+[here](https://github.com/honza/vim-snippets/tree/master/snippets)
+The plugin itself doesn't provide any snippets. To add snippets for a language
+place a file containing the snippets at `~/.config/micro/plugins/snippets/[filetype].snippets`
+
+## Commands
+
+The plugin provides the following commands:
+
+ | Command | Description of function | Key |
+ | ------------- | :--------------------------------------------------- | :---: |
+ | snippetinsert | with an optional parameter to specify which snippet | Alt+S |
+ | ' | should be inserted. |
+ | ' | If the parameter is absent, the word right |
+ | ' | before the cursor will be used for this. |
+ | snippetnext | proceeds to the next placeholder | Alt+W |
+ | snippetcancel | removes all the current snippet | Alt+D |
+ | snippetaccept | finishes the snippet editing for the current snippet | Alt+A |
+
+## Snippet Files
+
+The snippet files have a very simple syntax:
+
+* lines starting with `#` are ignored
+* a line starts with `snippet` starts a new snippet.
+* after `snippet` you can add one or more shortcuts for the snippets,
+ like `snippet aFunc bFunc` (at least one shortcut is required)
+* every line of code within the snippet must start with a tab (`\t`)
+* a snippet can have multiple placeholders which are indicated by `
+ ${num[:name]}` where num is a numeric value. Placeholders with the
+ same number share the same value. You also have the option to give
+ a placeholder a name / description / default value.
+
+Plugins can provide snippet files they just need to publish them as a runtime file with type `snippets`.
+See the plugins help for additional details.
+
+Sample for go:
+
+```go
+# creates a function that prints a value
+snippet fmtfunc
+ func ${0:name}() {
+ fmt.Println("${0} prints:", ${1:value})
+ }
+```
+
+## Custom Key Bindings
+
+Add a file, if not already created in `~/.config/micro/bindings.json`
+
+Change the default keys you want to use.
+
+Micro editor as a help file here https://github.com/zyedidia/micro/blob/master/runtime/help/keybindings.md
+
+```json
+{
+"Alt-w": "snippets.Next",
+"Alt-a": "snippets.Accept",
+"Alt-s": "snippets.Insert",
+"Alt-d": "snippets.Cancel"
+}
+```
+
+## Raw key codes
+
+Micro has a command `raw`
+
+Micro will open a new tab and show the escape sequence for every event it receives from the terminal.
+
+This shows you what micro actually sees from the terminal and helps you see which bindings aren't possible and why.
+
+This is most useful for debugging keybindings.
+
+Example
+
+\x1b turns into \u001 then the same as the raw output.
+
+`"\u001bctrlback": "DeleteWordLeft"`
+
+Micro editor help file https://github.com/zyedidia/micro/blob/master/runtime/help/keybindings.md
diff --git a/micro-snippets-plugin/info.json b/micro-snippets-plugin/info.json
new file mode 100644
index 0000000..fd6450b
--- /dev/null
+++ b/micro-snippets-plugin/info.json
@@ -0,0 +1,10 @@
+{
+ "name": "snippets",
+ "description": "Snippets plugin",
+ "website": "",
+ "install": "",
+ "version": "1.0.0",
+ "require": [
+ "micro >= 2.0.0"
+ ]
+}
diff --git a/micro-snippets-plugin/repo.json b/micro-snippets-plugin/repo.json
new file mode 100644
index 0000000..8e66336
--- /dev/null
+++ b/micro-snippets-plugin/repo.json
@@ -0,0 +1,29 @@
+[{
+ "Name": "snippets",
+ "Description": "plugin to insert language specific code snippets.",
+ "Website": "https://github.com/tommyshem/micro-snippets-plugin",
+ "Tags": ["snippet"],
+ "Versions": [
+ {
+ "Version": "0.2.0",
+ "Url": "https://github.com/tommyshem/micro-snippets-plugin/archive/v0.2.0.zip",
+ "Require": {
+ "micro": ">=2.0.0-1"
+ }
+ },
+ {
+ "Version": "0.1.4",
+ "Url": "https://github.com/tommyshem/micro-snippets-plugin/archive/v0.1.4.zip",
+ "Require": {
+ "micro": ">=1.3.4"
+ }
+ },
+ {
+ "Version": "0.1.2",
+ "Url": "https://github.com/tommyshem/micro-snippets-plugin/archive/v0.1.2.zip",
+ "Require": {
+ "micro": ">1.0.3 <1.3.4"
+ }
+ }
+ ]
+}]
diff --git a/micro-snippets-plugin/snippets.lua b/micro-snippets-plugin/snippets.lua
new file mode 100644
index 0000000..d5d71b3
--- /dev/null
+++ b/micro-snippets-plugin/snippets.lua
@@ -0,0 +1,586 @@
+VERSION = "0.2.0"
+
+local micro = import("micro")
+local buffer = import("micro/buffer")
+local config = import("micro/config")
+local util = import("micro/util")
+
+local debugflag = true
+local curFileType = ""
+local snippets = {}
+local currentSnippet = nil
+local RTSnippets = config.NewRTFiletype()
+
+local Location = {}
+Location.__index = Location
+
+local Snippet = {}
+Snippet.__index = Snippet
+
+-- Snippets
+-- --> Snippet
+-- --> Location
+
+function Location.new(idx, ph, snippet)
+ debug1("Location.new(idx, ph, snip) idx = " , idx)
+ --debugt("Location.new(idx, ph, snip) ph = ", ph)
+ --debugt("Location.new(idx, ph, snippet) snippet = ", snippet)
+
+ local self = setmetatable({}, Location)
+ self.idx = idx
+ self.ph = ph
+ self.snippet = snippet
+ return self
+end
+
+-- offset of the location relative to the snippet start
+function Location.offset(self)
+ debug("Location.offset(self)")
+ local add = 0
+ for i = 1, #self.snippet.locations do
+ local loc = self.snippet.locations[i]
+ debug1("loc = ",loc)
+ if loc == self then
+ break
+ end
+
+ local val = loc.ph.value
+ micro.Log("VAL", val)
+ if val then
+ add = add + val:len()
+ end
+ end
+ return self.idx+add
+end
+
+function Location.startPos(self)
+ --debugt("Location.startPos(self) = ",self)
+ local loc = self.snippet.startPos
+ return loc:Move(self:offset(), self.snippet.view.buf)
+end
+
+-- returns the length of the location (but at least 1)
+function Location.len(self)
+ debug("Location.len(self)")
+ local len = 0
+ if self.ph.value then
+ len = self.ph.value:len()
+ end
+ if len <= 0 then
+ len = 1
+ end
+ return len
+end
+
+function Location.endPos(self)
+ debug("Location.endPos(self)")
+ local start = self:startPos()
+ micro.Log("ENDPOS", self.ph.value)
+ return start:Move(self:len(), self.snippet.view.buf)
+end
+
+-- check if the given loc is within the location
+function Location.isWithin(self, loc)
+ debug("Location.isWithin(self, loc)")
+ return loc:GreaterEqual(self:startPos()) and loc:LessEqual(self:endPos())
+end
+
+function Location.focus(self)
+ debug("Location.focus(self)")
+ local view = self.snippet.view
+ local startP = self:startPos():Move(-1, view.Buf)
+ local endP = self:endPos():Move(-1, view.Buf)
+ micro.Log(startP, endP)
+
+ if view.Cursor:LessThan(startP) then
+ while view.Cursor:LessThan(startP) do
+ view.Cursor:Right()
+ end
+ elseif view.Cursor:GreaterEqual(endP) then
+ while view.Cursor:GreaterEqual(endP) do
+ view.Cursor:Left()
+ end
+ end
+
+ if self.ph.value:len() > 0 then
+ view.Cursor:SetSelectionStart(startP)
+ view.Cursor:SetSelectionEnd(endP)
+ else
+ view.Cursor:ResetSelection()
+ end
+end
+
+function Location.handleInput(self, ev)
+ debug("Location.handleInput(self, ev)")
+ if ev.EventType == 1 then
+ -- TextInput
+ if util.String(ev.Deltas[1].Text) == "\n" then
+ Accept()
+ return false
+ else
+ local offset = 1
+ local sp = self:startPos()
+ while sp:LessEqual(-ev.Deltas[1].Start) do
+ sp = sp:Move(1, self.snippet.view.Buf)
+ offset = offset + 1
+ end
+
+ self.snippet:remove()
+ local v = self.ph.value
+ if v == nil then
+ v = ""
+ end
+
+ self.ph.value = v:sub(0, offset-1) .. util.String(ev.Deltas[1].Text) .. v:sub(offset)
+ self.snippet:insert()
+ return true
+ end
+ elseif ev.EventType == -1 then
+ -- TextRemove
+ local offset = 1
+ local sp = self:startPos()
+ while sp:LessEqual(-ev.Deltas[1].Start) do
+ sp = sp:Move(1, self.snippet.view.Buf)
+ offset = offset + 1
+ end
+
+ if ev.Deltas[1].Start.Y ~= ev.Deltas[1].End.Y then
+ return false
+ end
+
+ self.snippet:remove()
+
+ local v = self.ph.value
+ if v == nil then
+ v = ""
+ end
+
+ local len = ev.Deltas[1].End.X - ev.Deltas[1].Start.X
+
+ self.ph.value = v:sub(0, offset-1) .. v:sub(offset+len)
+ self.snippet:insert()
+ return true
+ end
+
+ return false
+end
+
+-- new snippet
+function Snippet.new()
+ debug("Snippet.new()")
+ local self = setmetatable({}, Snippet)
+ self.code = ""
+ return self
+end
+
+-- add line of code to snippet
+function Snippet.AddCodeLine(self, line)
+ --debugt("Snippet.AddCodeLine(self,line) self = " , self)
+ debug1("Snippet.AddCodeLine(self, line) line = " , line)
+ if self.code ~= "" then
+ self.code = self.code .. "\n"
+ end
+ self.code = self.code .. line
+end
+
+function Snippet.Prepare(self)
+ debug("Snippet.Prepare(self)")
+ if not self.placeholders then
+ self.placeholders = {}
+ self.locations = {}
+ local count = 0
+ local pattern = "${(%d+):?([^}]*)}"
+ while true do
+ local num, value = self.code:match(pattern)
+ if not num then
+ break
+ end
+ count = count+1
+ num = tonumber(num)
+ local idx = self.code:find(pattern)
+ self.code = self.code:gsub(pattern, "", 1)
+ micro.Log("IDX", idx, self.code)
+
+ local placeHolders = self.placeholders[num]
+ if not placeHolders then
+ placeHolders = {num = num}
+ self.placeholders[#self.placeholders+1] = placeHolders
+ end
+ self.locations[#self.locations+1] = Location.new(idx, placeHolders, self)
+ debug1("location total = ",#self.locations)
+ if value then
+ placeHolders.value = value
+ end
+ end
+ end
+end
+
+function Snippet.clone(self)
+ debug("Snippet.clone(self)")
+ local result = Snippet.new()
+ result:AddCodeLine(self.code)
+ result:Prepare()
+ return result
+end
+
+function Snippet.str(self)
+ debug("Snippet.str(self)")
+ local res = self.code
+ for i = #self.locations, 1, -1 do
+ local loc = self.locations[i]
+ res = res:sub(0, loc.idx-1) .. loc.ph.value .. res:sub(loc.idx)
+ end
+ return res
+end
+
+function Snippet.findLocation(self, loc)
+ debug1("Snippet.findLocation(self, loc) loc = ",loc)
+ for i = 1, #self.locations do
+ if self.locations[i]:isWithin(loc) then
+ return self.locations[i]
+ end
+ end
+ return nil
+end
+
+function Snippet.remove(self)
+ debug("Snippet.remove(self)")
+ local endPos = self.startPos:Move(self:str():len(), self.view.Buf)
+ self.modText = true
+ self.view.Cursor:SetSelectionStart(self.startPos)
+ self.view.Cursor:SetSelectionEnd(endPos)
+ self.view.Cursor:DeleteSelection()
+ self.view.Cursor:ResetSelection()
+ self.modText = false
+end
+
+function Snippet.insert(self)
+ debug("Snippet.insert(self)")
+ self.modText = true
+ self.view.Buf:insert(self.startPos, self:str())
+ self.modText = false
+end
+
+function Snippet.focusNext(self)
+ debug("Snippet.focusNext(self)")
+ if self.focused == nil then
+ self.focused = 0
+ else
+ self.focused = (self.focused + 1) % #self.placeholders
+ end
+
+ local ph = self.placeholders[self.focused+1]
+
+ for i = 1, #self.locations do
+ if self.locations[i].ph == ph then
+ self.locations[i]:focus()
+ return
+ end
+ end
+end
+
+local function CursorWord(bp)
+ debug1("CursorWord(bp)",bp)
+ local c = bp.Cursor
+ local x = c.X-1 -- start one rune before the cursor
+ local result = ""
+ while x >= 0 do
+ local r = util.RuneStr(c:RuneUnder(x))
+ if (r == " ") then -- IsWordChar(r) then
+ break
+ else
+ result = r .. result
+ end
+ x = x-1
+ end
+
+ return result
+end
+
+local function ReadSnippets(filetype)
+ debug1("ReadSnippets(filetype)",filetype)
+ local snippets = {}
+ local allSnippetFiles = config.ListRuntimeFiles(RTSnippets)
+ local exists = false
+
+ for i = 1, #allSnippetFiles do
+ if allSnippetFiles[i] == filetype then
+ exists = true
+ break
+ end
+ end
+
+ if not exists then
+ micro.InfoBar():Error("No snippets file for \""..filetype.."\"")
+ return snippets
+ end
+
+ local snippetFile = config.ReadRuntimeFile(RTSnippets, filetype)
+
+ local curSnip = nil
+ local lineNo = 0
+ for line in string.gmatch(snippetFile, "(.-)\r?\n") do
+ lineNo = lineNo + 1
+ if string.match(line,"^#") then
+ -- comment
+ elseif line:match("^snippet") then
+ curSnip = Snippet.new()
+ for snipName in line:gmatch("%s(.+)") do -- %s space .+ one or more non-empty sequence
+ snippets[snipName] = curSnip
+ end
+ else
+ local codeLine = line:match("^\t(.*)$")
+ if codeLine ~= nil then
+ curSnip:AddCodeLine(codeLine)
+ elseif line ~= "" then
+ micro.InfoBar():Error("Invalid snippets file (Line #"..tostring(lineNo)..")")
+ end
+ end
+ end
+ debugt("ReadSnippets(filetype) snippets = ",snippets)
+ return snippets
+end
+
+-- Check filetype and load snippets
+-- Return true is snippets loaded for filetype
+-- Return false if no snippets loaded
+local function EnsureSnippets(bp)
+ debug("EnsureSnippets()")
+ local filetype = bp.Buf.Settings["filetype"]
+ if curFileType ~= filetype then
+ snippets = ReadSnippets(filetype)
+ curFileType = filetype
+ end
+ if next(snippets) == nil then
+ return false
+ end
+ return true
+end
+
+function onBeforeTextEvent(sb, ev)
+ debug1("onBeforeTextEvent(ev)",ev)
+ if currentSnippet ~= nil and currentSnippet.view.Buf.SharedBuffer == sb then
+ if currentSnippet.modText then
+ -- text event from the snippet. simply ignore it...
+ return true
+ end
+
+ local locStart = nil
+ local locEnd = nil
+
+ if ev.Deltas[1].Start ~= nil and currentSnippet ~= nil then
+ locStart = currentSnippet:findLocation(ev.Deltas[1].Start:Move(1, currentSnippet.view.Buf))
+ locEnd = currentSnippet:findLocation(ev.Deltas[1].End)
+ end
+ if locStart ~= nil and ((locStart == locEnd) or (ev.Deltas[1].End.Y==0 and ev.Deltas[1].End.X==0)) then
+ if locStart:handleInput(ev) then
+ currentSnippet.view.Cursor:Goto(-ev.C)
+ return false
+ end
+ end
+ Accept()
+ end
+
+ return true
+
+end
+
+-- Insert snippet if found.
+-- Pass in the name of the snippet to be inserted by command mode
+-- No name passed in then it will check the text left of the cursor
+function Insert(bp, args)
+ local snippetName = nil
+ if args ~= nil and #args > 0 then
+ snippetName = args[1]
+ end
+ debug1("Insert(snippetName)",snippetName)
+
+ local c = bp.Cursor
+ local buf = bp.Buf
+ local xy = buffer.Loc(c.X, c.Y)
+ -- check if a snippet name was passed in
+ local noArg = false
+ if not snippetName then
+ snippetName = CursorWord(bp)
+ noArg = true
+ end
+ -- check filetype and load snippets
+ local result = EnsureSnippets(bp)
+ -- if no snippets return early
+ if (result == false) then return end
+
+ -- curSn cloned into currentSnippet if snippet found
+ local curSn = snippets[snippetName]
+ if curSn then
+ currentSnippet = curSn:clone()
+ currentSnippet.view = bp
+ -- remove snippet keyword from micro buffer before inserting snippet
+ if noArg then
+ currentSnippet.startPos = xy:Move(-snippetName:len(), buf)
+
+ currentSnippet.modText = true
+
+ c:SetSelectionStart(currentSnippet.startPos)
+ c:SetSelectionEnd(xy)
+ c:DeleteSelection()
+ c:ResetSelection()
+
+ currentSnippet.modText = false
+ else
+ -- no need to remove snippet keyword from buffer as run from command mode
+ currentSnippet.startPos = xy
+ end
+ -- insert snippet to micro buffer
+ currentSnippet:insert()
+ micro.InfoBar():Message("Snippet Inserted \""..snippetName.."\"")
+
+ -- Placeholders
+ if #currentSnippet.placeholders == 0 then
+ local pos = currentSnippet.startPos:Move(currentSnippet:str():len(), bp.Buf)
+ while bp.Cursor:LessThan(pos) do
+ bp.Cursor:Right()
+ end
+ while bp.Cursor:GreaterThan(pos) do
+ bp.Cursor:Left()
+ end
+ else
+ currentSnippet:focusNext()
+ end
+ else
+ -- Snippet not found
+ micro.InfoBar():Message("Unknown snippet \""..snippetName.."\"")
+ end
+end
+
+function Next()
+ debug("Next()")
+ if currentSnippet then
+ currentSnippet:focusNext()
+ end
+end
+
+function Accept()
+ debug("Accept()")
+ currentSnippet = nil
+end
+
+function Cancel()
+ debug("Cancel()")
+ if currentSnippet then
+ currentSnippet:remove()
+ Accept()
+ end
+end
+
+
+local function StartsWith(String,Start)
+ debug1("StartsWith(String,Start) String ",String)
+ debug1("StartsWith(String,Start) start ",start)
+ String = String:upper()
+ Start = Start:upper()
+ return string.sub(String,1,string.len(Start))==Start
+end
+
+-- Used for auto complete in the command prompt
+function findSnippet(input)
+ debug1("findSnippet(input)",input)
+ local result = {}
+ -- TODO: pass bp
+ EnsureSnippets()
+
+ for name,v in pairs(snippets) do
+ if StartsWith(name, input) then
+ table.insert(result, name)
+ end
+ end
+ return result
+end
+
+-- Debug functions below
+-- debug1 is for logging functionName and 1 argument passed
+function debug1(functionName, argument)
+ if debugflag == false then return end
+ if argument == nil then
+ micro.Log("snippets-plugin -> function " .. functionName .. " = nil")
+ elseif argument == "" then
+ micro.Log("snippets-plugin -> function " .. functionName .. " = empty string")
+ else micro.Log("snippets-plugin -> function " .. functionName .. " = " .. tostring(argument))
+ end
+end
+
+-- debug is for logging functionName only
+function debug(functionName)
+ if debugflag == false then return end
+ micro.Log("snippets-plugin -> function " .. functionName)
+end
+
+-- debug is for logging functionName and table
+function debugt(functionName,tablepassed)
+ if debugflag == false then return end
+ micro.Log("snippets-plugin -> function " .. functionName )
+ tprint(tablepassed)
+-- if (tablepassed == nil) then return end
+-- for key,value in pairs(tablepassed) do
+-- micro.Log("key - " .. tostring(key) .. "value = " .. tostring(value[1]) )
+-- end
+end
+
+-- dump table
+function dump(o)
+ if type(o) == 'table' then
+ local s = '{ '
+ for k,v in pairs(o) do
+ if type(k) ~= 'number' then k = '"'..k..'"' end
+ s = s .. '['..k..'] = ' .. dump(v) .. ','
+ end
+ return s .. '} '
+ else
+ return tostring(o)
+ end
+ end
+
+ function tprint (tbl, indent)
+ if not indent then indent = 0 end
+ for k, v in pairs(tbl) do
+ formatting = string.rep(" ", indent) .. k .. ": "
+ if type(v) == "table" then
+ micro.Log(formatting .. "Table ->")
+ tprint(v, indent+1)
+ elseif type(v) == nil then
+ micro.Log(formatting .. " nil")
+ else
+ micro.Log(formatting .. tostring(v))
+ end
+ end
+ end
+
+ function checkTableisEmpty(myTable)
+ if next(myTable) == nil then
+ -- myTable is empty
+ end
+end
+
+function tablePrint(tbl)
+ for index = 1, #tbl do
+ micro.Log(tostring(index) .. " = " .. tostring(tbl[index]))
+ end
+end
+
+function init()
+ -- Insert a snippet
+ config.MakeCommand("snippetinsert", Insert, config.NoComplete)
+ -- Mark next placeholder
+ config.MakeCommand("snippetnext", Next, config.NoComplete)
+ -- Cancel current snippet (removes the text)
+ config.MakeCommand("snippetcancel", Cancel, config.NoComplete)
+ -- Acceptes snipped editing
+ config.MakeCommand("snippetaccept", Accept, config.NoComplete)
+
+ config.AddRuntimeFile("snippets", config.RTHelp, "help/snippets.md")
+ config.AddRuntimeFilesFromDirectory("snippets", RTSnippets, "snippets", "*.snippets")
+
+ config.TryBindKey("Alt-w", "lua:snippets.Next", false)
+ config.TryBindKey("Alt-a", "lua:snippets.Accept", false)
+ config.TryBindKey("Alt-s", "lua:snippets.Insert", false)
+ config.TryBindKey("Alt-d", "lua:snippets.Cancel", false)
+end
diff --git a/micro-snippets-plugin/snippets/apacheconf.snippets b/micro-snippets-plugin/snippets/apacheconf.snippets
new file mode 100644
index 0000000..db3256e
--- /dev/null
+++ b/micro-snippets-plugin/snippets/apacheconf.snippets
@@ -0,0 +1,35 @@
+# Snippets for code blocks used oftenly in Apache files.
+#
+snippet dir
+
+ DirectoryIndex ${0:index.html}
+ Order Deny,Allow
+ Deny from All
+
+#
+snippet filesmatch
+
+ ${0}
+
+#
+snippet ifmodule
+
+ ${0}
+
+#
+snippet limitexcept
+
+ ${0}
+
+#
+snippet proxy
+
+ ${0}
+
+#
+snippet virtualhost
+
+ ServerAdmin ${3:webmaster@example.com}
+ DocumentRoot ${4:/www/example.com}
+ ServerName ${0:www.example.com}
+
diff --git a/micro-snippets-plugin/snippets/awk.snippets b/micro-snippets-plugin/snippets/awk.snippets
new file mode 100644
index 0000000..32e56f2
--- /dev/null
+++ b/micro-snippets-plugin/snippets/awk.snippets
@@ -0,0 +1,102 @@
+# cannot use /usr/bin/env because it does not support parameters (as -f)
+snippet #! #!/usr/bin/awk -f
+ #!/usr/bin/awk -f
+
+# @include is a gawk extension
+snippet inc @include
+ @include "${1}"${0}
+
+# @load is a gawk extension
+snippet loa @load
+ @load "${1}"${0}
+
+snippet beg BEGIN { ... }
+ BEGIN {
+ ${0}
+ }
+
+# BEGINFILE is a gawk extension
+snippet begf BEGINFILE { ... }
+ BEGINFILE {
+ ${0}
+ }
+
+snippet end END { ... }
+ END {
+ ${0}
+ }
+
+# ENDFILE is a gawk extension
+snippet endf ENDFILE { ... }
+ ENDFILE {
+ ${0}
+ }
+
+snippet pri print
+ print ${1:"${2}"}${0}
+
+snippet printf printf
+ printf("${1:%s}\n", ${2})${0}
+
+snippet ign IGNORECASE
+ IGNORECASE = ${1:1}
+
+snippet if if {...}
+ if (${1}) {
+ ${0}
+ }
+
+snippet ife if ... else ...
+ if (${1}) {
+ ${2}
+ } else {
+ ${0}
+ }
+
+snippet eif else if ...
+ else if (${1}) {
+ ${0}
+ }
+
+snippet el else {...}
+ else {
+ ${0}
+ }
+
+snippet wh while
+ while (${1}) {
+ ${2}
+ }
+
+snippet do do ... while
+ do {
+ ${0}
+ } while (${1})
+
+snippet for for
+ for (${2:i} = 0; i < ${1:n}; ${3:++i}) {
+ ${0}
+ }
+
+snippet fore for each
+ for (${1:i} in ${2:array}) {
+ ${0}
+ }
+
+# the switch is a gawk extension
+snippet sw switch
+ switch (${1}) {
+ case ${2}:
+ ${3}
+ break
+ default:
+ ${0}
+ break
+ }
+
+# the switch is a gawk extension
+snippet case case
+ case ${1}:
+ ${0}
+ break
+
diff --git a/micro-snippets-plugin/snippets/c#.snippets b/micro-snippets-plugin/snippets/c#.snippets
new file mode 100644
index 0000000..40e9c8f
--- /dev/null
+++ b/micro-snippets-plugin/snippets/c#.snippets
@@ -0,0 +1,470 @@
+# cs.snippets
+# ===========
+#
+# Standard C-Sharp snippets for snipmate.
+#
+# Largely ported over from Visual Studio 2010 snippets plus
+# a few snippets from Resharper plus a few widely known snippets.
+#
+# Most snippets on elements (i.e. classes, properties)
+# follow suffix conventions. The order of suffixes to a snippet
+# is fixed.
+#
+# Snippet Suffix Order
+# --------------------
+# 1. Access Modifiers
+# 2. Class Modifiers
+#
+# Access Modifier Suffix Table
+# ----------------------------
+# + = public
+# & = internal
+# | = protected
+# - = private
+#
+# Example: `cls&` expands to `internal class $1`.
+# Access modifiers might be doubled to indicate
+# different modifiers for get/set on properties.
+# Example: `pb+-` expands to `public bool $1 { get; private set; }`
+#
+# Class Modifier Table
+# --------------------
+# ^ = static
+# % = abstract
+#
+# Example: `cls|%` expands to `protected abstract class $1`
+#
+# On method and property snippets, you can directly set
+# one of the common types int, string and bool, if desired,
+# just by appending the type modifier.
+#
+# Type Modifier Table
+# -------------------
+# i = integer
+# s = string
+# b = bool
+#
+# Example: `pi+&` expands to `public int $1 { get; internal set; }`
+#
+# I'll most propably add more stuff in here like
+# * List/Array constructio
+# * Mostly used generics
+# * Linq
+# * Funcs, Actions, Predicates
+# * Lambda
+# * Events
+#
+# Feedback is welcome!
+#
+# Main
+snippet sim
+ ${1:public }static int Main(string[] args) {
+ ${0}
+ return 0;
+ }
+snippet simc
+ public class Application {
+ ${1:public }static int Main(string[] args) {
+ ${0}
+ return 0;
+ }
+ }
+snippet svm
+ ${1:public }static void Main(string[] args) {
+ ${0}
+ }
+# if condition
+snippet if
+ if (${1:true}) {
+ ${0}
+ }
+snippet el
+ else {
+ ${0}
+ }
+snippet ifs
+ if (${1})
+ ${0}
+# ternary conditional
+snippet t
+ ${1} ? ${2} : ${0}
+snippet ?
+ ${1} ? ${2} : ${0}
+# do while loop
+snippet do
+ do {
+ ${0}
+ } while (${1:true});
+# while loop
+snippet wh
+ while (${1:true}) {
+ ${0}
+ }
+# for loop
+snippet for
+ for (int ${1:i} = 0; $1 < ${2:count}; $1${3:++}) {
+ ${0}
+ }
+snippet forr
+ for (int ${1:i} = ${2:length}; $1 >= 0; $1--) {
+ ${0}
+ }
+# foreach
+snippet fore
+ foreach (${1:var} ${2:entry} in ${3}) {
+ ${0}
+ }
+snippet foreach
+ foreach (${1:var} ${2:entry} in ${3}) {
+ ${0}
+ }
+snippet each
+ foreach (${1:var} ${2:entry} in ${3}) {
+ ${0}
+ }
+# interfaces
+snippet interface
+ public interface ${1:`vim_snippets#Filename()`} {
+ ${0}
+ }
+snippet if+
+ public interface ${1:`vim_snippets#Filename()`} {
+ ${0}
+ }
+# class bodies
+snippet class
+ public class ${1:`vim_snippets#Filename()`} {
+ ${0}
+ }
+snippet cls
+ ${2:public} class ${1:`vim_snippets#Filename()`} {
+ ${0}
+ }
+snippet cls+
+ public class ${1:`vim_snippets#Filename()`} {
+ ${0}
+ }
+snippet cls+^
+ public static class ${1:`vim_snippets#Filename()`} {
+ ${0}
+ }
+snippet cls&
+ internal class ${1:`vim_snippets#Filename()`} {
+ ${0}
+ }
+snippet cls&^
+ internal static class ${1:`vim_snippets#Filename()`} {
+ ${0}
+ }
+snippet cls|
+ protected class ${1:`vim_snippets#Filename()`} {
+ ${0}
+ }
+snippet cls|%
+ protected abstract class ${1:`vim_snippets#Filename()`} {
+ ${0}
+ }
+# constructor
+snippet ctor
+ public ${1:`vim_snippets#Filename()`}() {
+ ${0}
+ }
+# properties - auto properties by default.
+# default type is int with layout get / set.
+snippet prop
+ ${1:public} ${2:int} ${3} { get; set; }
+snippet p
+ ${1:public} ${2:int} ${3} { get; set; }
+snippet p+
+ public ${1:int} ${2} { get; set; }
+snippet p+&
+ public ${1:int} ${2} { get; internal set; }
+snippet p+|
+ public ${1:int} ${2} { get; protected set; }
+snippet p+-
+ public ${1:int} ${2} { get; private set; }
+snippet p&
+ internal ${1:int} ${2} { get; set; }
+snippet p&|
+ internal ${1:int} ${2} { get; protected set; }
+snippet p&-
+ internal ${1:int} ${2} { get; private set; }
+snippet p|
+ protected ${1:int} ${2} { get; set; }
+snippet p|-
+ protected ${1:int} ${2} { get; private set; }
+snippet p-
+ private ${1:int} ${2} { get; set; }
+# property - bool
+snippet pi
+ ${1:public} int ${2} { get; set; }
+snippet pi+
+ public int ${1} { get; set; }
+snippet pi+&
+ public int ${1} { get; internal set; }
+snippet pi+|
+ public int ${1} { get; protected set; }
+snippet pi+-
+ public int ${1} { get; private set; }
+snippet pi&
+ internal int ${1} { get; set; }
+snippet pi&|
+ internal int ${1} { get; protected set; }
+snippet pi&-
+ internal int ${1} { get; private set; }
+snippet pi|
+ protected int ${1} { get; set; }
+snippet pi|-
+ protected int ${1} { get; private set; }
+snippet pi-
+ private int ${1} { get; set; }
+# property - bool
+snippet pb
+ ${1:public} bool ${2} { get; set; }
+snippet pb+
+ public bool ${1} { get; set; }
+snippet pb+&
+ public bool ${1} { get; internal set; }
+snippet pb+|
+ public bool ${1} { get; protected set; }
+snippet pb+-
+ public bool ${1} { get; private set; }
+snippet pb&
+ internal bool ${1} { get; set; }
+snippet pb&|
+ internal bool ${1} { get; protected set; }
+snippet pb&-
+ internal bool ${1} { get; private set; }
+snippet pb|
+ protected bool ${1} { get; set; }
+snippet pb|-
+ protected bool ${1} { get; private set; }
+snippet pb-
+ private bool ${1} { get; set; }
+# property - string
+snippet ps
+ ${1:public} string ${2} { get; set; }
+snippet ps+
+ public string ${1} { get; set; }
+snippet ps+&
+ public string ${1} { get; internal set; }
+snippet ps+|
+ public string ${1} { get; protected set; }
+snippet ps+-
+ public string ${1} { get; private set; }
+snippet ps&
+ internal string ${1} { get; set; }
+snippet ps&|
+ internal string ${1} { get; protected set; }
+snippet ps&-
+ internal string ${1} { get; private set; }
+snippet ps|
+ protected string ${1} { get; set; }
+snippet ps|-
+ protected string ${1} { get; private set; }
+snippet ps-
+ private string ${1} { get; set; }
+# members - void
+snippet m
+ ${1:public} ${2:void} ${3}(${4}) {
+ ${0}
+ }
+snippet m+
+ public ${1:void} ${2}(${3}) {
+ ${0}
+ }
+snippet m&
+ internal ${1:void} ${2}(${3}) {
+ ${0}
+ }
+snippet m|
+ protected ${1:void} ${2}(${3}) {
+ ${0}
+ }
+snippet m-
+ private ${1:void} ${2}(${3}) {
+ ${0}
+ }
+# members - int
+snippet mi
+ ${1:public} int ${2}(${3}) {
+ ${0:return 0;}
+ }
+snippet mi+
+ public int ${1}(${2}) {
+ ${0:return 0;}
+ }
+snippet mi&
+ internal int ${1}(${2}) {
+ ${0:return 0;}
+ }
+snippet mi|
+ protected int ${1}(${2}) {
+ ${0:return 0;}
+ }
+snippet mi-
+ private int ${1}(${2}) {
+ ${0:return 0;}
+ }
+# members - bool
+snippet mb
+ ${1:public} bool ${2}(${3}) {
+ ${0:return false;}
+ }
+snippet mb+
+ public bool ${1}(${2}) {
+ ${0:return false;}
+ }
+snippet mb&
+ internal bool ${1}(${2}) {
+ ${0:return false;}
+ }
+snippet mb|
+ protected bool ${1}(${2}) {
+ ${0:return false;}
+ }
+snippet mb-
+ private bool ${1}(${2}) {
+ ${0:return false;}
+ }
+# members - string
+snippet ms
+ ${1:public} string ${2}(${3}) {
+ ${0:return "";}
+ }
+snippet ms+
+ public string ${1}(${2}) {
+ ${0:return "";}
+ }
+snippet ms&
+ internal string ${1}(${2}) {
+ ${0:return "";}
+ }
+snippet ms|
+ protected string ${1:}(${2:}) {
+ ${0:return "";}
+ }
+snippet ms-
+ private string ${1}(${2}) {
+ ${0:return "";}
+ }
+# structure
+snippet struct
+ public struct ${1:`vim_snippets#Filename()`} {
+ ${0}
+ }
+# enumeration
+snippet enum
+ enum ${1} {
+ ${0}
+ }
+
+snippet enum+
+ public enum ${1} {
+ ${0}
+ }
+# preprocessor directives
+snippet #if
+ #if
+ ${0}
+ #endif
+# inline xml documentation
+snippet ///
+ ///
+ /// ${0}
+ ///
+snippet