├── .aliases
├── .config
├── fish
│ ├── conf.d
│ │ ├── my.fish
│ │ ├── my.osx.fish
│ │ └── my.termux.fish
│ └── functions
│ │ ├── brew_activate.fish
│ │ └── fish_prompt.fish
└── micro
│ ├── bindings.chromeos.json
│ ├── bindings.json
│ ├── plug
│ ├── filemanager
│ │ ├── .editorconfig
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── example.jpg
│ │ ├── filemanager.lua
│ │ ├── repo.json
│ │ └── syntax.yaml
│ └── manipulator
│ │ ├── .gitignore
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── demo.gif
│ │ ├── demo.txt
│ │ ├── help
│ │ └── manipulator.md
│ │ ├── manipulator.lua
│ │ └── repo.json
│ └── settings.json
├── .gemrc
├── .gitconfig
├── .gitconfig.local.template
├── .gitignore
├── .inputrc
├── .screenrc
├── .termux
└── termux.properties
├── .tilix.dconf
├── .tmux.conf
├── .vim
└── syntax
│ └── txt2tags.vim
├── .vimrc
├── README.md
├── bin
├── git-check-author
├── git-cleanup
├── git-pre-commit
├── git-pull-forced
└── git-replace-text
└── setup
/.aliases:
--------------------------------------------------------------------------------
1 | # Aliases used by Bash and Fish (same syntax)
2 |
3 | # Lazy
4 | alias ..="cd .."
5 | alias ...="cd ../.."
6 | alias ....="cd ../../.."
7 | alias .....="cd ../../../.."
8 | alias l='ls -la'
9 |
10 | # Colors ON
11 | alias ls='ls --color=auto'
12 | alias grep='grep --color' # in Termux: pkg install grep
13 |
14 | # Docker
15 | alias docker-gc='docker system prune'
16 |
17 | # JSON
18 | alias json-pp='python -m json.tool'
19 |
20 | # Tmux: have a different session for each path
21 | alias temu='tmux new -A -D -s $PWD'
22 |
23 | # Web
24 | # curl -L https://raw.github.com/aureliojargas/css-grep/master/css-grep.txt > ~/.css.txt
25 | alias css='cat ~/.css.txt | grep -i'
26 |
27 | # VS Code
28 | # List of absolute paths for files in the current dir.
29 | # Use it from VS Code Terminal and click a path to open it in the editor.
30 | alias code-files='find "$PWD" -type f | grep -Fv /.git/'
31 |
32 | # Fish
33 | alias fish-my-config='find ~/.config/fish/ -not -type d'
34 |
35 | # Git aliases
36 | alias gg='git grep --line-number'
37 | alias ggf='git grep-filename'
38 | alias gs='git status'
39 | alias gw='git show'
40 | alias gws='git show --stat'
41 | alias gsl='git stash list'
42 | alias gsd='git stash show -p stash@{0}'
43 | alias gd='git diff'
44 | alias gdw='GIT_PAGER= git diff' # wrap long lines
45 | alias gdc='git diff --cached'
46 | alias ga='git add'
47 | alias gau='git add -u'
48 | alias gap='git add -p'
49 | alias grp='git reset -p'
50 | alias gc='git commit'
51 | alias gca='git-pre-commit; git commit --amend'
52 | alias gcaa='git commit --amend --no-verify --no-edit'
53 | alias gcm='git commit-message'
54 | alias gpf='git push --force-with-lease --force-if-includes' # safer force-push
55 | alias gpu='git push -u'
56 | alias gb='git branch'
57 | alias gba='git branch -va'
58 | alias gr='git remote -v'
59 | alias gl='git log4'
60 | alias gl2='git log2'
61 | alias gl3='git log3'
62 |
63 | # ed: Show a ruler as reference to git commit 50/72 max width
64 | alias ed-ruler="echo '-----------------------------------------------50|-------------------72|'"
65 |
66 | # vim: filetype=sh
67 |
--------------------------------------------------------------------------------
/.config/fish/conf.d/my.fish:
--------------------------------------------------------------------------------
1 | # Activate brew tools (if available)
2 | brew_activate
3 |
4 | # Python tools installed with pipx
5 | fish_add_path top ~/.local/bin
6 |
7 | # Add my dear ~/bin to PATH
8 | fish_add_path ~/bin
9 |
10 | # https://github.com/fish-shell/fish-shell/issues/5394
11 | if status is-interactive
12 |
13 | # GNU ls: yellow folders, not blue
14 | set -x LS_COLORS 'di=33'
15 |
16 | # Dear Python venv, please leave my prompt alone
17 | # https://github.com/pypa/virtualenv/blob/adcf327/src/virtualenv/activation/fish/activate.fish#L80
18 | set -x VIRTUAL_ENV_DISABLE_PROMPT 1
19 |
20 | # Load aliases
21 | test -r ~/.aliases
22 | and source ~/.aliases
23 |
24 | # Colors ON
25 | # Use functions, not aliases: https://github.com/fish-shell/fish-shell/issues/6899
26 | functions --erase ls grep # remove aliases
27 | function ls
28 | command ls --color=auto $argv
29 | end
30 | function grep
31 | command grep --color $argv
32 | end
33 |
34 | # I always forget those commands
35 | function ssh-no-password
36 | eval (ssh-agent -c)
37 | ssh-add ~/.ssh/id_rsa
38 | end
39 | end
40 |
--------------------------------------------------------------------------------
/.config/fish/conf.d/my.osx.fish:
--------------------------------------------------------------------------------
1 | # Use brew GNU utils (gsed, gcut, gwc, ...) without the g prefix
2 | fish_add_path /opt/homebrew/opt/coreutils/libexec/gnubin
3 |
4 | # Use brew ruby by default (Ruby 2.7 for Jekyll 4.1.1)
5 | # fish_add_path /usr/local/opt/ruby/bin
6 |
--------------------------------------------------------------------------------
/.config/fish/conf.d/my.termux.fish:
--------------------------------------------------------------------------------
1 | # Termux is a Linux emulator for Android
2 | # https://termux.com
3 |
4 | # https://github.com/fish-shell/fish-shell/issues/5394
5 | if status is-interactive
6 |
7 | # Requirement: https://wiki.termux.com/wiki/Termux:API
8 | alias pbcopy=termux-clipboard-set
9 | alias pbpaste=termux-clipboard-get
10 |
11 | # Default cursor is a non-blinking block. Change it to |
12 | # https://www.reddit.com/r/termux/comments/d9rxeo/how_do_i_change_cursor/
13 | printf '\e[6 q'
14 |
15 | # `pkg install micro` is broken for me
16 | # Since I set micro as the git editor, map it to Vim (aliases won't work)
17 | ln -s (command -v vim) ~/bin/micro
18 | end
19 |
--------------------------------------------------------------------------------
/.config/fish/functions/brew_activate.fish:
--------------------------------------------------------------------------------
1 | function brew_activate --description 'Activate brew environment (if brew is available)'
2 |
3 | # brew is already setup, nothing to do
4 | set -q HOMEBREW_REPOSITORY; and return 0
5 |
6 | # Possible paths for the brew command (Mac, Linux)
7 | set -l brew_paths \
8 | /opt/homebrew/bin/brew \
9 | /home/linuxbrew/.linuxbrew/bin/brew
10 |
11 | # Activate the first path found (if any)
12 | for brew in $brew_paths
13 | if command --query $brew
14 | eval ($brew shellenv)
15 | break
16 | end
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/.config/fish/functions/fish_prompt.fish:
--------------------------------------------------------------------------------
1 | function fish_prompt --description 'Write out the prompt'
2 | # Quick hack to disable the fancy prompt and get a simple one
3 | # echo '$ '
4 | # return
5 |
6 | set -l last_pipestatus $pipestatus
7 |
8 | if not set -q __fish_git_prompt_show_informative_status
9 | set -g __fish_git_prompt_show_informative_status 1
10 | end
11 | if not set -q __fish_git_prompt_hide_untrackedfiles
12 | set -g __fish_git_prompt_hide_untrackedfiles 1
13 | end
14 | if not set -q __fish_git_prompt_color_branch
15 | set -g __fish_git_prompt_color_branch magenta --bold
16 | end
17 | if not set -q __fish_git_prompt_showupstream
18 | set -g __fish_git_prompt_showupstream informative
19 | end
20 | if not set -q __fish_git_prompt_char_upstream_ahead
21 | set -g __fish_git_prompt_char_upstream_ahead "↑"
22 | end
23 | if not set -q __fish_git_prompt_char_upstream_behind
24 | set -g __fish_git_prompt_char_upstream_behind "↓"
25 | end
26 | if not set -q __fish_git_prompt_char_upstream_prefix
27 | set -g __fish_git_prompt_char_upstream_prefix ""
28 | end
29 | if not set -q __fish_git_prompt_char_stagedstate
30 | set -g __fish_git_prompt_char_stagedstate "●"
31 | end
32 | if not set -q __fish_git_prompt_char_dirtystate
33 | set -g __fish_git_prompt_char_dirtystate "✚"
34 | end
35 | if not set -q __fish_git_prompt_char_untrackedfiles
36 | set -g __fish_git_prompt_char_untrackedfiles "…"
37 | end
38 | if not set -q __fish_git_prompt_char_invalidstate
39 | set -g __fish_git_prompt_char_invalidstate "✖"
40 | end
41 | if not set -q __fish_git_prompt_char_cleanstate
42 | set -g __fish_git_prompt_char_cleanstate "✔"
43 | end
44 | if not set -q __fish_git_prompt_color_dirtystate
45 | set -g __fish_git_prompt_color_dirtystate blue
46 | end
47 | if not set -q __fish_git_prompt_color_stagedstate
48 | set -g __fish_git_prompt_color_stagedstate yellow
49 | end
50 | if not set -q __fish_git_prompt_color_invalidstate
51 | set -g __fish_git_prompt_color_invalidstate red
52 | end
53 | if not set -q __fish_git_prompt_color_untrackedfiles
54 | set -g __fish_git_prompt_color_untrackedfiles $fish_color_normal
55 | end
56 | if not set -q __fish_git_prompt_color_cleanstate
57 | set -g __fish_git_prompt_color_cleanstate green --bold
58 | end
59 |
60 | set -l color_cwd
61 | set -l prefix
62 | set -l suffix
63 | switch "$USER"
64 | case root toor
65 | if set -q fish_color_cwd_root
66 | set color_cwd $fish_color_cwd_root
67 | else
68 | set color_cwd $fish_color_cwd
69 | end
70 | set suffix '#'
71 | case '*'
72 | set color_cwd $fish_color_cwd
73 | set suffix '$'
74 | end
75 |
76 | # horizontal line
77 | set_color green
78 | string repeat -n $COLUMNS –
79 | set_color normal
80 |
81 | # show a red "ssh" if we're on a remote machine
82 | if set -q SSH_TTY; or set -q SSH_CLIENT
83 | set_color red
84 | echo -n 'ssh '
85 | set_color normal
86 | end
87 |
88 | # working dir
89 | set_color green
90 | echo -n (prompt_pwd)
91 | set_color normal
92 |
93 | # git
94 | printf '%s ' (fish_vcs_prompt)
95 |
96 | # show activated Python virtual env
97 | if set -q VIRTUAL_ENV
98 | set_color yellow
99 | printf '[%s] ' (basename $VIRTUAL_ENV)
100 | set_color normal
101 | end
102 |
103 | # exit status from last command (e.g.: "[1]" in red)
104 | set -l pipestatus_string (__fish_print_pipestatus "[" "] " "|" (set_color $fish_color_status) (set_color --bold $fish_color_status) $last_pipestatus)
105 | echo -n $pipestatus_string
106 | set_color normal
107 |
108 | # line break
109 | echo
110 |
111 | # finally, the actual "$ " prompt
112 | echo -n "$suffix "
113 | end
114 |
--------------------------------------------------------------------------------
/.config/micro/bindings.chromeos.json:
--------------------------------------------------------------------------------
1 | {
2 | "Alt-/": "lua:comment.comment",
3 | "Alt-q": "command-edit:textfilter fmt -w 80 -p #",
4 | "CtrlUnderscore": "lua:comment.comment",
5 | "PageDown": "MoveLinesDown",
6 | "PageUp": "MoveLinesUp"
7 | }
8 |
--------------------------------------------------------------------------------
/.config/micro/bindings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Alt-/": "lua:comment.comment",
3 | "Alt-q": "command-edit:textfilter fmt -w 80 -p #",
4 | "CtrlUnderscore": "lua:comment.comment",
5 | }
6 |
--------------------------------------------------------------------------------
/.config/micro/plug/filemanager/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: http://EditorConfig.org
2 |
3 | # top-most EditorConfig file
4 | root = true
5 |
6 | # Unix-style newlines with a newline ending every file
7 | [*]
8 | charset = utf-8
9 | end_of_line = lf
10 | indent_style = tab
11 | indent_size = 2
12 | insert_final_newline = true
13 | trim_trailing_whitespace = true
14 |
15 | # Ignore .md files, because 2 spaces at end-of-line has meaning
16 | [*.md]
17 | trim_trailing_whitespace = false
18 |
--------------------------------------------------------------------------------
/.config/micro/plug/filemanager/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file.
4 |
5 | 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).
6 |
7 | ## [Unreleased]
8 |
9 | ## [3.4.0] - 2018-10-22
10 |
11 | ### Fixed
12 |
13 | - 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)
14 |
15 | ### Added
16 |
17 | - Adds the option `filemanager-openonstart` to allow auto-opening the file tree when Micro is started (default OFF)
18 |
19 | ### Changed
20 |
21 | - Update README's option's documentation
22 |
23 | ## [3.3.1] - 2018-10-03
24 |
25 | ### Changed
26 |
27 | - Performance improvement by removing unnecessary refresh of the opened file, [thanks to @jackwilsdon](https://github.com/NicolaiSoeborg/filemanager-plugin/pull/37)
28 |
29 | ## [3.3.0] - 2018-09-13
30 |
31 | ### Added
32 |
33 | - The ability to sort folders above files, [thanks to @cbrown1](https://github.com/NicolaiSoeborg/filemanager-plugin/pull/33)
34 |
35 | ### Fixed
36 |
37 | - The displayed filenames are now correctly only showing their "basename" on Windows
38 |
39 | ## [3.2.0] - 2018-02-15
40 |
41 | ### Added
42 |
43 | - The ability to go to parent directory with left arrow (when not minimizing). Thanks @avently
44 | - The ability to jump to the `..` as a "parent directory". Thanks @avently
45 |
46 | ## [3.1.2] - 2018-02-07
47 |
48 | ### Fixed
49 |
50 | - The minimum Micro version, which was incorrectly set to v1.4.0. Ref [issue #28](https://github.com/NicolaiSoeborg/filemanager-plugin/issues/28)
51 |
52 | ## [3.1.1] - 2018-02-04
53 |
54 | ### Fixed
55 |
56 | Ref https://github.com/zyedidia/micro/issues/992 for both of these fixes.
57 |
58 | - The syntax parser not loading correctly (mostly block comments) on opened files. **Requires Micro >= v1.4.0**
59 | - An errant tab being inserted into the newly opened file.
60 |
61 | ## [3.1.0] - 2018-01-30
62 |
63 | ### Added
64 |
65 | - The ability to hide dotfiles using the `filemanager-showdotfiles` option.
66 | - The ability to hide files ignored in your VCS (aka `.gitignore`'d) using the `filemanager-showignored` option. Only works with Git at the moment.
67 | - This `CHANGELOG.md`
68 |
69 | ### Fixed
70 |
71 | - A bug with the `rm` command that caused weird, undefined behaviour to contents within the same dir as the file/dir deleted.
72 | - Issue [#24](https://github.com/NicolaiSoeborg/filemanager-plugin/issues/24)
73 |
74 | ## [3.0.0] - 2018-01-10
75 |
76 | ### Fixed
77 |
78 | - 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)
79 | - The broken syntax highlighting
80 |
81 | ### Added
82 |
83 | - Directory expansion/compression below itself for viewing more akin to a file tree.
84 | - The `rm` command, which deletes the file/directory under the cursor.
85 | - The `touch` command, which creates a file with the passed filename.
86 | - The `mkdir` command, which creates a directory with the passed filename.
87 | - An API, of sorts, for the user to rebind their keys to if they dislike the defaults.
88 | - An [editorconfig](http://editorconfig.org/) file.
89 |
90 | ### Changed
91 |
92 | - The view that it spawns in to read-only, which requires Micro version >= 1.3.5
93 | - The functionality of some keybindings (when in the view) so they work safetly, or at all, with the plugin.
94 | - From the `enter` key to `tab` for opening/going into files/dirs (a side-effect of using the read-only setting)
95 |
96 | ### Removed
97 |
98 | - The ability to use a lot of keybindings that would otherwise mess with the view, and have no benifit.
99 | - The pointless `.gitignore` file.
100 |
101 | [unreleased]: https://github.com/NicolaiSoeborg/filemanager-plugin/compare/v3.4.0...HEAD
102 | [3.4.0]: https://github.com/NicolaiSoeborg/filemanager-plugin/compare/v3.3.1...v3.4.0
103 | [3.3.1]: https://github.com/NicolaiSoeborg/filemanager-plugin/compare/v3.3.0...v3.3.1
104 | [3.3.0]: https://github.com/NicolaiSoeborg/filemanager-plugin/compare/v3.2.0...v3.3.0
105 | [3.2.0]: https://github.com/NicolaiSoeborg/filemanager-plugin/compare/v3.1.2...v3.2.0
106 | [3.1.2]: https://github.com/NicolaiSoeborg/filemanager-plugin/compare/v3.1.1...v3.1.2
107 | [3.1.1]: https://github.com/NicolaiSoeborg/filemanager-plugin/compare/v3.1.0...v3.1.1
108 | [3.1.0]: https://github.com/NicolaiSoeborg/filemanager-plugin/compare/v3.0.0...v3.1.0
109 | [3.0.0]: https://github.com/NicolaiSoeborg/filemanager-plugin/compare/v2.1.1...v3.0.0
110 |
--------------------------------------------------------------------------------
/.config/micro/plug/filemanager/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Nicolai Søborg
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/.config/micro/plug/filemanager/README.md:
--------------------------------------------------------------------------------
1 | # Filemanager Plugin
2 |
3 | A simple plugin that allows for easy navigation of a file tree.
4 |
5 | 
6 |
7 | **Installation:** run `plugin install filemanager` and restart Micro.
8 |
9 | ## Basics
10 |
11 | The top line always has the current directory's path to show you where you are.\
12 | The `..` near the top is used to move back a directory, from your current position.
13 |
14 | All directories have a `/` added to the end of it, and are syntax-highlighted as a `special` character.\
15 | If the directory is expanded, there will be a `+` to the left of it. If it is collapsed there will be a `-` instead.
16 |
17 | **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.
18 |
19 | ### Options
20 |
21 | | Option | Purpose | Default |
22 | | :--------------------------- | :----------------------------------------------------------- | :------ |
23 | | `filemanager-showdotfiles` | Show dotfiles (hidden if false) | `true` |
24 | | `filemanager-showignored` | Show gitignore'd files (hidden if false) | `true` |
25 | | `filemanager-compressparent` | Collapse the parent dir when left is pressed on a child file | `true` |
26 | | `filemanager-foldersfirst` | Sorts folders above any files | `true` |
27 | | `filemanager-openonstart` | Automatically open the file tree when starting Micro | `false` |
28 |
29 | ### Commands and Keybindings
30 |
31 | 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.
32 |
33 | 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.
34 |
35 | | Command | Keybinding(s) | What it does | API for `bindings.json` |
36 | | :------- | :------------------------- | :------------------------------------------------------------------------------------------ | :------------------------------------ |
37 | | `tree` | - | Open/close the tree | `filemanager.toggle_tree` |
38 | | - | Tab & MouseLeft | Open a file, or go into the directory. Goes back a dir if on `..` | `filemanager.try_open_at_cursor` |
39 | | - | → | Expand directory in tree listing | `filemanager.uncompress_at_cursor` |
40 | | - | ← | Collapse directory listing | `filemanager.compress_at_cursor` |
41 | | - | Shift ⬆ | Go to the target's parent directory | `filemanager.goto_parent_dir` |
42 | | - | Alt Shift { | Jump to the previous directory in the view | `filemanager.goto_next_dir` |
43 | | - | Alt Shift } | Jump to the next directory in the view | `filemanager.goto_prev_dir` |
44 | | `rm` | - | Prompt to delete the target file/directory your cursor is on | `filemanager.prompt_delete_at_cursor` |
45 | | `rename` | - | Rename the file/directory your cursor is on, using the passed name | `filemanager.rename_at_cursor` |
46 | | `touch` | - | Make a new file under/into the file/directory your cursor is on, using the passed name | `filemanager.new_file` |
47 | | `mkdir` | - | Make a new directory under/into the file/directory your cursor is on, using the passed name | `filemanager.new_dir` |
48 |
49 | #### Notes
50 |
51 | - `rename`, `touch`, and `mkdir` require a name to be passed when calling.\
52 | Example: `rename newnamehere`, `touch filenamehere`, `mkdir dirnamehere`.\
53 | If the passed name already exists in the current dir, it will cancel instead of overwriting (for safety).
54 |
55 | - The Ctrl w keybinding is to switch which buffer your cursor is on.\
56 | This isn't specific to the plugin, it's just part of Micro, but many people seem to not know this.
57 |
--------------------------------------------------------------------------------
/.config/micro/plug/filemanager/example.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aureliojargas/dotfiles/f35732301f6592415f45038c00207c7e463b685e/.config/micro/plug/filemanager/example.jpg
--------------------------------------------------------------------------------
/.config/micro/plug/filemanager/filemanager.lua:
--------------------------------------------------------------------------------
1 | VERSION = "3.5.1"
2 |
3 | local micro = import("micro")
4 | local config = import("micro/config")
5 | local shell = import("micro/shell")
6 | local buffer = import("micro/buffer")
7 | local os = import("os")
8 | local filepath = import("path/filepath")
9 |
10 | -- Clear out all stuff in Micro's messenger
11 | local function clear_messenger()
12 | -- messenger:Reset()
13 | -- messenger:Clear()
14 | end
15 |
16 | -- Holds the micro.CurPane() we're manipulating
17 | local tree_view = nil
18 | -- Keeps track of the current working directory
19 | local current_dir = os.Getwd()
20 | -- Keep track of current highest visible indent to resize width appropriately
21 | local highest_visible_indent = 0
22 | -- Holds a table of paths -- objects from new_listobj() calls
23 | local scanlist = {}
24 |
25 | -- Get a new object used when adding to scanlist
26 | local function new_listobj(p, d, o, i)
27 | return {
28 | ["abspath"] = p,
29 | ["dirmsg"] = d,
30 | ["owner"] = o,
31 | ["indent"] = i,
32 | -- Since decreasing/increasing is common, we include these with the object
33 | ["decrease_owner"] = function(self, minus_num)
34 | self.owner = self.owner - minus_num
35 | end,
36 | ["increase_owner"] = function(self, plus_num)
37 | self.owner = self.owner + plus_num
38 | end
39 | }
40 | end
41 |
42 | -- Repeats a string x times, then returns it concatenated into one string
43 | local function repeat_str(str, len)
44 | -- Do NOT try to concat in a loop, it freezes micro...
45 | -- instead, use a temporary table to hold values
46 | local string_table = {}
47 | for i = 1, len do
48 | string_table[i] = str
49 | end
50 | -- Return the single string of repeated characters
51 | return table.concat(string_table)
52 | end
53 |
54 | -- A check for if a path is a dir
55 | local function is_dir(path)
56 | -- Used for checking if dir
57 | local golib_os = import("os")
58 | -- Returns a FileInfo on the current file/path
59 | local file_info, stat_error = golib_os.Stat(path)
60 | -- Wrap in nil check for file/dirs without read permissions
61 | if file_info ~= nil then
62 | -- Returns true/false if it's a dir
63 | return file_info:IsDir()
64 | else
65 | -- Couldn't stat the file/dir, usually because no read permissions
66 | micro.InfoBar():Error("Error checking if is dir: ", stat_error)
67 | -- Nil since we can't read the path
68 | return nil
69 | end
70 | end
71 |
72 | -- Returns a list of files (in the target dir) that are ignored by the VCS system (if exists)
73 | -- aka this returns a list of gitignored files (but for whatever VCS is found)
74 | local function get_ignored_files(tar_dir)
75 | -- True/false if the target dir returns a non-fatal error when checked with 'git status'
76 | local function has_git()
77 | local git_rp_results = shell.ExecCommand('git -C "' .. tar_dir .. '" rev-parse --is-inside-work-tree')
78 | return git_rp_results:match("^true%s*$")
79 | end
80 | local readout_results = {}
81 | -- TODO: Support more than just Git, such as Mercurial or SVN
82 | if has_git() then
83 | -- If the dir is a git dir, get all ignored in the dir
84 | local git_ls_results =
85 | shell.ExecCommand('git -C "' .. tar_dir .. '" ls-files . --ignored --exclude-standard --others --directory')
86 | -- Cut off the newline that is at the end of each result
87 | for split_results in string.gmatch(git_ls_results, "([^\r\n]+)") do
88 | -- git ls-files adds a trailing slash if it's a dir, so we remove it (if it is one)
89 | readout_results[#readout_results + 1] =
90 | (string.sub(split_results, -1) == "/" and string.sub(split_results, 1, -2) or split_results)
91 | end
92 | end
93 |
94 | -- Make sure we return a table
95 | return readout_results
96 | end
97 |
98 | -- Returns the basename of a path (aka a name without leading path)
99 | local function get_basename(path)
100 | if path == nil then
101 | micro.Log("Bad path passed to get_basename")
102 | return nil
103 | else
104 | -- Get Go's path lib for a basename callback
105 | local golib_path = import("filepath")
106 | return golib_path.Base(path)
107 | end
108 | end
109 |
110 | -- Returns true/false if the file is a dotfile
111 | local function is_dotfile(file_name)
112 | -- Check if the filename starts with a dot
113 | if string.sub(file_name, 1, 1) == "." then
114 | return true
115 | else
116 | return false
117 | end
118 | end
119 |
120 | -- Structures the output of the scanned directory content to be used in the scanlist table
121 | -- This is useful for both initial creation of the tree, and when nesting with uncompress_target()
122 | local function get_scanlist(dir, ownership, indent_n)
123 | local golib_ioutil = import("ioutil")
124 | -- Gets a list of all the files in the current dir
125 | local dir_scan, scan_error = golib_ioutil.ReadDir(dir)
126 |
127 | -- dir_scan will be nil if the directory is read-protected (no permissions)
128 | if dir_scan == nil then
129 | micro.InfoBar():Error("Error scanning dir: ", scan_error)
130 | return nil
131 | end
132 |
133 | -- The list of files to be returned (and eventually put in the view)
134 | local results = {}
135 | local files = {}
136 |
137 | local function get_results_object(file_name)
138 | local abs_path = filepath.Join(dir, file_name)
139 | -- Use "+" for dir's, "" for files
140 | local dirmsg = (is_dir(abs_path) and "+" or "")
141 | return new_listobj(abs_path, dirmsg, ownership, indent_n)
142 | end
143 |
144 | -- Save so we don't have to rerun GetOption a bunch
145 | local show_dotfiles = config.GetGlobalOption("filemanager.showdotfiles")
146 | local show_ignored = config.GetGlobalOption("filemanager.showignored")
147 | local folders_first = config.GetGlobalOption("filemanager.foldersfirst")
148 |
149 | -- The list of VCS-ignored files (if any)
150 | -- Only bother getting ignored files if we're not showing ignored
151 | local ignored_files = (not show_ignored and get_ignored_files(dir) or {})
152 | -- True/false if the file is an ignored file
153 | local function is_ignored_file(filename)
154 | for i = 1, #ignored_files do
155 | if ignored_files[i] == filename then
156 | return true
157 | end
158 | end
159 | return false
160 | end
161 |
162 | -- Hold the current scan's filename in most of the loops below
163 | local filename
164 |
165 | for i = 1, #dir_scan do
166 | local showfile = true
167 | filename = dir_scan[i]:Name()
168 | -- If we should not show dotfiles, and this is a dotfile, don't show
169 | if not show_dotfiles and is_dotfile(filename) then
170 | showfile = false
171 | end
172 | -- If we should not show ignored files, and this is an ignored file, don't show
173 | if not show_ignored and is_ignored_file(filename) then
174 | showfile = false
175 | end
176 | if showfile then
177 | -- This file is good to show, proceed
178 | if folders_first and not is_dir(filepath.Join(dir, filename)) then
179 | -- If folders_first and this is a file, add it to (temporary) files
180 | files[#files + 1] = get_results_object(filename)
181 | else
182 | -- Otherwise, add to results
183 | results[#results + 1] = get_results_object(filename)
184 | end
185 | end
186 | end
187 | if #files > 0 then
188 | -- Append any files to results, now that all folders have been added
189 | -- files will be > 0 only if folders_first and there are files
190 | for i = 1, #files do
191 | results[#results + 1] = files[i]
192 | end
193 | end
194 |
195 | -- Return the list of scanned files
196 | return results
197 | end
198 |
199 | -- A short "get y" for when acting on the scanlist
200 | -- Needed since we don't store the first 3 visible indicies in scanlist
201 | local function get_safe_y(optional_y)
202 | -- Default to 0 so we can check against and see if it's bad
203 | local y = 0
204 | -- Make the passed y optional
205 | if optional_y == nil then
206 | -- Default to cursor's Y loc if nothing was passed, instead of declaring another y
207 | optional_y = tree_view.Cursor.Loc.Y
208 | end
209 | -- 0/1/2 would be the top "dir, separator, .." so check if it's past
210 | if optional_y > 2 then
211 | -- -2 to conform to our scanlist, since zero-based Go index & Lua's one-based
212 | y = tree_view.Cursor.Loc.Y - 2
213 | end
214 | return y
215 | end
216 |
217 | -- Joins the target dir's leading path to the passed name
218 | local function dirname_and_join(path, join_name)
219 | -- The leading path to the dir we're in
220 | local leading_path = filepath.Dir(path)
221 | -- Joins with OS-specific slashes
222 | return filepath.Join(leading_path, join_name)
223 | end
224 |
225 | -- Hightlights the line when you move the cursor up/down
226 | local function select_line(last_y)
227 | -- Make last_y optional
228 | if last_y ~= nil then
229 | -- Don't let them move past ".." by checking the result first
230 | if last_y > 1 then
231 | -- If the last position was valid, move back to it
232 | tree_view.Cursor.Loc.Y = last_y
233 | end
234 | elseif tree_view.Cursor.Loc.Y < 2 then
235 | -- Put the cursor on the ".." if it's above it
236 | tree_view.Cursor.Loc.Y = 2
237 | end
238 |
239 | -- Puts the cursor back in bounds (if it isn't) for safety
240 | tree_view.Cursor:Relocate()
241 |
242 | -- Makes sure the cursor is visible (if it isn't)
243 | -- (false) means no callback
244 | tree_view:Center()
245 |
246 | -- Highlight the current line where the cursor is
247 | tree_view.Cursor:SelectLine()
248 | end
249 |
250 | -- Simple true/false if scanlist is currently empty
251 | local function scanlist_is_empty()
252 | if next(scanlist) == nil then
253 | return true
254 | else
255 | return false
256 | end
257 | end
258 |
259 | local function refresh_view()
260 | clear_messenger()
261 |
262 | -- If it's less than 30, just use 30 for width. Don't want it too small
263 |
264 | if tree_view:GetView().Width < 30 then
265 | tree_view:ResizePane(30)
266 | end
267 |
268 | -- Delete everything in the view/buffer
269 | tree_view.Buf.EventHandler:Remove(tree_view.Buf:Start(), tree_view.Buf:End())
270 |
271 | -- Insert the top 3 things that are always there
272 | -- Current dir
273 | tree_view.Buf.EventHandler:Insert(buffer.Loc(0, 0), current_dir .. "\n")
274 | -- An ASCII separator
275 | tree_view.Buf.EventHandler:Insert(buffer.Loc(0, 1), repeat_str("─", tree_view:GetView().Width) .. "\n")
276 | -- The ".." and use a newline if there are things in the current dir
277 | tree_view.Buf.EventHandler:Insert(buffer.Loc(0, 2), (#scanlist > 0 and "..\n" or ".."))
278 |
279 | -- Holds the current basename of the path (purely for display)
280 | local display_content
281 |
282 | -- NOTE: might want to not do all these concats in the loop, it can get slow
283 | for i = 1, #scanlist do
284 | -- The first 3 indicies are the dir/separator/"..", so skip them
285 | if scanlist[i].dirmsg ~= "" then
286 | -- Add the + or - to the left to signify if it's compressed or not
287 | -- Add a forward slash to the right to signify it's a dir
288 | display_content = scanlist[i].dirmsg .. " " .. get_basename(scanlist[i].abspath) .. "/"
289 | else
290 | -- Use the basename from the full path for display
291 | -- Two spaces to align with any directories, instead of being "off"
292 | display_content = " " .. get_basename(scanlist[i].abspath)
293 | end
294 |
295 | if scanlist[i].owner > 0 then
296 | -- Add a space and repeat it * the indent number
297 | display_content = repeat_str(" ", 2 * scanlist[i].indent) .. display_content
298 | end
299 |
300 | -- Newlines are needed for all inserts except the last
301 | -- If you insert a newline on the last, it leaves a blank spot at the bottom
302 | if i < #scanlist then
303 | display_content = display_content .. "\n"
304 | end
305 |
306 | -- Insert line-by-line to avoid out-of-bounds on big folders
307 | -- +2 so we skip the 0/1/2 positions that hold the top dir/separator/..
308 | tree_view.Buf.EventHandler:Insert(buffer.Loc(0, i + 2), display_content)
309 | end
310 |
311 | -- Resizes all views after messing with ours
312 | tree_view:Tab():Resize()
313 | end
314 |
315 | -- Moves the cursor to the ".." in tree_view
316 | local function move_cursor_top()
317 | -- 2 is the position of the ".."
318 | tree_view.Cursor.Loc.Y = 2
319 |
320 | -- select the line after moving
321 | select_line()
322 | end
323 |
324 | local function refresh_and_select()
325 | -- Save the cursor position before messing with the view..
326 | -- because changing contents in the view causes the Y loc to move
327 | local last_y = tree_view.Cursor.Loc.Y
328 | -- Actually refresh
329 | refresh_view()
330 | -- Moves the cursor back to it's original position
331 | select_line(last_y)
332 | end
333 |
334 | -- Find everything nested under the target, and remove it from the scanlist
335 | local function compress_target(y, delete_y)
336 | -- Can't compress the top stuff, or if there's nothing there, so exit early
337 | if y == 0 or scanlist_is_empty() then
338 | return
339 | end
340 | -- Check if the target is a dir, since files don't have anything to compress
341 | -- Also make sure it's actually an uncompressed dir by checking the gutter message
342 | if scanlist[y].dirmsg == "-" then
343 | local target_index, delete_index
344 | -- Add the original target y to stuff to delete
345 | local delete_under = {[1] = y}
346 | local new_table = {}
347 | local del_count = 0
348 | -- Loop through the whole table, looking for nested content, or stuff with ownership == y...
349 | -- and delete matches. y+1 because we want to start under y, without actually touching y itself.
350 | for i = 1, #scanlist do
351 | delete_index = false
352 | -- Don't run on y, since we don't always delete y
353 | if i ~= y then
354 | -- On each loop, check if the ownership matches
355 | for x = 1, #delete_under do
356 | -- Check for something belonging to a thing to delete
357 | if scanlist[i].owner == delete_under[x] then
358 | -- Delete the target if it has an ownership to our delete target
359 | delete_index = true
360 | -- Keep count of total deleted (can't use #delete_under because it's for deleted dir count)
361 | del_count = del_count + 1
362 | -- Check if an uncompressed dir
363 | if scanlist[i].dirmsg == "-" then
364 | -- Add the index to stuff to delete, since it holds nested content
365 | delete_under[#delete_under + 1] = i
366 | end
367 | -- See if we're on the "deepest" nested content
368 | if scanlist[i].indent == highest_visible_indent and scanlist[i].indent > 0 then
369 | -- Save the lower indent, since we're minimizing/deleting nested dirs
370 | highest_visible_indent = highest_visible_indent - 1
371 | end
372 | -- Nothing else to do, so break this inner loop
373 | break
374 | end
375 | end
376 | end
377 | if not delete_index then
378 | -- Save the index in our new table
379 | new_table[#new_table + 1] = scanlist[i]
380 | end
381 | end
382 |
383 | scanlist = new_table
384 |
385 | if del_count > 0 then
386 | -- Ownership adjusting since we're deleting an index
387 | for i = y + 1, #scanlist do
388 | -- Don't touch root file/dirs
389 | if scanlist[i].owner > y then
390 | -- Minus ownership, on everything below i, the number deleted
391 | scanlist[i]:decrease_owner(del_count)
392 | end
393 | end
394 | end
395 |
396 | -- If not deleting, then update the gutter message to be + to signify compressed
397 | if not delete_y then
398 | -- Update the dir message
399 | scanlist[y].dirmsg = "+"
400 | end
401 | elseif config.GetGlobalOption("filemanager.compressparent") and not delete_y then
402 | goto_parent_dir()
403 | -- Prevent a pointless refresh of the view
404 | return
405 | end
406 |
407 | -- Put outside check above because we call this to delete targets as well
408 | if delete_y then
409 | local second_table = {}
410 | -- Quickly remove y
411 | for i = 1, #scanlist do
412 | if i == y then
413 | -- Reduce everything's ownership by 1 after y
414 | for x = i + 1, #scanlist do
415 | -- Don't touch root file/dirs
416 | if scanlist[x].owner > y then
417 | -- Minus 1 since we're just deleting y
418 | scanlist[x]:decrease_owner(1)
419 | end
420 | end
421 | else
422 | -- Put everything but y into the temporary table
423 | second_table[#second_table + 1] = scanlist[i]
424 | end
425 | end
426 | -- Put everything (but y) back into scanlist, with adjusted ownership values
427 | scanlist = second_table
428 | end
429 |
430 | if tree_view:GetView().Width > (30 + highest_visible_indent) then
431 | -- Shave off some width
432 | tree_view:ResizePane(30 + highest_visible_indent)
433 | end
434 |
435 | refresh_and_select()
436 | end
437 |
438 | -- Prompts the user for deletion of a file/dir when triggered
439 | -- Not local so Micro can access it
440 | function prompt_delete_at_cursor()
441 | local y = get_safe_y()
442 | -- Don't let them delete the top 3 index dir/separator/..
443 | if y == 0 or scanlist_is_empty() then
444 | micro.InfoBar():Error("You can't delete that")
445 | -- Exit early if there's nothing to delete
446 | return
447 | end
448 |
449 | micro.InfoBar():YNPrompt("Do you want to delete the " .. (scanlist[y].dirmsg ~= "" and "dir" or "file") .. ' "' .. scanlist[y].abspath .. '"? ', function(yes, canceled)
450 | if yes and not canceled then
451 | -- Use Go's os.Remove to delete the file
452 | local go_os = import("os")
453 | -- Delete the target (if its a dir then the children too)
454 | local remove_log = go_os.RemoveAll(scanlist[y].abspath)
455 | if remove_log == nil then
456 | micro.InfoBar():Message("Filemanager deleted: ", scanlist[y].abspath)
457 | -- Remove the target (and all nested) from scanlist[y + 1]
458 | -- true to delete y
459 | compress_target(get_safe_y(), true)
460 | else
461 | micro.InfoBar():Error("Failed deleting file/dir: ", remove_log)
462 | end
463 | else
464 | micro.InfoBar():Message("Nothing was deleted")
465 | end
466 | end)
467 | end
468 |
469 | -- Changes the current dir in the top of the tree..
470 | -- then scans that dir, and prints it to the view
471 | local function update_current_dir(path)
472 | -- Clear the highest since this is a full refresh
473 | highest_visible_indent = 0
474 | -- Set the width back to 30
475 | tree_view:ResizePane(30)
476 | -- Update the current dir to the new path
477 | current_dir = path
478 |
479 | -- Get the current working dir's files into our list of files
480 | -- 0 ownership because this is a scan of the base dir
481 | -- 0 indent because this is the base dir
482 | local scan_results = get_scanlist(path, 0, 0)
483 | -- Safety check with not-nil
484 | if scan_results ~= nil then
485 | -- Put in the new scan stuff
486 | scanlist = scan_results
487 | else
488 | -- If nil, just empty it
489 | scanlist = {}
490 | end
491 |
492 | refresh_view()
493 | -- Since we're going into a new dir, move cursor to the ".." by default
494 | move_cursor_top()
495 | end
496 |
497 | -- (Tries to) go back one "step" from the current directory
498 | local function go_back_dir()
499 | -- Use Micro's dirname to get everything but the current dir's path
500 | local one_back_dir = filepath.Dir(current_dir)
501 | -- Try opening, assuming they aren't at "root", by checking if it matches last dir
502 | if one_back_dir ~= current_dir then
503 | -- If filepath.Dir returns different, then they can move back..
504 | -- so we update the current dir and refresh
505 | update_current_dir(one_back_dir)
506 | end
507 | end
508 |
509 | -- Tries to open the current index
510 | -- If it's the top dir indicator, or separator, nothing happens
511 | -- If it's ".." then it tries to go back a dir
512 | -- If it's a dir then it moves into the dir and refreshes
513 | -- If it's actually a file, open it in a new vsplit
514 | -- THIS EXPECTS ZERO-BASED Y
515 | local function try_open_at_y(y)
516 | -- 2 is the zero-based index of ".."
517 | if y == 2 then
518 | go_back_dir()
519 | elseif y > 2 and not scanlist_is_empty() then
520 | -- -2 to conform to our scanlist "missing" first 3 indicies
521 | y = y - 2
522 | if scanlist[y].dirmsg ~= "" then
523 | -- if passed path is a directory, update the current dir to be one deeper..
524 | update_current_dir(scanlist[y].abspath)
525 | else
526 | -- If it's a file, then open it
527 | micro.InfoBar():Message("Filemanager opened ", scanlist[y].abspath)
528 | -- Opens the absolute path in new vertical view
529 | micro.CurPane():VSplitIndex(buffer.NewBufferFromFile(scanlist[y].abspath), true)
530 | -- Resizes all views after opening a file
531 | -- tabs[curTab + 1]:Resize()
532 | end
533 | else
534 | micro.InfoBar():Error("Can't open that")
535 | end
536 | end
537 |
538 | -- Opens the dir's contents nested under itself
539 | local function uncompress_target(y)
540 | -- Exit early if on the top 3 non-list items
541 | if y == 0 or scanlist_is_empty() then
542 | return
543 | end
544 | -- Only uncompress if it's a dir and it's not already uncompressed
545 | if scanlist[y].dirmsg == "+" then
546 | -- Get a new scanlist with results from the scan in the target dir
547 | local scan_results = get_scanlist(scanlist[y].abspath, y, scanlist[y].indent + 1)
548 | -- Don't run any of this if there's nothing in the dir we scanned, pointless
549 | if scan_results ~= nil then
550 | -- Will hold all the old values + new scan results
551 | local new_table = {}
552 | -- By not inserting in-place, some unexpected results can be avoided
553 | -- Also, table.insert actually moves values up (???) instead of down
554 | for i = 1, #scanlist do
555 | -- Put the current val into our new table
556 | new_table[#new_table + 1] = scanlist[i]
557 | if i == y then
558 | -- Fill in the scan results under y
559 | for x = 1, #scan_results do
560 | new_table[#new_table + 1] = scan_results[x]
561 | end
562 | -- Basically "moving down" everything below y, so ownership needs to increase on everything
563 | for inner_i = y + 1, #scanlist do
564 | -- When root not pushed by inserting, don't change its ownership
565 | -- This also has a dual-purpose to make it not effect root file/dirs
566 | -- since y is always >= 3
567 | if scanlist[inner_i].owner > y then
568 | -- Increase each indicies ownership by the number of scan results inserted
569 | scanlist[inner_i]:increase_owner(#scan_results)
570 | end
571 | end
572 | end
573 | end
574 |
575 | -- Update our scanlist with the new values
576 | scanlist = new_table
577 | end
578 |
579 | -- Change to minus to signify it's uncompressed
580 | scanlist[y].dirmsg = "-"
581 |
582 | -- Check if we actually need to resize, or if we're nesting at the same indent
583 | -- Also check if there's anything in the dir, as we don't need to expand on an empty dir
584 | if scan_results ~= nil then
585 | if scanlist[y].indent > highest_visible_indent and #scan_results >= 1 then
586 | -- Save the new highest indent
587 | highest_visible_indent = scanlist[y].indent
588 | -- Increase the width to fit the new nested content
589 | tree_view:ResizePane(tree_view:GetView().Width + scanlist[y].indent)
590 | end
591 | end
592 |
593 | refresh_and_select()
594 | end
595 | end
596 |
597 | -- Stat a path to check if it exists, returning true/false
598 | local function path_exists(path)
599 | local go_os = import("os")
600 | -- Stat the file/dir path we created
601 | -- file_stat should be non-nil, and stat_err should be nil on success
602 | local file_stat, stat_err = go_os.Stat(path)
603 | -- Check if what we tried to create exists
604 | if stat_err ~= nil then
605 | -- true/false if the file/dir exists
606 | return go_os.IsExist(stat_err)
607 | elseif file_stat ~= nil then
608 | -- Assume it exists if no errors
609 | return true
610 | end
611 | return false
612 | end
613 |
614 | -- Prompts for a new name, then renames the file/dir at the cursor's position
615 | -- Not local so Micro can use it
616 | function rename_at_cursor(bp, args)
617 |
618 | if micro.CurPane() ~= tree_view then
619 | micro.InfoBar():Message("Rename only works with the cursor in the tree!")
620 | return
621 | end
622 |
623 | -- Safety check they actually passed a name
624 | if #args < 1 then
625 | micro.InfoBar():Error('When using "rename" you need to input a new name')
626 | return
627 | end
628 |
629 | local new_name = args[1]
630 |
631 | -- +1 since Go uses zero-based indices
632 | local y = get_safe_y()
633 | -- Check if they're trying to rename the top stuff
634 | if y == 0 then
635 | -- Error since they tried to rename the top stuff
636 | micro.InfoBar():Message("You can't rename that!")
637 | return
638 | end
639 |
640 | -- The old file/dir's path
641 | local old_path = scanlist[y].abspath
642 | -- Join the path into their supplied rename, so that we have an absolute path
643 | local new_path = dirname_and_join(old_path, new_name)
644 | -- Use Go's os package for renaming the file/dir
645 | local golib_os = import("os")
646 | -- Actually rename the file
647 | local log_out = golib_os.Rename(old_path, new_path)
648 | -- Output the log, if any, of the rename
649 | if log_out ~= nil then
650 | micro.Log("Rename log: ", log_out)
651 | end
652 |
653 | -- Check if the rename worked
654 | if not path_exists(new_path) then
655 | micro.InfoBar():Error("Path doesn't exist after rename!")
656 | return
657 | end
658 |
659 | -- NOTE: doesn't alphabetically sort after refresh, but it probably should
660 | -- Replace the old path with the new path
661 | scanlist[y].abspath = new_path
662 | -- Refresh the tree with our new name
663 | refresh_and_select()
664 | end
665 |
666 | -- Prompts the user for the file/dir name, then creates the file/dir using Go's os package
667 | local function create_filedir(filedir_name, make_dir)
668 | if micro.CurPane() ~= tree_view then
669 | micro.InfoBar():Message("You can't create a file/dir if your cursor isn't in the tree!")
670 | return
671 | end
672 |
673 | -- Safety check they passed a name
674 | if filedir_name == nil then
675 | micro.InfoBar():Error('You need to input a name when using "touch" or "mkdir"!')
676 | return
677 | end
678 |
679 | -- The target they're trying to create on top of/in/at/whatever
680 | local y = get_safe_y()
681 | -- Holds the path passed to Go for the eventual new file/dir
682 | local filedir_path
683 | -- A true/false if scanlist is empty
684 | local scanlist_empty = scanlist_is_empty()
685 |
686 | -- Check there's actually anything in the list, and that they're not on the ".."
687 | if not scanlist_empty and y ~= 0 then
688 | -- If they're inserting on a folder, don't strip its path
689 | if scanlist[y].dirmsg ~= "" then
690 | -- Join our new file/dir onto the dir
691 | filedir_path = filepath.Join(scanlist[y].abspath, filedir_name)
692 | else
693 | -- The current index is a file, so strip its name and join ours onto it
694 | filedir_path = dirname_and_join(scanlist[y].abspath, filedir_name)
695 | end
696 | else
697 | -- if nothing in the list, or cursor is on top of "..", use the current dir
698 | filedir_path = filepath.Join(current_dir, filedir_name)
699 | end
700 |
701 | -- Check if the name is already taken by a file/dir
702 | if path_exists(filedir_path) then
703 | micro.InfoBar():Error("You can't create a file/dir with a pre-existing name")
704 | return
705 | end
706 |
707 | -- Use Go's os package for creating the files
708 | local golib_os = import("os")
709 | -- Create the dir or file
710 | if make_dir then
711 | -- Creates the dir
712 | golib_os.Mkdir(filedir_path, golib_os.ModePerm)
713 | micro.Log("Filemanager created directory: " .. filedir_path)
714 | else
715 | -- Creates the file
716 | golib_os.Create(filedir_path)
717 | micro.Log("Filemanager created file: " .. filedir_path)
718 | end
719 |
720 | -- If the file we tried to make doesn't exist, fail
721 | if not path_exists(filedir_path) then
722 | micro.InfoBar():Error("The file/dir creation failed")
723 |
724 | return
725 | end
726 |
727 | -- Creates a sort of default object, to be modified below
728 | -- If creating a dir, use a "+"
729 | local new_filedir = new_listobj(filedir_path, (make_dir and "+" or ""), 0, 0)
730 |
731 | -- Refresh with our new value(s)
732 | local last_y
733 |
734 | -- Only insert to scanlist if not created into a compressed dir, since it'd be hidden if it was
735 | -- Wrap the below checks so a y=0 doesn't break something
736 | if not scanlist_empty and y ~= 0 then
737 | -- +1 so it's highlighting the new file/dir
738 | last_y = tree_view.Cursor.Loc.Y + 1
739 |
740 | -- Only actually add the object to the list if it's not created on an uncompressed folder
741 | if scanlist[y].dirmsg == "+" then
742 | -- Exit early, since it was created into an uncompressed folder
743 |
744 | return
745 | elseif scanlist[y].dirmsg == "-" then
746 | -- Check if created on top of an uncompressed folder
747 | -- Change ownership to the folder it was created on top of..
748 | -- otherwise, the ownership would be incorrect
749 | new_filedir.owner = y
750 | -- We insert under the folder, so increment the indent
751 | new_filedir.indent = scanlist[y].indent + 1
752 | else
753 | -- This triggers if the cursor is on top of a file...
754 | -- so we copy the properties of it
755 | new_filedir.owner = scanlist[y].owner
756 | new_filedir.indent = scanlist[y].indent
757 | end
758 |
759 | -- A temporary table for adding our new object, and manipulation
760 | local new_table = {}
761 | -- Insert the new file/dir, and update ownership of everything below it
762 | for i = 1, #scanlist do
763 | -- Don't use i as index, as it will be off by one on the next pass after below "i == y"
764 | new_table[#new_table + 1] = scanlist[i]
765 | if i == y then
766 | -- Insert our new file/dir (below the last item)
767 | new_table[#new_table + 1] = new_filedir
768 | -- Increase ownership of everything below it, since we're inserting
769 | -- Basically "moving down" everything below y, so ownership needs to increase on everything
770 | for inner_i = y + 1, #scanlist do
771 | -- When root not pushed by inserting, don't change its ownership
772 | -- This also has a dual-purpose to make it not effect root file/dirs
773 | -- since y is always >= 3
774 | if scanlist[inner_i].owner > y then
775 | -- Increase each indicies ownership by 1 since we're only inserting 1 file/dir
776 | scanlist[inner_i]:increase_owner(1)
777 | end
778 | end
779 | end
780 | end
781 | -- Update the scanlist with the new object & updated ownerships
782 | scanlist = new_table
783 | else
784 | -- The scanlist is empty (or cursor is on ".."), so we add on our new file/dir at the bottom
785 | scanlist[#scanlist + 1] = new_filedir
786 | -- Add current position so it takes into account where we are
787 | last_y = #scanlist + tree_view.Cursor.Loc.Y
788 | end
789 |
790 | refresh_view()
791 | select_line(last_y)
792 | end
793 |
794 | -- Triggered with "touch filename"
795 | function new_file(bp, args)
796 |
797 | -- Safety check they actually passed a name
798 | if #args < 1 then
799 | micro.InfoBar():Error('When using "touch" you need to input a file name')
800 | return
801 | end
802 |
803 | local file_name = args[1]
804 |
805 | -- False because not a dir
806 | create_filedir(file_name, false)
807 | end
808 |
809 | -- Triggered with "mkdir dirname"
810 | function new_dir(bp, args)
811 |
812 | -- Safety check they actually passed a name
813 | if #args < 1 then
814 | micro.InfoBar():Error('When using "mkdir" you need to input a dir name')
815 | return
816 | end
817 |
818 | local dir_name = args[1]
819 |
820 | -- True because dir
821 | create_filedir(dir_name, true)
822 | end
823 |
824 | -- open_tree setup's the view
825 | local function open_tree()
826 | -- Open a new Vsplit (on the very left)
827 | micro.CurPane():VSplitIndex(buffer.NewBuffer("", "filemanager"), false)
828 | -- Save the new view so we can access it later
829 | tree_view = micro.CurPane()
830 |
831 | -- Set the width of tree_view to 30% & lock it
832 | tree_view:ResizePane(30)
833 | -- Set the type to unsavable
834 | -- tree_view.Buf.Type = buffer.BTLog
835 | tree_view.Buf.Type.Scratch = true
836 | tree_view.Buf.Type.Readonly = true
837 |
838 | -- Set the various display settings, but only on our view (by using SetLocalOption instead of SetOption)
839 | -- NOTE: Micro requires the true/false to be a string
840 | -- Softwrap long strings (the file/dir paths)
841 | tree_view.Buf:SetOptionNative("softwrap", true)
842 | -- No line numbering
843 | tree_view.Buf:SetOptionNative("ruler", false)
844 | -- Is this needed with new non-savable settings from being "vtLog"?
845 | tree_view.Buf:SetOptionNative("autosave", false)
846 | -- Don't show the statusline to differentiate the view from normal views
847 | tree_view.Buf:SetOptionNative("statusformatr", "")
848 | tree_view.Buf:SetOptionNative("statusformatl", "filemanager")
849 | tree_view.Buf:SetOptionNative("scrollbar", false)
850 |
851 | -- Fill the scanlist, and then print its contents to tree_view
852 | update_current_dir(os.Getwd())
853 | end
854 |
855 | -- close_tree will close the tree plugin view and release memory.
856 | local function close_tree()
857 | if tree_view ~= nil then
858 | tree_view:Quit()
859 | tree_view = nil
860 | clear_messenger()
861 | end
862 | end
863 |
864 | -- toggle_tree will toggle the tree view visible (create) and hide (delete).
865 | function toggle_tree()
866 | if tree_view == nil then
867 | open_tree()
868 | else
869 | close_tree()
870 | end
871 | end
872 |
873 | -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
874 | -- Functions exposed specifically for the user to bind
875 | -- Some are used in callbacks as well
876 | -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
877 |
878 | function uncompress_at_cursor()
879 | if micro.CurPane() == tree_view then
880 | uncompress_target(get_safe_y())
881 | end
882 | end
883 |
884 | function compress_at_cursor()
885 | if micro.CurPane() == tree_view then
886 | -- False to not delete y
887 | compress_target(get_safe_y(), false)
888 | end
889 | end
890 |
891 | -- Goes up 1 visible directory (if any)
892 | -- Not local so it can be bound
893 | function goto_prev_dir()
894 | if micro.CurPane() ~= tree_view or scanlist_is_empty() then
895 | return
896 | end
897 |
898 | local cur_y = get_safe_y()
899 | -- If they try to run it on the ".." do nothing
900 | if cur_y ~= 0 then
901 | local move_count = 0
902 | for i = cur_y - 1, 1, -1 do
903 | move_count = move_count + 1
904 | -- If a dir, stop counting
905 | if scanlist[i].dirmsg ~= "" then
906 | -- Jump to its parent (the ownership)
907 | tree_view.Cursor:UpN(move_count)
908 | select_line()
909 | break
910 | end
911 | end
912 | end
913 | end
914 |
915 | -- Goes down 1 visible directory (if any)
916 | -- Not local so it can be bound
917 | function goto_next_dir()
918 | if micro.CurPane() ~= tree_view or scanlist_is_empty() then
919 | return
920 | end
921 |
922 | local cur_y = get_safe_y()
923 | local move_count = 0
924 | -- If they try to goto_next on "..", pretends the cursor is valid
925 | if cur_y == 0 then
926 | cur_y = 1
927 | move_count = 1
928 | end
929 | -- Only do anything if it's even possible for there to be another dir
930 | if cur_y < #scanlist then
931 | for i = cur_y + 1, #scanlist do
932 | move_count = move_count + 1
933 | -- If a dir, stop counting
934 | if scanlist[i].dirmsg ~= "" then
935 | -- Jump to its parent (the ownership)
936 | tree_view.Cursor:DownN(move_count)
937 | select_line()
938 | break
939 | end
940 | end
941 | end
942 | end
943 |
944 | -- Goes to the parent directory (if any)
945 | -- Not local so it can be keybound
946 | function goto_parent_dir()
947 | if micro.CurPane() ~= tree_view or scanlist_is_empty() then
948 | return
949 | end
950 |
951 | local cur_y = get_safe_y()
952 | -- Check if the cursor is even in a valid location for jumping to the owner
953 | if cur_y > 0 then
954 | -- Jump to its parent (the ownership)
955 | tree_view.Cursor:UpN(cur_y - scanlist[cur_y].owner)
956 | select_line()
957 | end
958 | end
959 |
960 | function try_open_at_cursor()
961 | if micro.CurPane() ~= tree_view or scanlist_is_empty() then
962 | return
963 | end
964 |
965 | try_open_at_y(tree_view.Cursor.Loc.Y)
966 | end
967 |
968 | -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
969 | -- Shorthand functions for actions to reduce repeat code
970 | -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
971 |
972 | -- Used to fail certain actions that we shouldn't allow on the tree_view
973 | local function false_if_tree(view)
974 | if view == tree_view then
975 | return false
976 | end
977 | end
978 |
979 | -- Select the line at the cursor
980 | local function selectline_if_tree(view)
981 | if view == tree_view then
982 | select_line()
983 | end
984 | end
985 |
986 | -- Move the cursor to the top, but don't allow the action
987 | local function aftermove_if_tree(view)
988 | if view == tree_view then
989 | if tree_view.Cursor.Loc.Y < 2 then
990 | -- If it went past the "..", move back onto it
991 | tree_view.Cursor:DownN(2 - tree_view.Cursor.Loc.Y)
992 | end
993 | select_line()
994 | end
995 | end
996 |
997 | local function clearselection_if_tree(view)
998 | if view == tree_view then
999 | -- Clear the selection when doing a find, so it doesn't copy the current line
1000 | tree_view.Cursor:ResetSelection()
1001 | end
1002 | end
1003 |
1004 | -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1005 | -- All the events for certain Micro keys go below here
1006 | -- Other than things we flat-out fail
1007 | -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1008 |
1009 | -- Close current
1010 | function preQuit(view)
1011 | if view == tree_view then
1012 | -- A fake quit function
1013 | close_tree()
1014 | -- Don't actually "quit", otherwise it closes everything without saving for some reason
1015 | return false
1016 | end
1017 | end
1018 |
1019 | -- Close all
1020 | function preQuitAll(view)
1021 | close_tree()
1022 | end
1023 |
1024 | -- FIXME: Workaround for the weird 2-index movement on cursordown
1025 | function preCursorDown(view)
1026 | if view == tree_view then
1027 | tree_view.Cursor:Down()
1028 | select_line()
1029 | -- Don't actually go down, as it moves 2 indicies for some reason
1030 | return false
1031 | end
1032 | end
1033 |
1034 | -- Up
1035 | function onCursorUp(view)
1036 | selectline_if_tree(view)
1037 | end
1038 |
1039 | -- Alt-Shift-{
1040 | -- Go to target's parent directory (if exists)
1041 | function preParagraphPrevious(view)
1042 | if view == tree_view then
1043 | goto_prev_dir()
1044 | -- Don't actually do the action
1045 | return false
1046 | end
1047 | end
1048 |
1049 | -- Alt-Shift-}
1050 | -- Go to next dir (if exists)
1051 | function preParagraphNext(view)
1052 | if view == tree_view then
1053 | goto_next_dir()
1054 | -- Don't actually do the action
1055 | return false
1056 | end
1057 | end
1058 |
1059 | -- PageUp
1060 | function onCursorPageUp(view)
1061 | aftermove_if_tree(view)
1062 | end
1063 |
1064 | -- Ctrl-Up
1065 | function onCursorStart(view)
1066 | aftermove_if_tree(view)
1067 | end
1068 |
1069 | -- PageDown
1070 | function onCursorPageDown(view)
1071 | selectline_if_tree(view)
1072 | end
1073 |
1074 | -- Ctrl-Down
1075 | function onCursorEnd(view)
1076 | selectline_if_tree(view)
1077 | end
1078 |
1079 | function onNextSplit(view)
1080 | selectline_if_tree(view)
1081 | end
1082 |
1083 | function onPreviousSplit(view)
1084 | selectline_if_tree(view)
1085 | end
1086 |
1087 | -- On click, open at the click's y
1088 | function preMousePress(view, event)
1089 | if view == tree_view then
1090 | local x, y = event:Position()
1091 | -- Fixes the y because softwrap messes with it
1092 | local new_x, new_y = tree_view:GetMouseClickLocation(x, y)
1093 | -- Try to open whatever is at the click's y index
1094 | -- Will go into/back dirs based on what's clicked, nothing gets expanded
1095 | try_open_at_y(new_y)
1096 | -- Don't actually allow the mousepress to trigger, so we avoid highlighting stuff
1097 | return false
1098 | end
1099 | end
1100 |
1101 | -- Up
1102 | function preCursorUp(view)
1103 | if view == tree_view then
1104 | -- Disallow selecting past the ".." in the tree
1105 | if tree_view.Cursor.Loc.Y == 2 then
1106 | return false
1107 | end
1108 | end
1109 | end
1110 |
1111 | -- Left
1112 | function preCursorLeft(view)
1113 | if view == tree_view then
1114 | -- +1 because of Go's zero-based index
1115 | -- False to not delete y
1116 | compress_target(get_safe_y(), false)
1117 | -- Don't actually move the cursor, as it messes with selection
1118 | return false
1119 | end
1120 | end
1121 |
1122 | -- Right
1123 | function preCursorRight(view)
1124 | if view == tree_view then
1125 | -- +1 because of Go's zero-based index
1126 | uncompress_target(get_safe_y())
1127 | -- Don't actually move the cursor, as it messes with selection
1128 | return false
1129 | end
1130 | end
1131 |
1132 | -- Workaround for tab getting inserted into opened files
1133 | -- Ref https://github.com/zyedidia/micro/issues/992
1134 | local tab_pressed = false
1135 |
1136 | -- Tab
1137 | function preIndentSelection(view)
1138 | if view == tree_view then
1139 | tab_pressed = true
1140 | -- Open the file
1141 | -- Using tab instead of enter, since enter won't work with Readonly
1142 | try_open_at_y(tree_view.Cursor.Loc.Y)
1143 | -- Don't actually insert a tab
1144 | return false
1145 | end
1146 | end
1147 |
1148 | -- Workaround for tab getting inserted into opened files
1149 | -- Ref https://github.com/zyedidia/micro/issues/992
1150 | function preInsertTab(view)
1151 | if tab_pressed then
1152 | tab_pressed = false
1153 | return false
1154 | end
1155 | end
1156 | function preInsertNewline(view)
1157 | if view == tree_view then
1158 | return false
1159 | end
1160 | return true
1161 | end
1162 | -- CtrlL
1163 | function onJumpLine(view)
1164 | -- Highlight the line after jumping to it
1165 | -- Also moves you to index 3 (2 in zero-base) if you went to the first 2 lines
1166 | aftermove_if_tree(view)
1167 | end
1168 |
1169 | -- ShiftUp
1170 | function preSelectUp(view)
1171 | if view == tree_view then
1172 | -- Go to the file/dir's parent dir (if any)
1173 | goto_parent_dir()
1174 | -- Don't actually selectup
1175 | return false
1176 | end
1177 | end
1178 |
1179 | -- CtrlF
1180 | function preFind(view)
1181 | -- Since something is always selected, clear before a find
1182 | -- Prevents copying the selection into the find input
1183 | clearselection_if_tree(view)
1184 | end
1185 |
1186 | -- FIXME: doesn't work for whatever reason
1187 | function onFind(view)
1188 | -- Select the whole line after a find, instead of just the input txt
1189 | selectline_if_tree(view)
1190 | end
1191 |
1192 | -- CtrlN after CtrlF
1193 | function onFindNext(view)
1194 | selectline_if_tree(view)
1195 | end
1196 |
1197 | -- CtrlP after CtrlF
1198 | function onFindPrevious(view)
1199 | selectline_if_tree(view)
1200 | end
1201 |
1202 | -- NOTE: This is a workaround for "cd" not having its own callback
1203 | local precmd_dir
1204 |
1205 | function preCommandMode(view)
1206 | precmd_dir = os.Getwd()
1207 | end
1208 |
1209 | -- Update the current dir when using "cd"
1210 | function onCommandMode(view)
1211 | local new_dir = os.Getwd()
1212 | -- Only do anything if the tree is open, and they didn't cd to nothing
1213 | if tree_view ~= nil and new_dir ~= precmd_dir and new_dir ~= current_dir then
1214 | update_current_dir(new_dir)
1215 | end
1216 | end
1217 |
1218 | ------------------------------------------------------------------
1219 | -- Fail a bunch of useless actions
1220 | -- Some of these need to be removed (read-only makes some useless)
1221 | ------------------------------------------------------------------
1222 |
1223 | function preStartOfLine(view)
1224 | return false_if_tree(view)
1225 | end
1226 |
1227 | function preStartOfText(view)
1228 | return false_if_tree(view)
1229 | end
1230 |
1231 | function preEndOfLine(view)
1232 | return false_if_tree(view)
1233 | end
1234 |
1235 | function preMoveLinesDown(view)
1236 | return false_if_tree(view)
1237 | end
1238 |
1239 | function preMoveLinesUp(view)
1240 | return false_if_tree(view)
1241 | end
1242 |
1243 | function preWordRight(view)
1244 | return false_if_tree(view)
1245 | end
1246 |
1247 | function preWordLeft(view)
1248 | return false_if_tree(view)
1249 | end
1250 |
1251 | function preSelectDown(view)
1252 | return false_if_tree(view)
1253 | end
1254 |
1255 | function preSelectLeft(view)
1256 | return false_if_tree(view)
1257 | end
1258 |
1259 | function preSelectRight(view)
1260 | return false_if_tree(view)
1261 | end
1262 |
1263 | function preSelectWordRight(view)
1264 | return false_if_tree(view)
1265 | end
1266 |
1267 | function preSelectWordLeft(view)
1268 | return false_if_tree(view)
1269 | end
1270 |
1271 | function preSelectToStartOfLine(view)
1272 | return false_if_tree(view)
1273 | end
1274 |
1275 | function preSelectToStartOfText(view)
1276 | return false_if_tree(view)
1277 | end
1278 |
1279 | function preSelectToEndOfLine(view)
1280 | return false_if_tree(view)
1281 | end
1282 |
1283 | function preSelectToStart(view)
1284 | return false_if_tree(view)
1285 | end
1286 |
1287 | function preSelectToEnd(view)
1288 | return false_if_tree(view)
1289 | end
1290 |
1291 | function preDeleteWordLeft(view)
1292 | return false_if_tree(view)
1293 | end
1294 |
1295 | function preDeleteWordRight(view)
1296 | return false_if_tree(view)
1297 | end
1298 |
1299 | function preOutdentSelection(view)
1300 | return false_if_tree(view)
1301 | end
1302 |
1303 | function preOutdentLine(view)
1304 | return false_if_tree(view)
1305 | end
1306 |
1307 | function preSave(view)
1308 | return false_if_tree(view)
1309 | end
1310 |
1311 | function preCut(view)
1312 | return false_if_tree(view)
1313 | end
1314 |
1315 | function preCutLine(view)
1316 | return false_if_tree(view)
1317 | end
1318 |
1319 | function preDuplicateLine(view)
1320 | return false_if_tree(view)
1321 | end
1322 |
1323 | function prePaste(view)
1324 | return false_if_tree(view)
1325 | end
1326 |
1327 | function prePastePrimary(view)
1328 | return false_if_tree(view)
1329 | end
1330 |
1331 | function preMouseMultiCursor(view)
1332 | return false_if_tree(view)
1333 | end
1334 |
1335 | function preSpawnMultiCursor(view)
1336 | return false_if_tree(view)
1337 | end
1338 |
1339 | function preSelectAll(view)
1340 | return false_if_tree(view)
1341 | end
1342 |
1343 | function init()
1344 | -- Let the user disable showing of dotfiles like ".editorconfig" or ".DS_STORE"
1345 | config.RegisterCommonOption("filemanager", "showdotfiles", true)
1346 | -- Let the user disable showing files ignored by the VCS (i.e. gitignored)
1347 | config.RegisterCommonOption("filemanager", "showignored", true)
1348 | -- Let the user disable going to parent directory via left arrow key when file selected (not directory)
1349 | config.RegisterCommonOption("filemanager", "compressparent", true)
1350 | -- Let the user choose to list sub-folders first when listing the contents of a folder
1351 | config.RegisterCommonOption("filemanager", "foldersfirst", true)
1352 | -- Lets the user have the filetree auto-open any time Micro is opened
1353 | -- false by default, as it's a rather noticable user-facing change
1354 | config.RegisterCommonOption("filemanager", "openonstart", false)
1355 |
1356 | -- Open/close the tree view
1357 | config.MakeCommand("tree", toggle_tree, config.NoComplete)
1358 | -- Rename the file/dir under the cursor
1359 | config.MakeCommand("rename", rename_at_cursor, config.NoComplete)
1360 | -- Create a new file
1361 | config.MakeCommand("touch", new_file, config.NoComplete)
1362 | -- Create a new dir
1363 | config.MakeCommand("mkdir", new_dir, config.NoComplete)
1364 | -- Delete a file/dir, and anything contained in it if it's a dir
1365 | config.MakeCommand("rm", prompt_delete_at_cursor, config.NoComplete)
1366 | -- Adds colors to the ".." and any dir's in the tree view via syntax highlighting
1367 | -- TODO: Change it to work with git, based on untracked/changed/added/whatever
1368 | config.AddRuntimeFile("filemanager", config.RTSyntax, "syntax.yaml")
1369 |
1370 | -- NOTE: This must be below the syntax load command or coloring won't work
1371 | -- Just auto-open if the option is enabled
1372 | -- This will run when the plugin first loads
1373 | if config.GetGlobalOption("filemanager.openonstart") then
1374 | -- Check for safety on the off-chance someone's init.lua breaks this
1375 | if tree_view == nil then
1376 | open_tree()
1377 | -- Puts the cursor back in the empty view that initially spawns
1378 | -- This is so the cursor isn't sitting in the tree view at startup
1379 | micro.CurPane():NextSplit()
1380 | else
1381 | -- Log error so they can fix it
1382 | micro.Log(
1383 | "Warning: filemanager.openonstart was enabled, but somehow the tree was already open so the option was ignored."
1384 | )
1385 | end
1386 | end
1387 | end
1388 |
--------------------------------------------------------------------------------
/.config/micro/plug/filemanager/repo.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "Name": "filemanager",
4 | "Description": "File manager for Micro",
5 | "Tags": ["filetree", "filemanager", "file", "manager"],
6 | "Website": "",
7 | "Versions": [
8 | {
9 | "Version": "2.1.1",
10 | "Url": "https://github.com/NicolaiSoeborg/filemanager-plugin/archive/v2.1.1.zip",
11 | "Require": {
12 | "micro": ">=1.3.2"
13 | }
14 | },
15 | {
16 | "Version": "3.1.0",
17 | "Url": "https://github.com/NicolaiSoeborg/filemanager-plugin/archive/v3.1.0.zip",
18 | "Require": {
19 | "micro": ">=1.3.5"
20 | }
21 | },
22 | {
23 | "Version": "3.4.0",
24 | "Url": "https://github.com/NicolaiSoeborg/filemanager-plugin/archive/v3.4.0.zip",
25 | "Require": {
26 | "micro": ">=1.4.1"
27 | }
28 | },
29 | {
30 | "Version": "3.5.0",
31 | "Url": "https://github.com/micro-editor/updated-plugins/releases/download/v1.0.0/filemanager-3.5.0.zip",
32 | "Require": {
33 | "micro": ">=2.0.0-1"
34 | }
35 | },
36 | {
37 | "Version": "3.5.1",
38 | "Url": "https://github.com/micro-editor/updated-plugins/releases/download/v1.0.0/filemanager-3.5.1.zip",
39 | "Require": {
40 | "micro": ">=2.0.0-1"
41 | }
42 | }
43 | ]
44 | }
45 | ]
46 |
--------------------------------------------------------------------------------
/.config/micro/plug/filemanager/syntax.yaml:
--------------------------------------------------------------------------------
1 | filetype: filemanager
2 |
3 | detect:
4 | filename: "^filemanager$"
5 |
6 | rules:
7 | # The "..", the separator line thing, and directories
8 | # Optionally, add this below to highlight the ascii line: "^─*$"
9 | - special: "^\\.\\.$|\\-\\s.*|\\+\\s.*"
10 |
--------------------------------------------------------------------------------
/.config/micro/plug/manipulator/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled Lua sources
2 | luac.out
3 |
4 | # luarocks build files
5 | *.src.rock
6 | *.zip
7 | *.tar.gz
8 |
9 | # Object files
10 | *.o
11 | *.os
12 | *.ko
13 | *.obj
14 | *.elf
15 |
16 | # Precompiled Headers
17 | *.gch
18 | *.pch
19 |
20 | # Libraries
21 | *.lib
22 | *.a
23 | *.la
24 | *.lo
25 | *.def
26 | *.exp
27 |
28 | # Shared objects (inc. Windows DLLs)
29 | *.dll
30 | *.so
31 | *.so.*
32 | *.dylib
33 |
34 | # Executables
35 | *.exe
36 | *.out
37 | *.app
38 | *.i*86
39 | *.x86_64
40 | *.hex
41 |
42 |
--------------------------------------------------------------------------------
/.config/micro/plug/manipulator/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Nicolai Søborg
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/.config/micro/plug/manipulator/README.md:
--------------------------------------------------------------------------------
1 | # Manipulator Plugin
2 |
3 | This is an simple plugin to extend text manipulation in Micro.
4 |
5 | ## Keybindings
6 | By default no keybindings exist, but you can easily modify that
7 | in your `bindings.json` file:
8 |
9 | ```json
10 | {
11 | "Ctrl-L": "manipulator.lower"
12 | }
13 | ```
14 |
15 | You can also execute a command which will do the same thing as
16 | the binding:
17 |
18 | ```
19 | > lower
20 | ```
21 |
22 | If you have a selection, the plugin will change all of the
23 | selection.
24 |
25 | The following commands currently exists:
26 | * `upper`: UPPERCASE
27 | * `lower`: lowercase
28 | * `reverse`: Reverses
29 | * `base64enc`: Base64 encodes
30 | * `base64dec`: Base64 decodes
31 | * `rot13`: ROT-13
32 | * `incNum`: Increase number by one
33 | * `decNum`: Decrease number by one
34 | * `capital`: Capitalize First Letter
35 | * `brace`: Adds brackets around selection
36 | * `curly`: Curly brackets (`{ }`)
37 | * `square`: Square brackets (`[ ]`)
38 | * `angle`: Angle brackets (`< >`)
39 | * `dquote`: Double quotes (`" "`)
40 | * `squote`: Single quotes (`' '`)
41 |
42 | ## Issues
43 |
44 | Please use the issue tracker if you have any issues or
45 | feature requests!
46 |
47 |
48 | ## Demo
49 |
50 | 
51 |
--------------------------------------------------------------------------------
/.config/micro/plug/manipulator/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aureliojargas/dotfiles/f35732301f6592415f45038c00207c7e463b685e/.config/micro/plug/manipulator/demo.gif
--------------------------------------------------------------------------------
/.config/micro/plug/manipulator/demo.txt:
--------------------------------------------------------------------------------
1 | THIS TEXT SHOULDN'T BE ALL UPPERCASE
2 | Write const all uppercase, e.g. max_connections
3 | This should be two: 1 <== you can also decrease!
4 |
5 | base64(rot13(txt)):
6 | ZmhjcmUgZnJwZXJn
7 |
--------------------------------------------------------------------------------
/.config/micro/plug/manipulator/help/manipulator.md:
--------------------------------------------------------------------------------
1 | # Manipulator Plugin
2 |
3 | This is an simple plugin to extend text manipulation in Micro.
4 |
5 | ## Keybindings
6 | By default no keybindings exist, but you can easily modify that
7 | in your `bindings.json` file:
8 |
9 | ```json
10 | {
11 | "Ctrl-L": "manipulator.lower"
12 | }
13 | ```
14 |
15 | You can also execute a command which will do the same thing as
16 | the binding:
17 |
18 | ```
19 | > lower
20 | ```
21 |
22 | If you have a selection, the plugin will change all of the
23 | selection.
24 |
25 | The following commands currently exists:
26 | * `upper`: UPPERCASE
27 | * `lower`: lowercase
28 | * `reverse`: Reverses
29 | * `base64enc`: Base64 encodes
30 | * `base64dec`: Base64 decodes
31 | * `rot13`: ROT-13
32 | * `incNum`: Increase number by one
33 | * `decNum`: Decrease number by one
34 | * `capital`: Capitalize First Letter
35 | * `brace`: Adds brackets around selection
36 | * `curly`: Curly brackets (`{ }`)
37 | * `square`: Square brackets (`[ ]`)
38 | * `angle`: Angle brackets (`< >`)
39 | * `dquote`: Double quotes (`" "`)
40 | * `squote`: Single quotes (`' '`)
41 |
42 | ## Issues
43 |
44 | Please use the issue tracker if you have any issues or
45 | feature requests!
46 |
47 |
48 | ## Demo
49 |
50 | 
51 |
--------------------------------------------------------------------------------
/.config/micro/plug/manipulator/manipulator.lua:
--------------------------------------------------------------------------------
1 | VERSION = "1.4.0"
2 |
3 | local micro = import("micro")
4 | local config = import("micro/config")
5 | local buffer = import("micro/buffer")
6 |
7 | -- Returns Loc-tuple w/ current marked text or whole line (begin, end)
8 | function getTextLoc()
9 | local v = micro.CurPane()
10 | local a, b, c = nil, nil, v.Cursor
11 | if c:HasSelection() then
12 | if c.CurSelection[1]:GreaterThan(-c.CurSelection[2]) then
13 | a, b = c.CurSelection[2], c.CurSelection[1]
14 | else
15 | a, b = c.CurSelection[1], c.CurSelection[2]
16 | end
17 | else
18 | local eol = string.len(v.Buf:Line(c.Loc.Y))
19 | a, b = c.Loc, buffer.Loc(eol, c.Y)
20 | end
21 | return buffer.Loc(a.X, a.Y), buffer.Loc(b.X, b.Y)
22 | end
23 |
24 | -- Returns the current marked text or whole line
25 | function getText(a, b)
26 | local txt, buf = {}, micro.CurPane().Buf
27 |
28 | -- Editing a single line?
29 | if a.Y == b.Y then
30 | return buf:Line(a.Y):sub(a.X+1, b.X)
31 | end
32 |
33 | -- Add first part of text selection (a.X+1 as Lua is 1-indexed)
34 | table.insert(txt, buf:Line(a.Y):sub(a.X+1))
35 |
36 | -- Stuff in the middle
37 | for lineNo = a.Y+1, b.Y-1 do
38 | table.insert(txt, buf:Line(lineNo))
39 | end
40 |
41 | -- Insert last part of selection
42 | table.insert(txt, buf:Line(b.Y):sub(1, b.X))
43 |
44 | return table.concat(txt, "\n")
45 | end
46 |
47 | -- Calls 'manipulator'-function on text matching 'regex'
48 | function manipulate(regex, manipulator, num)
49 | local num = math.inf or num
50 | local v = micro.CurPane()
51 | local a, b = getTextLoc()
52 |
53 | local oldTxt = getText(a,b)
54 |
55 | local newTxt = string.gsub(oldTxt, regex, manipulator, num)
56 | v.Buf:Replace(a, b, newTxt)
57 |
58 | -- Fix selection, if transformation changes text length (e.g. base64)
59 | local d = string.len(newTxt) - string.len(oldTxt)
60 | if d ~= 0 then
61 | local c = v.Cursor
62 | if c:HasSelection() then
63 | if c.CurSelection[1]:GreaterThan(-c.CurSelection[2]) then
64 | c.CurSelection[1].X = c.CurSelection[1].X - d
65 | else
66 | c.CurSelection[2].X = c.CurSelection[2].X + d
67 | end
68 | end
69 | end
70 |
71 | --v.Cursor:Relocate()
72 | --v.Cursor.LastVisualX = v.Cursor:GetVisualX()
73 | end
74 |
75 |
76 | --[[ DEFINE FUNCTIONS ]]--
77 |
78 | function rot13() manipulate("[%a]",
79 | function (txt)
80 | local result, lower, upper = {}, string.byte('a'), string.byte('A')
81 | for c in txt:gmatch(".") do
82 | local offset = string.lower(c) == c and lower or upper
83 | local p = ((c:byte() - offset + 13) % 26) + offset
84 | table.insert(result, string.char(p))
85 | end
86 | return table.concat(result, "")
87 | end
88 | ) end
89 |
90 | function incNum() manipulate("[%d]",
91 | function (txt) return tonumber(txt)+1 end
92 | ) end
93 |
94 | function decNum() manipulate("[%d]",
95 | function (txt) return tonumber(txt)-1 end
96 | ) end
97 |
98 | -- Credit: http://lua-users.org/wiki/BaseSixtyFour
99 | function base64enc() manipulate(".*",
100 | function (data)
101 | local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
102 | return ((data:gsub('.', function(x)
103 | local r,b='',x:byte()
104 | for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end
105 | return r;
106 | end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)
107 | if (#x < 6) then return '' end
108 | local c=0
109 | for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end
110 | return b:sub(c+1,c+1)
111 | end)..({ '', '==', '=' })[#data%3+1])
112 | end
113 | ) end
114 |
115 | function base64dec() manipulate(".*",
116 | function (data)
117 | local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
118 | data = string.gsub(data, '[^'..b..'=]', '')
119 | return (data:gsub('.', function(x)
120 | if (x == '=') then return '' end
121 | local r,f='',(b:find(x)-1)
122 | for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end
123 | return r;
124 | end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
125 | if (#x ~= 8) then return '' end
126 | local c=0
127 | for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(8-i) or 0) end
128 | return string.char(c)
129 | end))
130 | end
131 | ) end
132 |
133 | -- Credit: http://lua-users.org/wiki/StringRecipes
134 | function capital() manipulate("(%a)([%w_']*)",
135 | function (first, rest)
136 | return first:upper() .. rest:lower()
137 | end
138 | ) end
139 |
140 | function init()
141 | config.MakeCommand("capital", capital, config.NoComplete)
142 | -- Thanks marinopposite
143 | config.MakeCommand("brace", function() manipulate(".*", "(%1)", 1) end, config.NoComplete)
144 | config.MakeCommand("curly", function() manipulate(".*", "{%1}", 1) end, config.NoComplete)
145 | config.MakeCommand("square", function() manipulate(".*", "[%1]", 1) end, config.NoComplete)
146 | config.MakeCommand("dquote", function() manipulate(".*", '"%1"', 1) end, config.NoComplete)
147 | config.MakeCommand("squote", function() manipulate(".*", "'%1'", 1) end, config.NoComplete)
148 | config.MakeCommand("angle", function() manipulate(".*", "<%1>", 1) end, config.NoComplete)
149 | config.MakeCommand("base64dec", base64dec, config.NoComplete)
150 | config.MakeCommand("base64enc", base64enc, config.NoComplete)
151 | config.MakeCommand("decNum", decNum, config.NoComplete)
152 | config.MakeCommand("incNum", incNum, config.NoComplete)
153 | config.MakeCommand("rot13", rot13, config.NoComplete)
154 | config.MakeCommand("upper", function() manipulate("[%a]", string.upper) end, config.NoComplete)
155 | config.MakeCommand("lower", function() manipulate("[%a]", string.lower) end, config.NoComplete)
156 | config.MakeCommand("reverse", function() manipulate(".*", string.reverse) end, config.NoComplete)
157 |
158 | config.AddRuntimeFile("manipulator", config.RTHelp, "help/manipulator.md")
159 | end
160 |
--------------------------------------------------------------------------------
/.config/micro/plug/manipulator/repo.json:
--------------------------------------------------------------------------------
1 | [{
2 | "Name": "manipulator",
3 | "Description": "Plugin to do various kind of modifications to text in Micro",
4 | "Website": "https://github.com/NicolaiSoeborg/manipulator-plugin",
5 | "Tags": ["text", "manipulation", "base64", "rot13", "bracket", "Capital", "uppercase", "lowercase"],
6 | "Versions": [
7 | {
8 | "Version": "1.4.0",
9 | "Url": "https://github.com/NicolaiSoeborg/manipulator-plugin/archive/v1.4.0.zip",
10 | "Require": {
11 | "micro": ">=2.0.0-1"
12 | }
13 | },
14 | {
15 | "Version": "1.3.2",
16 | "Url": "https://github.com/NicolaiSoeborg/manipulator-plugin/archive/v1.3.2.zip",
17 | "Require": {
18 | "micro": ">=1.0.0"
19 | }
20 | }
21 | ]
22 | }]
23 |
--------------------------------------------------------------------------------
/.config/micro/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "autosave": 5,
3 | "backup": false,
4 | "colorcolumn": 80,
5 | "colorscheme": "bubblegum",
6 | "diffgutter": true,
7 | "fastdirty": true,
8 | "ft:git-commit": {
9 | "colorcolumn": 72,
10 | "filetype": "markdown"
11 | },
12 | "hlsearch": true,
13 | "parsecursor": true,
14 | "rmtrailingws": true,
15 | "savecursor": true,
16 | "softwrap": true,
17 | "tabsize": 8
18 | }
19 |
--------------------------------------------------------------------------------
/.gemrc:
--------------------------------------------------------------------------------
1 | gem: --no-document --user-install
2 |
--------------------------------------------------------------------------------
/.gitconfig:
--------------------------------------------------------------------------------
1 | # https://git-scm.com/docs/git-config#_configuration_file
2 |
3 | [alias] # https://git.wiki.kernel.org/index.php/Aliases
4 |
5 | # Using $GIT_PREFIX to workaround aliases running from the repository root
6 | # https://til.codeinthehole.com/posts/that-git-aliases-that-run-an-external-command-are-run-from-the-repository-root/
7 |
8 | co = checkout
9 |
10 | commit-message = !sh -c 'git-pre-commit && git commit -m \"$*\"' -
11 |
12 | cp = cherry-pick
13 |
14 | diffw = diff -w --ignore-blank-lines
15 |
16 | g = grep --extended-regexp --break --heading --line-number --ignore-case --show-function
17 |
18 | grep-filename = !sh -c 'git ls-files | grep --color=always $@' -
19 |
20 | # hash, date, email, summary
21 | log4 = log -n 7 --oneline --reverse --date=short \
22 | --pretty=format:'%Cblue%h%Creset %C(yellow)%ad%Creset %Cred%<(22,trunc)%ae%Creset %s'
23 |
24 | # hash, date, summary
25 | log3 = !sh -c 'git log4 --color=always $@ | tr -s \" \" | cut -d \" \" -f 1,2,4-' -
26 |
27 | # hash, summary
28 | log2 = !sh -c 'git log3 $@ | cut -d \" \" -f 1,4-' -
29 |
30 | log-graph = log --oneline --decorate --all --graph
31 |
32 | new = !sh -c 'git log4 -n 999 @{1}..@{0}' # What's new since the last git pull?
33 |
34 | # git sed 's/foo/bar/g'
35 | sed = "!f() { cd ${GIT_PREFIX:-./}; git grep -z -l . | xargs -0 gsed -E -i \"$1\"; }; f"
36 |
37 | # git sed2 's/foo/bar/g' '*.py'
38 | sed2 = "!f() { cd ${GIT_PREFIX:-./}; git ls-files -z -- \"${2:-*}\" | xargs -0 -n 1 gsed -E -i \"$1\"; }; f"
39 |
40 | uncommit = reset --soft HEAD^
41 |
42 | unstage = reset HEAD
43 |
44 | worddiff = diff --color-words
45 |
46 | [commit]
47 | status = true # show `git status` output in commit template
48 |
49 | [core]
50 | # editor = ed --loose-exit-status # ed challenge :)
51 | editor = micro
52 | # pager = diff-so-fancy | less --tabs=4 -RFX # word-highlight diffs
53 | pager = delta
54 | excludesfile = ~/.gitignore_global
55 |
56 | [blame]
57 | coloring = highlightRecent # --color-by-age
58 |
59 | [branch]
60 | sort = committerdate # show recently changed branches at bottom
61 |
62 | [color]
63 | ui = auto
64 |
65 | [diff]
66 | # Color moved lines differently
67 | # https://dev.to/cloudx/how-to-color-the-moved-code-in-git-10ei
68 | colormoved = default
69 | colormovedws = allow-indentation-change
70 |
71 | [delta] # https://github.com/dandavison/delta
72 | light = false
73 | syntax-theme = GitHub # GitHub, Monokai Extended, ansi, zenburn
74 | line-numbers = false
75 | side-by-side = false
76 | whitespace-error-style = red reverse # trailing spaces are red
77 | navigate = true # n and N move between diff sections
78 |
79 | [interactive]
80 | diffFilter = delta --color-only
81 |
82 | [help]
83 | autocorrect = 1 # fix typos
84 |
85 | # Customize the git info in the shell prompt (bash, fish)
86 | # https://fishshell.com/docs/current/cmds/fish_git_prompt.html
87 | [bash]
88 | showUntrackedFiles = false # …↑1
89 | showDirtyState = false # ●1✚1
90 |
91 | [pull]
92 | ff = only
93 |
94 | [push]
95 | # https://git-scm.com/docs/git-config.html#git-config-pushdefault
96 | default = current # default in Git 1.x
97 |
98 | [remote "origin"]
99 | prune = true # Autoremove already deleted remote branches
100 |
101 | [remote "upstream"]
102 | prune = true # Autoremove already deleted remote branches
103 |
104 | # Use separate files for username, credentials, etc
105 | # https://git-scm.com/docs/git-config#_includes
106 | [includeIf "gitdir:~/k/a/"]
107 | path = ~/.gitconfig.personal
108 | [includeIf "gitdir:~/k/w/"]
109 | path = ~/.gitconfig.work
110 |
111 | # Some useful Git commands that I always have to search the web
112 | #
113 | # All commits whose tree has this pattern
114 | # git rev-list --all | xargs git grep PATTERN
115 | #
116 | # All commits that added/removed/edited lines with this pattern
117 | # git log --pickaxe-regex -S PATTERN # added/removed
118 | # git log -G PATTERN # added/removed/edited
119 | #
120 | # Log for a removed file
121 | # git log --full-history -- REMOVED-FILE-PATH
122 | #
123 | # Change commit date (both)
124 | # git rebase -i, edit the desired commit
125 | # d="2017-10-08T09:51:07" # gl --date=iso-strict to get it from a commit
126 | # GIT_COMMITTER_DATE="$d" git commit --amend --no-edit --date="$d"
127 | # git rebase --continue
128 | # Note that any `git rebase` on those commits will have CommitterDate set
129 | # to the current date. To avoid that, use --committer-date-is-author-date
130 | #
131 | # Rename a file in history
132 | # git rebase -i, edit the commit that added the file, rename it, amend.
133 |
--------------------------------------------------------------------------------
/.gitconfig.local.template:
--------------------------------------------------------------------------------
1 | [user]
2 | name = John Doe
3 | email = john@example.com
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # auto generated files inside symlinked folders
2 | /.termux/shell
3 | /.vim/.netrwhist
4 | /.vim/pack/
5 |
6 | # other local-only files
7 | /.vscode
8 | /.a.md
9 |
--------------------------------------------------------------------------------
/.inputrc:
--------------------------------------------------------------------------------
1 | # TAB completion
2 | set completion-ignore-case on
3 |
4 | # No beeps
5 | set bell-style visible
6 |
--------------------------------------------------------------------------------
/.screenrc:
--------------------------------------------------------------------------------
1 | # Use fish as the default shell
2 | shell /usr/bin/fish
3 |
4 | # Autodetach session on hangup instead of terminating screen completely
5 | autodetach on
6 |
7 | # Define a bigger scrollback, default is 100 lines
8 | defscrollback 9999
9 |
10 | # Enable mouse scrolling and scroll bar history scrolling
11 | # https://unix.stackexchange.com/a/40246/288927
12 | termcapinfo xterm* ti@:te@
13 |
14 | # Turn off annoyances
15 | startup_message off
16 | vbell off
17 | bell_msg ""
18 |
19 | # Split related
20 | caption splitonly "%2n %t"
21 | bind 'R' wrap
22 | bind 'r' resize
23 | bind '+' resize +5
24 | bind '-' resize -5
25 |
26 | # Blue status bar, white text
27 | hardstatus alwayslastline "%w"
28 | sorendition 00 47
29 | #
30 | # sorendition
31 | # attributes:
32 | # 0 normal, 1 invert, 2 bright bg, 3 invert+bright bg (1+2), 5 bright fg
33 | # colors:
34 | # 0 black, 1 red, 2 green, 3 brown, 4 blue, 5 magenta, 6 cyan, 7 silver
35 | # examples:
36 | # 00 02 - black bg (0), green fg (2), no attributes (00)
37 | # 20 30 - brown bg (3), black fg (0), bright bg (2) -> turns yellow
38 | # 35 74 - silver bg (7), blue fg (4), invert+bright bg (3), bright fg (5)
39 |
--------------------------------------------------------------------------------
/.termux/termux.properties:
--------------------------------------------------------------------------------
1 | # https://wiki.termux.com/wiki/Touch_Keyboard
2 | # https://wiki.termux.com/wiki/Terminal_Settings
3 | # https://github.com/termux/termux-tools/blob/master/termux.properties
4 | # After editing run: termux-reload-settings
5 |
6 | # Two extra top rows for the on-screen keyboard
7 | # Also available: PGUP PGDN
8 | extra-keys = [['ESC','/','~','HOME','UP','END'],['TAB','CTRL','ALT','LEFT','DOWN','RIGHT']]
9 |
10 | # When using an external keyboard, the top rows are not necessary
11 | #extra-keys = []
12 |
--------------------------------------------------------------------------------
/.tilix.dconf:
--------------------------------------------------------------------------------
1 | [/]
2 | accelerators-enabled=true
3 | copy-on-select=true
4 | enable-wide-handle=false
5 | paste-advanced-default=false
6 | sidebar-on-right=false
7 | terminal-title-show-when-single=false
8 | terminal-title-style='small'
9 | theme-variant='dark'
10 | use-tabs=false
11 | window-save-state=true
12 | window-state=132
13 | window-style='disable-csd'
14 |
15 | [keybindings]
16 | app-new-session='disabled'
17 | app-new-window='disabled'
18 | nautilus-open='disabled'
19 | session-add-auto='disabled'
20 | session-add-down='disabled'
21 | session-add-right='disabled'
22 | session-close='disabled'
23 | session-open='disabled'
24 | session-resize-terminal-down='disabled'
25 | session-resize-terminal-left='disabled'
26 | session-resize-terminal-right='disabled'
27 | session-resize-terminal-up='disabled'
28 | session-save='disabled'
29 | session-switch-to-terminal-0='disabled'
30 | session-switch-to-terminal-1='disabled'
31 | session-switch-to-terminal-2='disabled'
32 | session-switch-to-terminal-3='disabled'
33 | session-switch-to-terminal-4='disabled'
34 | session-switch-to-terminal-5='disabled'
35 | session-switch-to-terminal-6='disabled'
36 | session-switch-to-terminal-7='disabled'
37 | session-switch-to-terminal-8='disabled'
38 | session-switch-to-terminal-9='disabled'
39 | session-switch-to-terminal-down='disabled'
40 | session-switch-to-terminal-left='disabled'
41 | session-switch-to-terminal-right='disabled'
42 | session-switch-to-terminal-up='disabled'
43 | terminal-close='disabled'
44 | terminal-find='disabled'
45 | terminal-find-next='disabled'
46 | terminal-find-previous='disabled'
47 | terminal-maximize='disabled'
48 | terminal-select-bookmark='disabled'
49 | terminal-toggle-margin='disabled'
50 | win-reorder-next-session='disabled'
51 | win-reorder-previous-session='disabled'
52 | win-switch-to-next-session='disabled'
53 | win-switch-to-previous-session='disabled'
54 | win-switch-to-session-0='disabled'
55 | win-switch-to-session-1='disabled'
56 | win-switch-to-session-2='disabled'
57 | win-switch-to-session-3='disabled'
58 | win-switch-to-session-4='disabled'
59 | win-switch-to-session-5='disabled'
60 | win-switch-to-session-6='disabled'
61 | win-switch-to-session-7='disabled'
62 | win-switch-to-session-8='disabled'
63 | win-switch-to-session-9='disabled'
64 | win-view-sidebar='disabled'
65 |
66 | [profiles/2b7c4080-0ddd-46c5-8f23-563fd3ba789d]
67 | visible-name='Default'
68 |
--------------------------------------------------------------------------------
/.tmux.conf:
--------------------------------------------------------------------------------
1 | # To reload this config file into running tmux sessions:
2 | # C-a :source-file ~/.tmux.conf
3 |
4 | # Use C-a as prefix (GNU screen muscle memory)
5 | unbind-key C-b
6 | set-option -g prefix C-a
7 |
8 | # "C-a a" to send a real C-a (i.e.: jumping to the line beginning)
9 | bind-key a send-prefix
10 |
11 | # C-a C-a to switch between two tabs
12 | bind-key C-a last-window
13 |
14 | # True color support https://github.com/tmux/tmux/issues/696
15 | set-option -ga terminal-overrides ",xterm-256color:Tc"
16 |
17 | # <><
18 | set-option -g default-shell /usr/bin/fish
19 |
20 | # Count windows and panes from 1
21 | set-option -g base-index 1
22 | set-window-option -g pane-base-index 1
23 |
24 | # Customize status bar
25 | # https://arcolinux.com/everything-you-need-to-know-about-tmux-status-bar/
26 | # Defaults (tmux show-options -g | grep status):
27 | # status on
28 | # status-interval 15
29 | # status-justify left
30 | # status-keys emacs
31 | # status-left "[#S] "
32 | # status-left-length 10
33 | # status-left-style default
34 | # status-position bottom
35 | # status-right " \"#{=21:pane_title}\" %H:%M %d-%b-%y"
36 | # status-right-length 40
37 | # status-right-style default
38 | # status-style fg=black,bg=green
39 | set-option -g status-position top
40 | set-option -g status-left ''
41 | set-option -g status-justify right
42 | set-option -g status-right '|#S'
43 | set-option -g status-bg '#117acc' # Same as VS Code Status Bar
44 | set-option -g status-fg '#ffffff' # Same as VS Code Status Bar
45 |
46 | # Comparison to screen: https://hyperpolyglot.org/multiplexers
47 |
48 | ### Misc
49 | # List sessions: tmux ls
50 | # New session foo: tmux new -s foo
51 | # Attach to session foo: tmux attach -t foo
52 | # Kill session foo: tmux kill-session -t foo
53 | # Kill tmux server: tmux kill-server
54 | # Detach from current session: C-a :detach
55 | # Detach session foo: tmux a -dt foo
56 | # Rename a session: C-a :rename-session
57 | # Rename a tab/window: C-a :rename-window
58 | # Change tag number from 2 to 3: C-a :move-window -s 2 -t 3
59 | # Renumber all tabs to be gapless: C-a :move-window -r
60 | # Show all settings: tmux show -g (or -s)
61 |
62 | ### Panes and tabs
63 | # Open a vertical pane: C-a + |
64 | # Open a horizontal pane: C-a + –
65 | # Move between panes: C-a + direction arrows
66 | # Drag a pane around: hold C-a and use direction arrows
67 | # Open a new tab: C-a + c
68 | # Move between tabs: C-a and use direction arrows
69 | # Close a tab: C-a :kill-window
70 |
--------------------------------------------------------------------------------
/.vim/syntax/txt2tags.vim:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aureliojargas/dotfiles/f35732301f6592415f45038c00207c7e463b685e/.vim/syntax/txt2tags.vim
--------------------------------------------------------------------------------
/.vimrc:
--------------------------------------------------------------------------------
1 | " Colors, please
2 | syntax on
3 |
4 | " Look mom, we're in the future!
5 | set encoding=utf-8
6 |
7 | " Fix common typos
8 | cabbrev W w
9 | cabbrev Q q
10 | cabbrev Wq wq
11 | cabbrev WQ wq
12 |
13 | " No beeps, just flashes
14 | set visualbell
15 |
16 | " Show line/column number at bottom
17 | set ruler
18 |
19 | " Show status line at bottom (and "file has changed" indicator)
20 | set laststatus=2
21 |
22 | " Options for a better search experience
23 | set incsearch hlsearch ignorecase smartcase magic
24 |
25 | " Colors for highlighted text (use NONE for transparent)
26 | highlight Visual ctermbg=Blue ctermfg=White
27 | highlight MatchParen ctermbg=Blue ctermfg=White
28 | highlight Search ctermbg=Yellow ctermfg=Black
29 |
30 | " Make tab completion more bash-like
31 | set wildmode=longest,list:full
32 |
33 | " My (current) code preferences
34 | set autoindent expandtab tabstop=4
35 |
36 | " Commands '>' and '<' will walk $tabstop spaces
37 | set shiftwidth=0
38 |
39 | " Append one, not two spaces after periods in 'gq'
40 | set nojoinspaces
41 |
42 | " Remember cursor position, save command history
43 | set viminfo='10,\"30,:40,%,n~/.viminfo
44 | autocmd BufReadPost * if line("'\"")|execute("normal `\"")|endif
45 |
46 | " Highlight tabs and trailing spaces
47 | " Sample: foo bar
48 | set list listchars=tab:\|·,trail:█,precedes:<,extends:>
49 |
50 | " Autosave, always
51 | autocmd TextChanged,TextChangedI * silent write
52 |
53 | " YAML defaults
54 | autocmd FileType yaml set tabstop=2
55 |
56 | " HTML/CSS defaults
57 | autocmd FileType html,css set tabstop=2 textwidth=0
58 |
59 | " Bash defaults
60 | autocmd FileType sh set textwidth=100
61 |
62 | " Python defaults
63 | autocmd FileType python set textwidth=88 " black default
64 | autocmd FileType python hi pythonString ctermfg=lightgreen
65 | autocmd FileType python hi pythonRawString ctermfg=lightgreen
66 |
67 | " txt2tags
68 | autocmd BufNewFile,BufRead *.t2t set filetype=txt2tags
69 |
70 | " Funções ZZ (tab-indented)
71 | autocmd BufNewFile,BufRead */funcoeszz/{*.sh,testador/run}
72 | \ set noexpandtab textwidth=72
73 |
74 | " Git commit message limited to 72 columns
75 | autocmd FileType gitcommit set textwidth=72
76 |
77 | " Quickly move betwwen open tabs
78 | nnoremap H :tabprevious
79 | nnoremap L :tabnext
80 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # dotfiles
2 |
3 | My config files.
4 |
5 | I try to be a minimalist. No config fireworks here.
6 |
7 | I use the Fish shell. See `.config/fish/`.
8 |
9 | ## Setup a new machine
10 |
11 | $ fish setup
12 |
--------------------------------------------------------------------------------
/bin/git-check-author:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env fish
2 | # List commits from all branches whose author email is not $email
3 |
4 | set email $argv[1]
5 |
6 | if test -z $email
7 | echo Missing argument: email
8 | exit 1
9 | end
10 |
11 | set branches (git branch -a | grep -v ' -> ' | cut -c3-) # local+remote
12 |
13 | for branch in $branches
14 | echo "$branch:"
15 | git log --oneline --date=short --pretty=format:"%h %ad [%ae] %s" $branch |
16 | grep -v -F "[$email]"
17 | end
18 |
--------------------------------------------------------------------------------
/bin/git-cleanup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env fish
2 |
3 | # Remove remote-tracking references that no longer exist on the remote
4 | git fetch --prune
5 |
6 | # Remove local branches that were already merged
7 | git branch --merged |
8 |
9 | # Always keep the current branch
10 | grep -v '^\* ' |
11 |
12 | # Remove left padding
13 | cut -c 3- |
14 |
15 | # Always keep these branches
16 | grep -v -x -e main -e master -e gh-pages |
17 |
18 | while read branch
19 | git branch --delete $branch
20 | end
21 |
--------------------------------------------------------------------------------
/bin/git-pre-commit:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env fish
2 | # To be run before attempting to create a new commit.
3 | # The idea here is avoiding committing personal stuff with work
4 | # credentials and vice versa.
5 |
6 | set author (git config user.email)
7 | set origin (git remote get-url origin)
8 |
9 | set perso_author "verde@"
10 | set perso_origin "github.com?aureliojargas/"
11 | # In previous line, ? matches / (https) and : (ssh)
12 |
13 | set is_perso_author (string match -- "*$perso_author*" $author)
14 | set is_perso_origin (string match -- "*$perso_origin*" $origin)
15 |
16 | # If author or repo are personal, both must be personal
17 | if test -n "$is_perso_author"
18 | and test -n "$is_perso_origin"
19 | :
20 | else if test -n "$is_perso_author"
21 | or test -n "$is_perso_origin"
22 | echo "WARNING: using $author in $origin. Is that ok?"
23 | read
24 | end
25 |
--------------------------------------------------------------------------------
/bin/git-pull-forced:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env fish
2 |
3 | # https://stackoverflow.com/a/5737794
4 | set dirty_tree (git status --porcelain)
5 |
6 | test -n "$dirty_tree"; and git stash --include-untracked
7 |
8 | # I'm assuming 10 commits ago the local copy sync'ed with the remote
9 | git fetch
10 | git reset --hard HEAD~10
11 | git merge FETCH_HEAD
12 |
13 | test -n "$dirty_tree"; and git stash pop
14 |
--------------------------------------------------------------------------------
/bin/git-replace-text:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env fish
2 | # Get replace at https://github.com/aureliojargas/replace
3 |
4 | function usage
5 | set -l script_name (basename (status -f))
6 | echo "Usage: $script_name pattern replacement [--regex]"
7 | end
8 |
9 | switch (count $argv)
10 | case 2
11 | set match_mode -F
12 | case 3
13 | set match_mode -P
14 | set regex_mode --regex
15 | case '*'
16 | usage
17 | exit 1
18 | end
19 |
20 | git grep $match_mode -I -l $argv[1] | while read path
21 | replace -i -f $argv[1] -t $argv[2] $regex_mode $path
22 | end
23 |
--------------------------------------------------------------------------------
/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env fish
2 | #
3 | # Setup all the dotfiles into a new machine.
4 | # May be run multiple times in the same machine.
5 | # Usage: ./setup
6 | #
7 | # GitHub Codespaces:
8 | # - This script is executed automatically in the container startup
9 | # - Logs: /workspaces/.codespaces/.persistedshare/creation.log
10 |
11 | # You should always run this script from the repository root
12 | set dotfiles_dir $PWD
13 |
14 | set user_bin $HOME/bin
15 |
16 | set fancy_diff_url https://github.com/so-fancy/diff-so-fancy/releases/download/v1.4.4/diff-so-fancy
17 | set replace_url https://raw.githubusercontent.com/aureliojargas/replace/main/replace.py
18 |
19 | function is_termux
20 | string match -q '*termux*' $HOME
21 | end
22 |
23 | function is_macos
24 | test (uname) = Darwin
25 | end
26 |
27 | function is_linux
28 | test -f /etc/debian_version
29 | and not is_github_codespace
30 | and not is_chromeos
31 | and not is_android
32 | end
33 |
34 | function is_chromeos
35 | test (uname -n) = penguin
36 | end
37 |
38 | function is_github_codespace
39 | test "$CODESPACES" = true
40 | end
41 |
42 | function is_android # avalible since Android 15
43 | test "$USER" = droid
44 | end
45 |
46 | function log
47 | set_color cyan
48 | echo "[$argv]"
49 | set_color normal
50 | end
51 |
52 | function done
53 | if set -q argv[1]
54 | set message $argv
55 | else
56 | set message Done
57 | end
58 | set_color green
59 | echo ✓ $message
60 | set_color normal
61 | end
62 |
63 | function already_done
64 | if set -q argv[1]
65 | set message $argv
66 | else
67 | set message Already done
68 | end
69 | set_color yellow
70 | echo ✓ $message
71 | set_color normal
72 | end
73 |
74 | function failed
75 | if set -q argv[1]
76 | set message $argv
77 | else
78 | set message Failed
79 | end
80 | set_color red
81 | echo × $message
82 | set_color normal
83 | end
84 |
85 | function run
86 | set fish_trace 1
87 | $argv
88 | set fish_trace 0
89 | end
90 |
91 | function download
92 | run curl --location --silent --output $argv[2] $argv[1]
93 | end
94 |
95 | function apt_install
96 | run sudo apt --yes --quiet --quiet install --no-install-recommends $argv
97 | end
98 |
99 | function brew_install
100 | run brew install --quiet $argv
101 | end
102 |
103 | function pipx_install
104 | run pipx install $argv
105 | end
106 |
107 | function create_symlink -a target symlink
108 | if not test (count $argv) -eq 2
109 | echo ERROR: usage: create_symlink target symlink
110 | exit 1
111 | end
112 |
113 | # Make sure the symlink parent dir exists
114 | mkdir -p (dirname $symlink)
115 |
116 | if test -L $symlink
117 | if test (readlink $symlink) = $target
118 | already_done $symlink is already done
119 | else
120 | failed $symlink is a symlink to an unknown file
121 | end
122 | else if test -f $symlink
123 | failed $symlink is a normal file, skipping symlink creation
124 | else if test -d $symlink
125 | failed $symlink is a directory, skipping symlink creation
126 | else
127 | ln -s -v $target $symlink
128 | end
129 | end
130 |
131 | #-----------------------------------------------------------------------
132 |
133 | log 'Detecting the platform'
134 | is_termux; and echo Termux
135 | is_macos; and echo macOS
136 | is_linux; and echo Linux
137 | is_chromeos; and echo ChromeOS '(Crostini)'
138 | is_github_codespace; and echo GitHub Codespace
139 | is_android; and echo Android native Terminal.app
140 |
141 | if is_github_codespace
142 | # This repo is always cloned in every codespace because I've enabled
143 | # it in my user configuration: Automatically install dotfiles
144 | set dotfiles_dir /workspaces/.codespaces/.persistedshare/dotfiles
145 | end
146 |
147 | set paths \
148 | .aliases \
149 | .config/fish/conf.d/my.fish \
150 | .config/fish/functions/brew_activate.fish \
151 | .config/fish/functions/fish_prompt.fish \
152 | .config/micro/bindings.json \
153 | .config/micro/plug \
154 | .config/micro/settings.json \
155 | .gemrc \
156 | .gitconfig \
157 | .inputrc \
158 | .screenrc \
159 | .tmux.conf \
160 | .vim \
161 | .vimrc
162 |
163 | if is_termux
164 | set paths $paths \
165 | .config/fish/conf.d/my.termux.fish \
166 | .termux
167 | end
168 |
169 | if is_macos
170 | set paths $paths \
171 | .config/fish/conf.d/my.osx.fish
172 | end
173 |
174 | log 'Create symlinks'
175 | for path_ in $paths
176 | create_symlink $dotfiles_dir/$path_ $HOME/$path_
177 | end
178 |
179 | if is_chromeos
180 | rm $HOME/.config/micro/bindings.json
181 | create_symlink \
182 | $dotfiles_dir/.config/micro/bindings.chromeos.json \
183 | $HOME/.config/micro/bindings.json
184 | end
185 |
186 | log 'Create user bin directory'
187 | if test -d $user_bin
188 | already_done
189 | else
190 | mkdir -p -v $user_bin
191 | end
192 |
193 | log 'Install my scripts in ~/bin'
194 | for path_ in bin/*
195 | create_symlink $dotfiles_dir/$path_ $HOME/$path_
196 | end
197 |
198 | log 'Install diff-so-fancy in user bin'
199 | set fancy_diff $user_bin/diff-so-fancy
200 | if test -f $fancy_diff; and test -x $fancy_diff
201 | already_done
202 | else
203 | download $fancy_diff_url $fancy_diff
204 | chmod +x $fancy_diff
205 | done Installed $fancy_diff
206 | end
207 |
208 | log 'Install the replace script'
209 | set replace_path $user_bin/replace
210 | if command --search --quiet replace
211 | already_done
212 | else
213 | download $replace_url $replace_path
214 | chmod +x $replace_path
215 | done Installed $replace_path
216 | end
217 |
218 | log 'Install extra software'
219 | if is_macos
220 | brew_install coreutils ed fish git-delta git-revise gnu-sed micro notunes pipx python shellcheck tig tree uv wget
221 |
222 | else if is_termux
223 | pkg update
224 | pkg install file fish git git-delta make openssh python tig tree vim
225 | pkg install termux-api # for pbcopy/pbpaste aliases
226 | python -m pip install --user pipx
227 | pipx_install git-revise uv
228 |
229 | log 'Some optional installs you may want to do'
230 | echo 'pkg install lynx # funcoeszz'
231 | echo 'pkg install perl # diff-so-fancy'
232 | echo 'pkg install ruby # Jekyll'
233 |
234 | else if is_linux
235 | sudo apt --quiet --quiet update
236 | apt_install ed make python3-pip tig tree
237 | brew_install fish git-delta git-revise micro pipx shellcheck uv
238 |
239 | log 'Some optional installs you may want to do'
240 | echo 'sudo apt install lynx # funcoeszz'
241 | echo 'sudo apt install ruby # Jekyll'
242 |
243 | else if is_android
244 | # brew is not supported on ARM64
245 | # https://docs.brew.sh/Homebrew-on-Linux#arm-unsupported
246 |
247 | sudo apt --quiet --quiet update
248 | apt_install ed fish make pipx python3-pip shellcheck tig tree
249 | pipx_install git-revise uv
250 |
251 | log 'Installing git-delta from GitHub'
252 | if command -v delta
253 | already_done
254 | else
255 | set delta_arm64_url https://github.com/dandavison/delta/releases/download/0.18.2/git-delta_0.18.2_arm64.deb
256 | download $delta_arm64_url /tmp/delta.deb
257 | run sudo dpkg -i /tmp/delta.deb
258 | done
259 | end
260 |
261 | log 'Installing micro from GitHub'
262 | if command -v micro
263 | already_done
264 | else
265 | set micro_version 2.0.14
266 | set micro_arm64_url https://github.com/zyedidia/micro/releases/download/v$micro_version/micro-$micro_version-linux-arm64.tgz
267 | download $micro_arm64_url /tmp/micro.tgz
268 | tar -C /tmp -xzf /tmp/micro.tgz
269 | cp /tmp/micro-$micro_version/micro ~/bin
270 | done Installed ~/bin/micro
271 | end
272 |
273 | log 'Some optional installs you may want to do'
274 | echo 'sudo apt install ruby # Jekyll'
275 |
276 | else if is_github_codespace
277 | # Install the missing software just in case we're running on the
278 | # default GitHub Codespaces container. This is a noop in my personal
279 | # devcontainer (https://github.com/aureliojargas/devcontainer) since
280 | # those are already installed there.
281 | sudo apt --quiet --quiet update
282 | apt_install ed git-revise shellcheck tig tree
283 | end
284 |
285 | if is_macos
286 | log "macOS: Add 'ed' symlink so GNU ed can be used as git core.editor"
287 | create_symlink /opt/homebrew/opt/ed/bin/ged $HOME/bin/ed
288 | end
289 |
290 | log 'Setup Git local configuration (manual intervention needed)'
291 | set git_config_local $HOME/.gitconfig.personal
292 | if is_github_codespace
293 | # GitHub Codespace sets GIT_COMMITTER_EMAIL and GIT_COMMITTER_NAME
294 | # env variables to the correct values
295 | already_done 'Skipped, git email/name already set by env vars.'
296 | else if test -f $git_config_local
297 | already_done
298 | else
299 | cp -v $dotfiles_dir/.gitconfig.local.template $git_config_local
300 | echo Please edit $git_config_local
301 | end
302 |
303 | # vim: filetype=fish
304 |
--------------------------------------------------------------------------------