Advertisement
  1. Computer Skills

fman: The Extendable File Manager for Any System

Scroll to top

Being a developer, I use the file manager all the time. I often find myself wanting to extend the file manager to help me automate some of my tasks. Unfortunately, the default file manager on most systems has always been very limiting.

fman File Managerfman File Managerfman File Manager
fman File Manager

The fman file manager is a game changer. fman is a minimalist dual pane file manager. It is extendable like Sublime Text using Python. It has all the same keyboard defaults as Commander One and similar file managers. You can use it on macOS, Windows, and Linux.

As of February 2017, fman is still in a private beta program that anyone can request access. The author is planning on starting sales on 1st March 2017.

Power Go To

One of the unique features of fman is the power go to. When you press Command-P, fman prompts for a directory name.

Power Go ToPower Go ToPower Go To
Power Go To

fman searches for matches. If you have many directories with the word fman you'll see all the directory paths containing that word. This makes finding the right directory very easy.

User Preferences

If you do not like the standard hotkeys, you can change them in the User directory where the plugins live. On macOS the plugins directory is ~/Library/Application Support/fman/Plugins/

On Windows, the plugins directory is %APPDATA%\fman. On a Linux system, the plugins directory is ~/.config/fman

The User directory in the plugins directory contains the file Key Bindings.json. This JSON formatted file contains your preferences for the hotkeys. The file format is:

1
[        { keys: [Down], command: move_cursor_down },        { keys: [Shift+Down], command: move_cursor_down, args: {toggle_selection: true} },        { keys: [Up], command: move_cursor_up },        { keys: [Shift+Up], command: move_cursor_up, args: {toggle_selection: true} },        { keys: [Home], command: move_cursor_home },        { keys: [Shift+Home], command: move_cursor_home, args: {toggle_selection: true} },        { keys: [End], command: move_cursor_end },        { keys: [Shift+End], command: move_cursor_end, args: {toggle_selection: true} },        { keys: [PgDwn], command: move_cursor_page_down },        { keys: [Shift+PgDown], command: move_cursor_page_down, args: {toggle_selection: true} },        { keys: [PgUp], command: move_cursor_page_up },        { keys: [Shift+PgUp], command: move_cursor_page_up, args: {toggle_selection: true} },        { keys: [Ins], command: move_cursor_down, args: {toggle_selection: true} },        { keys: [Space], command: toggle_selection },        { keys: [Backspace], command: go_up },        { keys: [Enter], command: open },        { keys: [Return], command: open },        { keys: [F4], command: open_with_editor },        { keys: [Shift+F4], command: open_with_editor, args: {create_new: true} },        { keys: [F5], command: copy },        { keys: [Shift+F6], command: rename },        { keys: [F6], command: move },        { keys: [F7], command: create_directory },        { keys: [F8], command: move_to_trash },        { keys: [Delete], command: move_to_trash },        { keys: [F9], command: open_terminal },        { keys: [F10], command: open_native_file_manager },        { keys: [F11], command: copy_paths_to_clipboard },        { keys: [Ctrl+Right], command: open_in_right_pane },        { keys: [Ctrl+Left], command: open_in_left_pane },        { keys: [Cmd+C], command: copy_to_clipboard },        { keys: [Cmd+V], command: paste },        { keys: [Cmd+Alt+V], command: paste_cut },        { keys: [Cmd+A], command: select_all },        { keys: [Space], command: move_cursor_down, args: {toggle_selection: true} },        { keys: [Cmd+Backspace], command: move_to_trash },        { keys: [Cmd+.], command: toggle_hidden_files },        { keys: [F2], command: show_volumes },        { keys: [Cmd+P], command: go_to}]

In this file, you specify the key to press and the command that you wish to execute. I use this file to figure out what commands come with the program.

The Command PromptThe Command PromptThe Command Prompt
The Command Prompt

Press Shift-Command-P in fman to show the command prompt in which a command can be executed. The command prompt displays any hotkey assignments to the command. 

To use a command in the command prompt type a space instead of an underscore. For example, the command show_volumes is run by typing show volumes in the command prompt.

You can launch any command in an extension from the command prompt. This feature is a convenient and a powerful way to get things done.

Adding Extensions

Extensions can be added to fman. From the extensions page of fman’s website, click any extension to download it. Then copy the extension’s full directory to the plugins directory.

fman With Status Bar Extender Pluginfman With Status Bar Extender Pluginfman With Status Bar Extender Plugin
fman With Status Bar Extender Plugin

By downloading the Status Bar Extender extension, the status bar will have more helpful information about the current directory for each pane.

Extensions can change the default behavior of fman

For example, my extension OpenWithEditor that overrides the open_with_editor built-in command. 

The built-in command opens the file with the default program for the extension. My extension overrides that command to open a file using the editor selected in my BitBar plugin currentFiles.1h.rb created in the BitBar tutorial on Tuts+.

Writing Extensions

To create extensions, first create a directory for the extension in the plugins directory. 

In this tutorial, I want an extension that will allow me to set a directory as the project directory and run a command script whenever entering this directory when a different project is set as the current project. 

So, when I leave one project directory that I have been working and go to a different project, it will execute the script to setup my environment. I’ll be creating this extension on macOS, but the exact same extension will work in Windows and Linux with some minor modifications.

To start, create the directory ProjectManager in the plugins directory. Then create another directory in this directory called projectmanager. This directory has to be all lower case and no spaces. 

Inside this directory, create the file __init__.py. You can have any other files in the directory that contains python code as well. When the extension is loaded, the code in this file gets executed first. It should load any other files as needed. 

In this file, add the following:

1
## Load the libraries that are used in these commands.#from fman import DirectoryPaneCommand, DirectoryPaneListener, show_alert, load_json, DATA_DIRECTORY, show_promptimport os, stat## I’m using two globals because it is faster for checking# the directories. I also have an Alfred workflow that makes# use of this information.#PROJECTDIR = os.path.expanduser(“~“) + “/.currentprojectdir”PROJECTSLIST = os.path.expanduser(“~“) + “/.projects”

This code loads the libraries used in this extension. I’m pulling several functions from the fman library. These items are:

 Library or Function 
Description
DirectoryPaneCommand
This is the object to subclass to create a directory level command.
DirectoryPaneListener This is the object to subclass to create a function that will listen to changes in the directory pane.
show_alert
This function will show an alert dialog with the message given. 
load_json
This loads in json data files from the User directory. 
DATA_DIRECTORY 
This is the directory for storing fman data. 
show_prompt 
This gets an input from the user with the given message.

You can see all the functions available in the fman documentation page. Currently, it is a list of function names and their default inputs.

After loading the libraries, set up two global variables: PROJECTDIR and PROJESTSLIST.

These globals help make the directory checking function run quickly. That is important when writing a routine that executes every time fman changes directories.

1
class SetProjectDirectory(DirectoryPaneCommand):    #    # This directory command is for setting up a new project    # directory. It will add to the list of project directories    # and set the current project directory to the directory.    #    def __call__(self):        #        # Get the directory path.        #        selected_files = self.pane.get_selected_files()        if len(selected_files) >= 1 or (len(selected_files) == 0 and self.get_chosen_files()):            if len(selected_files) == 0 and self.get_chosen_files():                selected_files.append(self.get_chosen_files()[0])            dirName = selected_files[0]            if os.path.isfile(dirName):                #                # It’s a file, not a directory. Get the directory                # name for this file’s parent directory.                #                dirName = os.path.dirname(dirName)            #            # Set the directory obtained as a project directory.            #            with open(PROJECTDIR, “w”) as f:                f.write(dirName)            #            # Add to the list of projects. Get a name            # from the user.            #            projName, checked = show_prompt(“Name this Project:“)            projEntry = projName + “|” + dirName            writeappend = ‘w’            if os.path.isfile(PROJECTSLIST):                writeappend = ‘a’            with open(PROJECTSLIST,writeappend) as f:                f.write(projEntry+“\n”)            #            # Create the launch script file and open in the            # editor.            #            scriptFile = dirName + “/.startproject”            with open(scriptFile, ‘w’) as f:                f.write(“#!/bin/sh\n\n”)            os.chmod(scriptFile, stat.S_IEXEC|stat.S_IRUSR|stat.S_IWUSR)            scriptLoc = load_json(“OpenWithEditor.json”)[“scriptLoc”]            if scriptLoc is None:                #                # Open the file with the TextEdit that is on every Mac.                #                os.system(“/usr/bin/open -a TextEdit ‘” + scriptFile + “‘“)            else:                #                # They have the OpenWithEditor extension. Use it.                #                os.system(“‘” + scriptLoc + “’ ‘file’ ‘” + scriptFile + “’ &“)        else:            #            # Technically, this will never be reached. Just here            # for completeness.            #            show_alert(“No directory selected”)

The SetProjectDirectory class is a sub-class of DirectoryPaneCommand. The __call__ method is run whenever the command set_project_directory is run. 

This function gets the current directory, sets it as the current project, asks the user for a name for the project, saves that into a projects list file, creates the .startproject file, and opens it in the editor of choice by the OpenWithEditor extension or TextEdit.

1
class ClearProjectDirectory(DirectoryPaneCommand):    #    # This directory command is clearing out the current project.    # This is good to do before exiting fman.    #    def __call__(self):        with open(PROJECTDIR,‘w’) as f:            f.write(““)

The next command is ClearProjectDirectory. This command clears out the ~/.currentproject file.

1
class EditProjectStartScript(DirectoryPaneCommand):    #    # This directory command is clearing out the current project.    # This is good to do before exiting fman.    #    def __call__(self):        #        # Get the current project directory.        #        dirName = “”        with open(PROJECTDIR,‘r’) as f:            dirName = f.read()        if dirName != ““:            #            # A project directory is set. Edit it’s start file.            #            scriptFile = dirName + “/.startproject”            scriptLoc = load_json(“OpenWithEditor.json”)[“scriptLoc”]            if scriptLoc is None:                #                # Open the file with the TextEdit that is on every Mac.                #                os.system(“/usr/bin/open -a TextEdit ‘” + scriptFile + “‘“)            else:                #                # They have the OpenWithEditor extension. Use it.                #                os.system(“‘” + scriptLoc + “’ ‘file’ ‘” + scriptFile + “’ &“)

The EditProjectStartScript command allows you to edit the .startproject script for the currently set project. 

You do not have to be in the project’s main directory to run this command but you must have a valid project set up.

1
class EnteringProjectDirectory(DirectoryPaneListener):    #    # This is called everytime a directory is changed    # in fman. Do a quick check to see if it's a project    # directory. If so, if it's not the current project,    # set to the current project and run the setup script    # for the project.    #    def on_path_changed(self):        #        # See if the new directory is a project directory.        #        newDir = self.pane.get_path()        scriptFile = newDir + "/.startproject"        if os.path.isfile(scriptFile):            #            # Get the current project name and see if they            # are the same.            #            projDir = ""            with open(PROJECTDIR) as f:                projDir = f.read()            if projDir != newDir:                #                # They are different! Set the new project directory                # and run the .startproject script.                #                with open(PROJECTDIR, "w") as f:                    f.write(newDir)                os.system("'" + scriptFile + "'")

The EnteringProjectDirectory command is the main command for this extension. All the other commands get this command to work.

Since it is a sub-class of the DirectoryPaneListener, it runs every time directories are changed. For that reason, this routine should be as short and fast as possible. Otherwise, fman will get very cumbersome to use. 

This command checks to see if it has entered a project directory. If so, it checks for being different from the currently set project. If that is true, then it sets the new project directory and runs the project’s .startproject script. 

To test the system, I added this code to the .startproject file:

1
#!/bin/shecho “d.type = date\nd.label = Example date\nd.default = 2007-05-30 17:00\nd.time = 1” | /Applications/Pashua.app/Contents/MacOS/Pashua -

This uses the Pashua program to show a calendar and clock. It makes for a quick visual check to see that the program is working.

The next item is the hotkey assignments for the extension. In the top of the extension directory, add this to the Key Bindings.json file:

1
[        { keys: [shift+s], command: set_project_directory },        { keys: [shift+c], command: clear_project_directory },        { keys: [shift+e], command: edit_project_start_script}]

Each extension can have assigned hotkeys to use. These get loaded before the same file gets loaded from the Users extension. 

In that way, the user can override anything setup in the extensions. I’m assigning Shift-S to the set_project_directory command. I’m assigning Shift-C to the clear_project_directory command. And, I’m assigning Shift-E to the edit_project_start_script command.

If you set the ProjectManager directory as the project directory, give it the project name Project Manager, exit the directory, clear out the projects with Shift-C or running the clear_project_directory from the command prompt, and then enter the directory again.

Executing the Test Script for Project ManagerExecuting the Test Script for Project ManagerExecuting the Test Script for Project Manager
Executing the Test Script for Project Manager

You will see the Pashua prompt showing the date and time. Now you can create start scripts that launch the text editor with the project files and organize the windows on the screen for better working. 

I use HammerSpoon to create the window layouts from a script. You could also run directory watchers to recompile your project and load into a web browser. 

The possibilities are endless.

Conclusion

Now that you know how to create extensions for fman, go ahead and create your own.

There are many ways you can extend the functionality of this simple extension. fman is a lot of fun to use and is very easy to extend to have the functionality you need.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Computer Skills tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.