Skip to content
49 changes: 0 additions & 49 deletions filemanager-plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,52 +5,3 @@ A simple plugin that allows for easy navigation of a file tree.
![Example picture](./example.jpg?raw=true "Example")

**Installation:** run `plugin install filemanager` and restart Micro.

## Basics

The top line always has the current directory's path to show you where you are.\
The `..` near the top is used to move back a directory, from your current position.

All directories have a `/` added to the end of it, and are syntax-highlighted as a `special` character.\
If the directory is expanded, there will be a `+` to the left of it. If it is collapsed there will be a `-` instead.

**NOTE:** If you change files without using the plugin, it can't know what you did. The only fix is to close and open the tree.

### Options

| Option | Purpose | Default |
| :--------------------------- | :----------------------------------------------------------- | :------ |
| `filemanager-showdotfiles` | Show dotfiles (hidden if false) | `true` |
| `filemanager-showignored` | Show gitignore'd files (hidden if false) | `true` |
| `filemanager-compressparent` | Collapse the parent dir when left is pressed on a child file | `true` |
| `filemanager-foldersfirst` | Sorts folders above any files | `true` |
| `filemanager-openonstart` | Automatically open the file tree when starting Micro | `false` |

### Commands and Keybindings

The keybindings below are the equivalent to Micro's defaults, and not actually set by the plugin. If you've changed any of those keybindings, then that key is used instead.

If you want to [keybind](https://github.com/zyedidia/micro/blob/master/runtime/help/keybindings.md#rebinding-keys) any of the operations/commands, bind to the labeled API in the table below.

| Command | Keybinding(s) | What it does | API for `bindings.json` |
| :------- | :------------------------- | :------------------------------------------------------------------------------------------ | :------------------------------------ |
| `tree` | - | Open/close the tree | `filemanager.toggle_tree` |
| - | <kbd>Tab</kbd> & MouseLeft | Open a file, or go into the directory. Goes back a dir if on `..` | `filemanager.try_open_at_cursor` |
| - | <kbd>→</kbd> | Expand directory in tree listing | `filemanager.uncompress_at_cursor` |
| - | <kbd>←</kbd> | Collapse directory listing | `filemanager.compress_at_cursor` |
| - | <kbd>Shift ⬆</kbd> | Go to the target's parent directory | `filemanager.goto_parent_dir` |
| - | <kbd>Alt Shift {</kbd> | Jump to the previous directory in the view | `filemanager.goto_next_dir` |
| - | <kbd>Alt Shift }</kbd> | Jump to the next directory in the view | `filemanager.goto_prev_dir` |
| `rm` | - | Prompt to delete the target file/directory your cursor is on | `filemanager.prompt_delete_at_cursor` |
| `rename` | - | Rename the file/directory your cursor is on, using the passed name | `filemanager.rename_at_cursor` |
| `touch` | - | Make a new file under/into the file/directory your cursor is on, using the passed name | `filemanager.new_file` |
| `mkdir` | - | Make a new directory under/into the file/directory your cursor is on, using the passed name | `filemanager.new_dir` |

#### Notes

- `rename`, `touch`, and `mkdir` require a name to be passed when calling.\
Example: `rename newnamehere`, `touch filenamehere`, `mkdir dirnamehere`.\
If the passed name already exists in the current dir, it will cancel instead of overwriting (for safety).

- The <kbd>Ctrl w</kbd> keybinding is to switch which buffer your cursor is on.\
This isn't specific to the plugin, it's just part of Micro, but many people seem to not know this.
62 changes: 32 additions & 30 deletions filemanager-plugin/filemanager.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
VERSION = "3.5.1"
VERSION = "3.5.2"

local micro = import("micro")
local config = import("micro/config")
Expand Down Expand Up @@ -53,10 +53,8 @@ end

-- A check for if a path is a dir
local function is_dir(path)
-- Used for checking if dir
local golib_os = import("os")
-- Returns a FileInfo on the current file/path
local file_info, stat_error = golib_os.Stat(path)
local file_info, stat_error = os.Stat(path)
-- Wrap in nil check for file/dirs without read permissions
if file_info ~= nil then
-- Returns true/false if it's a dir
Expand All @@ -74,15 +72,15 @@ end
local function get_ignored_files(tar_dir)
-- True/false if the target dir returns a non-fatal error when checked with 'git status'
local function has_git()
local git_rp_results = shell.ExecCommand('git -C "' .. tar_dir .. '" rev-parse --is-inside-work-tree')
local git_rp_results = shell.ExecCommand('git', '-C', tar_dir, 'rev-parse', '--is-inside-work-tree')
return git_rp_results:match("^true%s*$")
end
local readout_results = {}
-- TODO: Support more than just Git, such as Mercurial or SVN
if has_git() then
-- If the dir is a git dir, get all ignored in the dir
local git_ls_results =
shell.ExecCommand('git -C "' .. tar_dir .. '" ls-files . --ignored --exclude-standard --others --directory')
shell.ExecCommand('git', '-C', tar_dir, 'ls-files', '.', '--ignored', '--exclude-standard', '--others', '--directory')
-- Cut off the newline that is at the end of each result
for split_results in string.gmatch(git_ls_results, "([^\r\n]+)") do
-- git ls-files adds a trailing slash if it's a dir, so we remove it (if it is one)
Expand Down Expand Up @@ -438,6 +436,12 @@ end
-- Prompts the user for deletion of a file/dir when triggered
-- Not local so Micro can access it
function prompt_delete_at_cursor()

if micro.CurPane() ~= tree_view then
micro.InfoBar():Message('"rm" only works with the cursor in the tree!')
return
end

local y = get_safe_y()
-- Don't let them delete the top 3 index dir/separator/..
if y == 0 or scanlist_is_empty() then
Expand All @@ -446,12 +450,10 @@ function prompt_delete_at_cursor()
return
end

micro.InfoBar():YNPrompt("Do you want to delete the " .. (scanlist[y].dirmsg ~= "" and "dir" or "file") .. ' "' .. scanlist[y].abspath .. '"? ', function(yes, canceled)
micro.InfoBar():YNPrompt("Do you want to delete the " .. (scanlist[y].dirmsg ~= "" and "dir" or "file") .. ' "' .. scanlist[y].abspath .. '"? (y/n) ', function(yes, canceled)
if yes and not canceled then
-- Use Go's os.Remove to delete the file
local go_os = import("os")
-- Delete the target (if its a dir then the children too)
local remove_log = go_os.RemoveAll(scanlist[y].abspath)
local remove_log = os.RemoveAll(scanlist[y].abspath)
if remove_log == nil then
micro.InfoBar():Message("Filemanager deleted: ", scanlist[y].abspath)
-- Remove the target (and all nested) from scanlist[y + 1]
Expand Down Expand Up @@ -596,14 +598,13 @@ end

-- Stat a path to check if it exists, returning true/false
local function path_exists(path)
local go_os = import("os")
-- Stat the file/dir path we created
-- file_stat should be non-nil, and stat_err should be nil on success
local file_stat, stat_err = go_os.Stat(path)
local file_stat, stat_err = os.Stat(path)
-- Check if what we tried to create exists
if stat_err ~= nil then
-- true/false if the file/dir exists
return go_os.IsExist(stat_err)
return os.IsExist(stat_err)
elseif file_stat ~= nil then
-- Assume it exists if no errors
return true
Expand Down Expand Up @@ -641,10 +642,15 @@ function rename_at_cursor(bp, args)
local old_path = scanlist[y].abspath
-- Join the path into their supplied rename, so that we have an absolute path
local new_path = dirname_and_join(old_path, new_name)
-- Use Go's os package for renaming the file/dir
local golib_os = import("os")

-- Check if the name is already taken by a file/dir
if path_exists(new_path) then
micro.InfoBar():Error("You can't create a file/dir with a pre-existing name")
return
end

-- Actually rename the file
local log_out = golib_os.Rename(old_path, new_path)
local log_out = os.Rename(old_path, new_path)
-- Output the log, if any, of the rename
if log_out ~= nil then
micro.Log("Rename log: ", log_out)
Expand Down Expand Up @@ -704,16 +710,14 @@ local function create_filedir(filedir_name, make_dir)
return
end

-- Use Go's os package for creating the files
local golib_os = import("os")
-- Create the dir or file
if make_dir then
-- Creates the dir
golib_os.Mkdir(filedir_path, golib_os.ModePerm)
os.Mkdir(filedir_path, os.ModePerm)
micro.Log("Filemanager created directory: " .. filedir_path)
else
-- Creates the file
golib_os.Create(filedir_path)
os.Create(filedir_path)
micro.Log("Filemanager created file: " .. filedir_path)
end

Expand Down Expand Up @@ -958,11 +962,9 @@ function goto_parent_dir()
end

function try_open_at_cursor()
if micro.CurPane() ~= tree_view or scanlist_is_empty() then
return
if micro.CurPane() == tree_view then
try_open_at_y(tree_view.Cursor.Loc.Y)
end

try_open_at_y(tree_view.Cursor.Loc.Y)
end

-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down Expand Up @@ -1085,14 +1087,11 @@ function onPreviousSplit(view)
end

-- On click, open at the click's y
function preMousePress(view, event)
function onMousePress(view)
if view == tree_view then
local x, y = event:Position()
-- Fixes the y because softwrap messes with it
local new_x, new_y = tree_view:GetMouseClickLocation(x, y)
-- Try to open whatever is at the click's y index
-- Will go into/back dirs based on what's clicked, nothing gets expanded
try_open_at_y(new_y)
try_open_at_cursor()
-- Don't actually allow the mousepress to trigger, so we avoid highlighting stuff
return false
end
Expand Down Expand Up @@ -1139,7 +1138,7 @@ function preIndentSelection(view)
tab_pressed = true
-- Open the file
-- Using tab instead of enter, since enter won't work with Readonly
try_open_at_y(tree_view.Cursor.Loc.Y)
try_open_at_cursor()
-- Don't actually insert a tab
return false
end
Expand Down Expand Up @@ -1363,9 +1362,12 @@ function init()
config.MakeCommand("mkdir", new_dir, config.NoComplete)
-- Delete a file/dir, and anything contained in it if it's a dir
config.MakeCommand("rm", prompt_delete_at_cursor, config.NoComplete)

-- Adds colors to the ".." and any dir's in the tree view via syntax highlighting
-- TODO: Change it to work with git, based on untracked/changed/added/whatever
config.AddRuntimeFile("filemanager", config.RTSyntax, "syntax.yaml")
-- Add the help file
config.AddRuntimeFile("filemanager", config.RTHelp, "help/filemanager.md")

-- NOTE: This must be below the syntax load command or coloring won't work
-- Just auto-open if the option is enabled
Expand Down
52 changes: 52 additions & 0 deletions filemanager-plugin/help/filemanager.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Filemanager Plugin

A simple plugin that allows for easy navigation of a file tree.

## Basics

The top line always has the current directory's path to show you where you are.\
The `..` near the top is used to move back a directory, from your current position.

All directories have a `/` added to the end of it, and are syntax-highlighted as a `special` character.\
If the directory is expanded, there will be a `+` to the left of it. If it is collapsed there will be a `-` instead.

**NOTE:** If you change files without using the plugin, it can't know what you did. The only fix is to close and open the tree.

### Options

| Option | Purpose | Default |
| :--------------------------- | :----------------------------------------------------------- | :------ |
| `filemanager.showdotfiles` | Show dotfiles (hidden if false) | `true` |
| `filemanager.showignored` | Show gitignore'd files (hidden if false) | `true` |
| `filemanager.compressparent` | Collapse the parent dir when left is pressed on a child file | `true` |
| `filemanager.foldersfirst` | Sorts folders above any files | `true` |
| `filemanager.openonstart` | Automatically open the file tree when starting Micro | `false` |

### Commands and Keybindings

The keybindings below are the equivalent to Micro's defaults, and not actually set by the plugin. If you've changed any of those keybindings, then that key is used instead.

If you want to [keybind](https://github.com/zyedidia/micro/blob/master/runtime/help/keybindings.md#rebinding-keys) any of the operations/commands, bind to the labeled API in the table below.

| Command | Keybinding(s) | What it does | API for `bindings.json` |
| :------- | :------------------------- | :------------------------------------------------------------------------------------------ | :------------------------------------ |
| `tree` | - | Open/close the tree | `filemanager.toggle_tree` |
| - | <kbd>Tab</kbd> & MouseLeft | Open a file, or go into the directory. Goes back a dir if on `..` | `filemanager.try_open_at_cursor` |
| - | <kbd>→</kbd> | Expand directory in tree listing | `filemanager.uncompress_at_cursor` |
| - | <kbd>←</kbd> | Collapse directory listing | `filemanager.compress_at_cursor` |
| - | <kbd>Shift ⬆</kbd> | Go to the target's parent directory | `filemanager.goto_parent_dir` |
| - | <kbd>Alt Shift {</kbd> | Jump to the previous directory in the view | `filemanager.goto_next_dir` |
| - | <kbd>Alt Shift }</kbd> | Jump to the next directory in the view | `filemanager.goto_prev_dir` |
| `rm` | - | Prompt to delete the target file/directory your cursor is on | `filemanager.prompt_delete_at_cursor` |
| `rename` | - | Rename the file/directory your cursor is on, using the passed name | `filemanager.rename_at_cursor` |
| `touch` | - | Make a new file under/into the file/directory your cursor is on, using the passed name | `filemanager.new_file` |
| `mkdir` | - | Make a new directory under/into the file/directory your cursor is on, using the passed name | `filemanager.new_dir` |

#### Notes

- `rename`, `touch`, and `mkdir` require a name to be passed when calling.\
Example: `rename newnamehere`, `touch filenamehere`, `mkdir dirnamehere`.\
If the passed name already exists in the current dir, it will cancel instead of overwriting (for safety).

- The <kbd>Ctrl w</kbd> keybinding is to switch which buffer your cursor is on.\
This isn't specific to the plugin, it's just part of Micro, but many people seem to not know this.