diff --git a/.config/foot/foot.ini b/.config/foot/foot.ini new file mode 100644 index 0000000..9ca801c --- /dev/null +++ b/.config/foot/foot.ini @@ -0,0 +1,225 @@ +# -*- conf -*- + +# shell=$SHELL (if set, otherwise user's default shell from /etc/passwd) +# term=foot (or xterm-256color if built with -Dterminfo=disabled) +# login-shell=no + +# app-id=foot # globally set wayland app-id. Default values are "foot" and "footclient" for desktop and server mode +# title=foot +# locked-title=no + +font=monospace:size=12 +# font-bold= +# font-italic= +# font-bold-italic= +# font-size-adjustment=0.5 +# line-height= +# letter-spacing=0 +# horizontal-letter-offset=0 +# vertical-letter-offset=0 +# underline-offset= +# underline-thickness= +# box-drawings-uses-font-glyphs=no +# dpi-aware=no + +# initial-window-size-pixels=700x500 # Or, +# initial-window-size-chars= +# initial-window-mode=windowed +# pad=0x0 # optionally append 'center' +# resize-delay-ms=100 + +# notify=notify-send -a ${app-id} -i ${app-id} ${title} ${body} + +# bold-text-in-bright=no +# word-delimiters=,│`|:"'()[]{}<> +# selection-target=primary +# workers= +# utmp-helper=/usr/lib/utempter/utempter # When utmp backend is ‘libutempter’ (Linux) +# utmp-helper=/usr/libexec/ulog-helper # When utmp backend is ‘ulog’ (FreeBSD) + +[environment] +# name=value + +[bell] +# urgent=no +# notify=no +# visual=no +# command= +# command-focused=no + +[scrollback] +# lines=1000 +# multiplier=3.0 +# indicator-position=relative +# indicator-format="" + +[url] +# launch=xdg-open ${url} +# label-letters=sadfjklewcmpgh +# osc8-underline=url-mode +# protocols=http, https, ftp, ftps, file, gemini, gopher +# uri-characters=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.,~:;/?#@!$&%*+="'()[] + +[cursor] +# style=block +# color= +# blink=no +# beam-thickness=1.5 +# underline-thickness= + +[mouse] +# hide-when-typing=no +# alternate-scroll-mode=yes + +[touch] +# long-press-delay=400 + +[colors] +# alpha=1.0 +# background=242424 +# foreground=ffffff +# flash=7f7f00 +# flash-alpha=0.5 + +## Normal/regular colors (color palette 0-7) +# regular0=242424 # black +# regular1=f62b5a # red +# regular2=47b413 # green +# regular3=e3c401 # yellow +# regular4=24acd4 # blue +# regular5=f2affd # magenta +# regular6=13c299 # cyan +# regular7=e6e6e6 # white + +## Bright colors (color palette 8-15) +# bright0=616161 # bright black +# bright1=ff4d51 # bright red +# bright2=35d450 # bright green +# bright3=e9e836 # bright yellow +# bright4=5dc5f8 # bright blue +# bright5=feabf2 # bright magenta +# bright6=24dfc4 # bright cyan +# bright7=ffffff # bright white + +## dimmed colors (see foot.ini(5) man page) +# dim0= +# ... +# dim7= + +## The remaining 256-color palette +# 16 = <256-color palette #16> +# ... +# 255 = <256-color palette #255> + +## Misc colors +# selection-foreground= +# selection-background= +# jump-labels= # black-on-yellow +# scrollback-indicator= # black-on-bright-blue +# search-box-no-match= # black-on-red +# search-box-match= # black-on-yellow +# urls= + +[csd] +# preferred=server +# size=26 +# font= +# color= +# hide-when-maximized=no +# double-click-to-maximize=yes +# border-width=0 +# border-color= +# button-width=26 +# button-color= +# button-minimize-color= +# button-maximize-color= +# button-close-color= + +[key-bindings] +# scrollback-up-page=Shift+Page_Up +# scrollback-up-half-page=none +# scrollback-up-line=none +# scrollback-down-page=Shift+Page_Down +# scrollback-down-half-page=none +# scrollback-down-line=none +# scrollback-home=none +# scrollback-end=none +# clipboard-copy=Control+Shift+c XF86Copy +# clipboard-paste=Control+Shift+v XF86Paste +# primary-paste=Shift+Insert +# search-start=Control+Shift+r +# font-increase=Control+plus Control+equal Control+KP_Add +# font-decrease=Control+minus Control+KP_Subtract +# font-reset=Control+0 Control+KP_0 +# spawn-terminal=Control+Shift+n +# minimize=none +# maximize=none +# fullscreen=none +# pipe-visible=[sh -c "xurls | fuzzel | xargs -r firefox"] none +# pipe-scrollback=[sh -c "xurls | fuzzel | xargs -r firefox"] none +# pipe-selected=[xargs -r firefox] none +# show-urls-launch=Control+Shift+o +# show-urls-copy=none +# show-urls-persistent=none +# prompt-prev=Control+Shift+z +# prompt-next=Control+Shift+x +# unicode-input=Control+Shift+u +# noop=none + +[search-bindings] +# cancel=Control+g Control+c Escape +# commit=Return +# find-prev=Control+r +# find-next=Control+s +# cursor-left=Left Control+b +# cursor-left-word=Control+Left Mod1+b +# cursor-right=Right Control+f +# cursor-right-word=Control+Right Mod1+f +# cursor-home=Home Control+a +# cursor-end=End Control+e +# delete-prev=BackSpace +# delete-prev-word=Mod1+BackSpace Control+BackSpace +# delete-next=Delete +# delete-next-word=Mod1+d Control+Delete +# extend-char=Shift+Right +# extend-to-word-boundary=Control+w Control+Shift+Right +# extend-to-next-whitespace=Control+Shift+w +# extend-line-down=Shift+Down +# extend-backward-char=Shift+Left +# extend-backward-to-word-boundary=Control+Shift+Left +# extend-backward-to-next-whitespace=none +# extend-line-up=Shift+Up +# clipboard-paste=Control+v Control+Shift+v Control+y XF86Paste +# primary-paste=Shift+Insert +# unicode-input=none +# scrollback-up-page=Shift+Page_Up +# scrollback-up-half-page=none +# scrollback-up-line=none +# scrollback-down-page=Shift+Page_Down +# scrollback-down-half-page=none +# scrollback-down-line=none +# scrollback-home=none +# scrollback-end=none + +[url-bindings] +# cancel=Control+g Control+c Control+d Escape +# toggle-url-visible=t + +[text-bindings] +# \x03=Mod4+c # Map Super+c -> Ctrl+c + +[mouse-bindings] +# scrollback-up-mouse=BTN_BACK +# scrollback-down-mouse=BTN_FORWARD +# selection-override-modifiers=Shift +# primary-paste=BTN_MIDDLE +# select-begin=BTN_LEFT +# select-begin-block=Control+BTN_LEFT +# select-extend=BTN_RIGHT +# select-extend-character-wise=Control+BTN_RIGHT +# select-word=BTN_LEFT-2 +# select-word-whitespace=Control+BTN_LEFT-2 +# select-quote = BTN_LEFT-3 +# select-row=BTN_LEFT-4 + +# vim: ft=dosini diff --git a/.config/fuzzel/fuzzel.ini b/.config/fuzzel/fuzzel.ini new file mode 100644 index 0000000..3b471b9 --- /dev/null +++ b/.config/fuzzel/fuzzel.ini @@ -0,0 +1,8 @@ +font=noto mono +icon-theme=hicolor + +[colors] +background=242424dd +text=ebebebff +selection=1e1e1edd +selection-text=fafafaff diff --git a/.config/micro/bindings.json b/.config/micro/bindings.json new file mode 100644 index 0000000..83659cb --- /dev/null +++ b/.config/micro/bindings.json @@ -0,0 +1,4 @@ +{ + "Alt-/": "lua:comment.comment", + "CtrlUnderscore": "lua:comment.comment" +} diff --git a/.config/micro/buffers/history b/.config/micro/buffers/history new file mode 100644 index 0000000..4c94627 Binary files /dev/null and b/.config/micro/buffers/history differ diff --git a/.config/micro/colorschemes/monokai-dark.micro b/.config/micro/colorschemes/monokai-dark.micro new file mode 100644 index 0000000..3a1e89c --- /dev/null +++ b/.config/micro/colorschemes/monokai-dark.micro @@ -0,0 +1,28 @@ +color-link default "#D5D8D6,#1D0000" +color-link comment "#75715E" +color-link identifier "#66D9EF" +color-link constant "#AE81FF" +color-link constant.string "#E6DB74" +color-link constant.string.char "#BDE6AD" +color-link statement "#F92672" +color-link preproc "#CB4B16" +color-link type "#66D9EF" +color-link special "#A6E22E" +color-link underlined "#D33682" +color-link error "bold #CB4B16" +color-link todo "bold #D33682" +color-link hlsearch "#1D0000,#E6DB74" +color-link statusline "#282828,#F8F8F2" +color-link indent-char "#505050,#282828" +color-link line-number "#AAAAAA,#282828" +color-link current-line-number "#AAAAAA,#1D0000" +color-link diff-added "#00AF00" +color-link diff-modified "#FFAF00" +color-link diff-deleted "#D70000" +color-link gutter-error "#CB4B16" +color-link gutter-warning "#E6DB74" +color-link cursor-line "#323232" +color-link color-column "#323232" +color-link match-brace "#1D0000,#AE81FF" +color-link tab-error "#D75F5F" +color-link trailingws "#D75F5F" diff --git a/.config/micro/plug/detectindent/README.md b/.config/micro/plug/detectindent/README.md new file mode 100644 index 0000000..18c4c2a --- /dev/null +++ b/.config/micro/plug/detectindent/README.md @@ -0,0 +1,3 @@ +# Detectindent plugin for Micro text editor + +Whenever a file is opened, the detectindent plugin tries to automatically detect its indentation style (spaces or tabs) and set the `tabstospaces` option accordingly. If spaces indentation is detected, it also tries to detect the indentation width and set the `tabsize` option accordingly. diff --git a/.config/micro/plug/detectindent/detectindent.lua b/.config/micro/plug/detectindent/detectindent.lua new file mode 100644 index 0000000..f5ed694 --- /dev/null +++ b/.config/micro/plug/detectindent/detectindent.lua @@ -0,0 +1,70 @@ +VERSION = "1.1.0" + +local config = import("micro/config") +local util = import("micro/util") + +function onBufferOpen(buf) + local spaces, tabs = 0, 0 + local space_count, prev_space_count = 0, 0 + local tabsizes = {} + local i = 0 + while spaces + tabs < 500 and i < 1000 and i < buf:LinesNum() do + space_count = 0 + local line = buf:Line(i) + local r = util.RuneAt(line, 0) + if r == " " then + space_count = string.len(util.GetLeadingWhitespace(line)) + -- for tab vs space detection, ignore space indents of 1 + -- which can be a false signal from C-style block comments + if space_count > 1 then + spaces = spaces + 1 + end + + -- treat whitespace-only lines as not changing the indent + if string.len(line) == space_count then + space_count = prev_space_count + end + -- look for an increasing number of spaces, ignoring increases of 1 + -- which can be a false signal from C-style block comments + if space_count > prev_space_count + 1 then + local t = space_count - prev_space_count + if tabsizes[t] == nil then + tabsizes[t] = 1 + else + tabsizes[t] = tabsizes[t] + 1 + end + end + elseif r == "\t" then + tabs = tabs + 1 + -- treat tabbed lines as not affecting spaced indentation + space_count = prev_space_count + elseif r == "" then + -- treat empty lines as not changing the indent + space_count = prev_space_count + end + prev_space_count = space_count + i = i + 1 + end + + if spaces > tabs then + buf.Settings["tabstospaces"] = true + -- get the indentation change used for the largest number of lines + local tabsize = -1 + local maxcount = 0 + for t, count in pairs(tabsizes) do + if count > maxcount then + maxcount = count + tabsize = t + end + end + if tabsize > 0 then + buf.Settings["tabsize"] = tabsize + end + elseif tabs > spaces then + buf.Settings["tabstospaces"] = false + end +end + +function init() + config.AddRuntimeFile("detectindent", config.RTHelp, "help/detectindent.md") +end diff --git a/.config/micro/plug/detectindent/help/detectindent.md b/.config/micro/plug/detectindent/help/detectindent.md new file mode 100644 index 0000000..d310b02 --- /dev/null +++ b/.config/micro/plug/detectindent/help/detectindent.md @@ -0,0 +1,12 @@ +# Detectindent + +Whenever a file is opened, the detectindent plugin tries to automatically +detect its indentation style (spaces or tabs) and set the `tabstospaces` +option accordingly. If spaces indentation is detected, it also tries to +detect the indentation width and set the `tabsize` option accordingly. + +To disable it, just turn off the plugin: + +``` +set detectindent off +``` diff --git a/.config/micro/plug/detectindent/repo.json b/.config/micro/plug/detectindent/repo.json new file mode 100644 index 0000000..acdd491 --- /dev/null +++ b/.config/micro/plug/detectindent/repo.json @@ -0,0 +1,14 @@ +[{ + "Name": "detectindent", + "Description": "Automatically detect indentation settings", + "Tags": ["indent", "detect"], + "Versions": [ + { + "Version": "1.1.0", + "Url": "https://github.com/dmaluka/micro-detectindent/archive/v1.1.0.zip", + "Require": { + "micro": ">=2.0.0" + } + } + ] +}] diff --git a/.config/micro/plug/filemanager/.editorconfig b/.config/micro/plug/filemanager/.editorconfig new file mode 100644 index 0000000..a34d753 --- /dev/null +++ b/.config/micro/plug/filemanager/.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/.config/micro/plug/filemanager/CHANGELOG.md b/.config/micro/plug/filemanager/CHANGELOG.md new file mode 100644 index 0000000..46d51c4 --- /dev/null +++ b/.config/micro/plug/filemanager/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/.config/micro/plug/filemanager/LICENSE b/.config/micro/plug/filemanager/LICENSE new file mode 100644 index 0000000..ae6fb2f --- /dev/null +++ b/.config/micro/plug/filemanager/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/.config/micro/plug/filemanager/README.md b/.config/micro/plug/filemanager/README.md new file mode 100644 index 0000000..79d983d --- /dev/null +++ b/.config/micro/plug/filemanager/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/.config/micro/plug/filemanager/example.jpg b/.config/micro/plug/filemanager/example.jpg new file mode 100644 index 0000000..ab5bf1c Binary files /dev/null and b/.config/micro/plug/filemanager/example.jpg differ diff --git a/.config/micro/plug/filemanager/filemanager.lua b/.config/micro/plug/filemanager/filemanager.lua new file mode 100644 index 0000000..fef17d4 --- /dev/null +++ b/.config/micro/plug/filemanager/filemanager.lua @@ -0,0 +1,1387 @@ +VERSION = "3.5.1" + +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 = shell.ExecCommand('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 = + shell.ExecCommand('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 = filepath.Dir(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 filepath.Dir 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(bp, args) + + 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 #args < 1 then + micro.InfoBar():Error('When using "rename" you need to input a new name') + return + end + + local new_name = args[1] + + -- +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(bp, args) + + -- Safety check they actually passed a name + if #args < 1 then + micro.InfoBar():Error('When using "touch" you need to input a file name') + return + end + + local file_name = args[1] + + -- False because not a dir + create_filedir(file_name, false) +end + +-- Triggered with "mkdir dirname" +function new_dir(bp, args) + + -- Safety check they actually passed a name + if #args < 1 then + micro.InfoBar():Error('When using "mkdir" you need to input a dir name') + return + end + + local dir_name = args[1] + + -- True because dir + create_filedir(dir_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/.config/micro/plug/filemanager/repo.json b/.config/micro/plug/filemanager/repo.json new file mode 100644 index 0000000..4bd7de2 --- /dev/null +++ b/.config/micro/plug/filemanager/repo.json @@ -0,0 +1,45 @@ +[ + { + "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/micro-editor/updated-plugins/releases/download/v1.0.0/filemanager-3.5.0.zip", + "Require": { + "micro": ">=2.0.0-1" + } + }, + { + "Version": "3.5.1", + "Url": "https://github.com/micro-editor/updated-plugins/releases/download/v1.0.0/filemanager-3.5.1.zip", + "Require": { + "micro": ">=2.0.0-1" + } + } + ] + } +] diff --git a/.config/micro/plug/filemanager/syntax.yaml b/.config/micro/plug/filemanager/syntax.yaml new file mode 100644 index 0000000..da001cf --- /dev/null +++ b/.config/micro/plug/filemanager/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/.config/micro/settings.json b/.config/micro/settings.json new file mode 100644 index 0000000..5246e61 --- /dev/null +++ b/.config/micro/settings.json @@ -0,0 +1,3 @@ +{ + "colorscheme": "monokai" +} diff --git a/.config/sway/config b/.config/sway/config new file mode 100644 index 0000000..ea4c5bc --- /dev/null +++ b/.config/sway/config @@ -0,0 +1,228 @@ +# Default config for sway +# +# Copy this to ~/.config/sway/config and edit it to your liking. +# +# Read `man 5 sway` for a complete reference. + +### Variables +# +# Logo key. Use Mod1 for Alt. +set $mod Mod4 +# Home row direction keys, like vim +set $left h +set $down j +set $up k +set $right l +# Your preferred terminal emulator +set $term foot +# Your preferred application launcher +# Note: pass the final command to swaymsg so that the resulting window can be opened +# on the original workspace that the command was run on. +# set $menu dmenu_path | wmenu | xargs swaymsg exec -- +# set $menu /usr/bin/bemenu-run +set $menu /usr/bin/fuzzel + +include /etc/sway/config-vars.d/* + +### Output configuration +# +# Default wallpaper (more resolutions are available in /usr/share/backgrounds/sway/) +#output * bg /usr/share/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill + +output * bg /home/winsdominoes/Pictures/road.webp fill + +# +# Example configuration: +# +# output HDMI-A-1 resolution 1920x1080 position 1920,0 +# +# You can get the names of your outputs by running: swaymsg -t get_outputs + +output eDP-1 mode 3200x1800 position 3200,0 + +### Idle configuration +# +# Example configuration: +# +# exec swayidle -w \ +# timeout 300 'swaylock -f -c 000000' \ +# timeout 600 'swaymsg "output * power off"' resume 'swaymsg "output * power on"' \ +# before-sleep 'swaylock -f -c 000000' +# +# This will lock your screen after 300 seconds of inactivity, then turn off +# your displays after another 300 seconds, and turn your screens back on when +# resumed. It will also lock your screen before your computer goes to sleep. + +### Input configuration +# +# Example configuration: +# +# input "2:14:SynPS/2_Synaptics_TouchPad" { +# dwt enabled +# tap enabled +# natural_scroll enabled +# middle_emulation enabled +# } +# +# You can get the names of your inputs by running: swaymsg -t get_inputs +# Read `man 5 sway-input` for more information about this section. + +### Key bindings +# +# Basics: +# + # Start a terminal + bindsym $mod+Return exec $term + + # Kill focused window + bindsym $mod+Shift+q kill + + # Start your launcher + bindsym $mod+d exec $menu + + # Drag floating windows by holding down $mod and left mouse button. + # Resize them with right mouse button + $mod. + # Despite the name, also works for non-floating windows. + # Change normal to inverse to use left mouse button for resizing and right + # mouse button for dragging. + floating_modifier $mod normal + + # Reload the configuration file + bindsym $mod+Shift+c reload + + # Exit sway (logs you out of your Wayland session) + bindsym $mod+Shift+e exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -B 'Yes, exit sway' 'swaymsg exit' +# +# Moving around: +# + # Move your focus around + bindsym $mod+$left focus left + bindsym $mod+$down focus down + bindsym $mod+$up focus up + bindsym $mod+$right focus right + # Or use $mod+[up|down|left|right] + bindsym $mod+Left focus left + bindsym $mod+Down focus down + bindsym $mod+Up focus up + bindsym $mod+Right focus right + + # Move the focused window with the same, but add Shift + bindsym $mod+Shift+$left move left + bindsym $mod+Shift+$down move down + bindsym $mod+Shift+$up move up + bindsym $mod+Shift+$right move right + # Ditto, with arrow keys + bindsym $mod+Shift+Left move left + bindsym $mod+Shift+Down move down + bindsym $mod+Shift+Up move up + bindsym $mod+Shift+Right move right +# +# Workspaces: +# + # Switch to workspace + bindsym $mod+1 workspace number 1 + bindsym $mod+2 workspace number 2 + bindsym $mod+3 workspace number 3 + bindsym $mod+4 workspace number 4 + bindsym $mod+5 workspace number 5 + bindsym $mod+6 workspace number 6 + bindsym $mod+7 workspace number 7 + bindsym $mod+8 workspace number 8 + bindsym $mod+9 workspace number 9 + bindsym $mod+0 workspace number 10 + # Move focused container to workspace + bindsym $mod+Shift+1 move container to workspace number 1 + bindsym $mod+Shift+2 move container to workspace number 2 + bindsym $mod+Shift+3 move container to workspace number 3 + bindsym $mod+Shift+4 move container to workspace number 4 + bindsym $mod+Shift+5 move container to workspace number 5 + bindsym $mod+Shift+6 move container to workspace number 6 + bindsym $mod+Shift+7 move container to workspace number 7 + bindsym $mod+Shift+8 move container to workspace number 8 + bindsym $mod+Shift+9 move container to workspace number 9 + bindsym $mod+Shift+0 move container to workspace number 10 + # Note: workspaces can have any name you want, not just numbers. + # We just use 1-10 as the default. +# +# Layout stuff: +# + # You can "split" the current object of your focus with + # $mod+b or $mod+v, for horizontal and vertical splits + # respectively. + bindsym $mod+b splith + bindsym $mod+v splitv + + # Switch the current container between different layout styles + bindsym $mod+s layout stacking + bindsym $mod+w layout tabbed + bindsym $mod+e layout toggle split + + # Make the current focus fullscreen + bindsym $mod+f fullscreen + + # Toggle the current focus between tiling and floating mode + bindsym $mod+Shift+space floating toggle + + # Swap focus between the tiling area and the floating area + bindsym $mod+space focus mode_toggle + + # Move focus to the parent container + bindsym $mod+a focus parent +# +# Scratchpad: +# + # Sway has a "scratchpad", which is a bag of holding for windows. + # You can send windows there and get them back later. + + # Move the currently focused window to the scratchpad + bindsym $mod+Shift+minus move scratchpad + + # Show the next scratchpad window or hide the focused scratchpad window. + # If there are multiple scratchpad windows, this command cycles through them. + bindsym $mod+minus scratchpad show +# +# Resizing containers: +# +mode "resize" { + # left will shrink the containers width + # right will grow the containers width + # up will shrink the containers height + # down will grow the containers height + bindsym $left resize shrink width 10px + bindsym $down resize grow height 10px + bindsym $up resize shrink height 10px + bindsym $right resize grow width 10px + + # Ditto, with arrow keys + bindsym Left resize shrink width 10px + bindsym Down resize grow height 10px + bindsym Up resize shrink height 10px + bindsym Right resize grow width 10px + + # Return to default mode + bindsym Return mode "default" + bindsym Escape mode "default" +} +bindsym $mod+r mode "resize" + +# +# Status Bar: +# +# Read `man 5 sway-bar` for more information about this section. +bar { + position bottom + + # When the status_command prints a new line to stdout, swaybar updates. + # The default just shows the current date and time. + # status_command while date +'%Y-%m-%d %X'; do sleep 1; done + + status_command i3status + + colors { + statusline #ffffff + background #323232 + inactive_workspace #32323200 #32323200 #5c5c5c + } +} + +include /etc/sway/config.d/*