The File Extension Associations feature allows you to configure which programs XeFM uses to open, view, and edit different types of files based on their extensions. This provides a flexible way to customize how XeFM handles various file types.
File associations let you configure which programs XeFM uses to open, view, and edit different file types. For example, you can use Preview for viewing images but Photoshop for editing them.
Add this to your ~/.xefm/config.py:
FILE_ASSOCIATIONS = [
# Images: Multiple patterns, Preview for viewing, Photoshop for editing
{
'pattern': ['*.jpg', '*.jpeg', '*.png', '*.gif'],
'open|view': ['open', '-a', 'Preview'], # Same for open and view
'edit': ['open', '-a', 'Photoshop']
},
# Videos: QuickTime for viewing, Final Cut for editing
{
'pattern': ['*.mp4', '*.mov'],
'open|view': ['open', '-a', 'QuickTime Player'],
'edit': ['open', '-a', 'Final Cut Pro']
},
# PDFs: Preview for viewing, Acrobat for editing
{
'pattern': '*.pdf',
'open|view': ['open', '-a', 'Preview'],
'edit': ['open', '-a', 'Adobe Acrobat']
}
]
File associations are configured in your ~/.xefm/config.py file using the FILE_ASSOCIATIONS list.
FILE_ASSOCIATIONS = [
{
'pattern': '*.ext' or ['*.ext1', '*.ext2'], # Single or multiple
'open|view': ['command', 'args'], # Combined actions
'edit': ['command', 'args'] # Separate action
}
]
| to assign same command to multiple actionsEach file pattern can configure up to four actions:
| Action | Key | What it does | Value |
|---|---|---|---|
| enter | Enter |
Casual open — handled inside XeFM | A built-in handler name |
| open | Cmd/Ctrl-Enter |
Deliberate open — hands off to another app | A command |
| view | V |
View the file | A command |
| edit | E |
Edit the file | A command |
Enter and Cmd/Ctrl-Enter are deliberately different gestures:
Enter never leaves XeFM. It enters directories, browses archives, and
opens files in the built-in viewer. It is safe to lean on — it will not
launch an application or steal focus.Cmd/Ctrl-Enter hands the file to a real application. Use it when you
actually want Preview, an IDE, or the OS default app.Because the enter tier stays inside XeFM, its value names a built-in
handler rather than a program to launch:
| Value | Effect |
|---|---|
'viewer' |
Open in the built-in text/markdown viewer |
'navigate' |
Browse the file as an archive (useful for *.jar, *.whl) |
None |
Do nothing |
| (no rule) | XeFM’s default: enter directories and archives, view files |
{
'pattern': '*.csv',
'enter': 'viewer', # Enter -> built-in viewer
'open': ['open', '-a', 'Numbers'], # Cmd-Enter -> Numbers
}
Programs can be specified in two formats:
'open': ['open', '-a', 'Preview']
'open': 'open -a Preview'
'edit': None # No editor configured for this file type
For view, None means something more specific: use the built-in
viewer. See Text Files.
You do not declare this. Whether XeFM hands over the display is a property of the backend you are running, not of the program you configured:
| Mode | What happens when a program launches |
|---|---|
Terminal (--backend tui) |
XeFM suspends, the program owns the terminal, XeFM restores and repaints when it exits |
| Desktop mode | There is no terminal to hand over, so the program is detached and XeFM stays responsive |
So 'view': ['less'] simply works in terminal mode — no flag needed:
{
'pattern': '*.log',
'view': ['less'], # terminal mode hands the display over and waits
'edit': ['code'], # a GUI editor in the same entry is fine
}
The practical consequence is the same one that governs TEXT_EDITOR: pick
programs that suit the mode you run in. A terminal program configured while
running in desktop mode has no terminal to draw on and will not appear; a GUI
launcher used in terminal mode works, with a brief repaint as XeFM resumes.
Earlier drafts of this feature had a per-entry
'terminal': Trueflag. It was removed: it duplicated a decision XeFM can already make, could not express one entry mixing a terminal viewer with a GUI editor, and failed unsafely — forgetting it onlesscorrupted the terminal. A leftoverterminalkey in a hand-written config is ignored.
Group related file patterns together:
{
'pattern': ['*.jpg', '*.jpeg', '*.png', '*.gif']
}
Instead of repeating the same configuration for each extension.
Use the pipe | operator to assign the same command to multiple actions:
{
'open|view': ['open', '-a', 'Preview']
}
This clearly shows that open and view use the same program, making the intent explicit.
Single pattern as string:
'pattern': '*.pdf'
Multiple patterns as list:
'pattern': ['*.mp4', '*.mov', '*.avi']
Old Format (Verbose):
FILE_ASSOCIATIONS = {
'*.jpg': {
'open': ['open', '-a', 'Preview'],
'view': ['open', '-a', 'Preview'],
'edit': ['open', '-a', 'Photoshop']
},
'*.jpeg': {
'open': ['open', '-a', 'Preview'],
'view': ['open', '-a', 'Preview'],
'edit': ['open', '-a', 'Photoshop']
},
# ... repeat for each extension
}
New Format (Compact):
FILE_ASSOCIATIONS = [
{
'pattern': ['*.jpg', '*.jpeg', '*.png', '*.gif'],
'open|view': ['open', '-a', 'Preview'],
'edit': ['open', '-a', 'Photoshop']
}
]
Reduction: 75% fewer lines!
FILE_ASSOCIATIONS entries are checked in order from top to bottom. This allows you to define specific rules before general rules, giving you fine-grained control over file handling.
When XeFM needs to find a program for a file and action:
First matching entry wins - but only if the action is present in that entry.
FILE_ASSOCIATIONS = [
# Specific: Test files
{
'pattern': 'test_*.py',
'open': ['pytest', '-v'],
'edit': ['vim']
},
# General: All Python files
{
'pattern': '*.py',
'open': ['python3'],
'view': ['less'],
'edit': ['vim']
}
]
Behavior:
test_main.py + open → pytest -v (matches first entry)test_main.py + view → less (first entry has no ‘view’, uses second entry)script.py + open → python3 (doesn’t match first pattern, uses second entry)FILE_ASSOCIATIONS = [
# Specific: README files with special viewer
{
'pattern': 'README*',
'view': ['glow'] # Markdown renderer
},
# General: All markdown files
{
'pattern': '*.md',
'open': ['typora'],
'view': ['less'],
'edit': ['vim']
}
]
Behavior:
README.md + view → glow (matches first entry)README.md + open → typora (first entry has no ‘open’, uses second entry)notes.md + view → less (doesn’t match first pattern, uses second entry)Once configured, XeFM will use these associations when you:
Enter uses the enter action. It never launches an external program.
Behavior:
'viewer' opens the built-in viewer; 'navigate' browses the file as an
archive; None does nothingopen_with_os, rather than
opening a viewer on content it cannot renderStep 5 is why images currently report “No built-in viewer for photo.png —
press Command-ENTER to open it in an external program”. Setting
'enter': 'viewer' on such a pattern overrides this and opens the viewer
anyway, which shows a binary placeholder.
Cmd-Enter (Ctrl-Enter on Windows) uses the open action.
Behavior:
None, nothing is launched (this is how you stop a file type
from ever being handed to the OS)open / xdg-open / start)When you press V on a file, XeFM uses the view action from file associations.
Behavior:
None, opens the built-in text viewerRemote and in-archive files always use the built-in viewer — an external program has no path on disk it could open.
When you press E, XeFM edits the selected files — or the focused file when nothing is selected — using the edit action from file associations.
Behavior (per file):
None, reports that no editor is configured and stopsTEXT_EDITOR config settingFiles that resolve to the same program are passed to it in one launch
(vim a.txt b.txt), so a multi-file edit is one editor session — a mixed
selection can still fan out to one launch per distinct editor.
Local files only; remote and in-archive paths are skipped (in a mixed selection they are skipped individually, with a log line each).
{
'pattern': ['*.jpg', '*.png'],
'open|view': ['open', '-a', 'Preview'], # Same for both
'edit': ['open', '-a', 'Photoshop'] # Different editor
}
Usage:
photo.jpg → Opens in Previewphoto.jpg → Opens in Preview (same as Enter)photo.jpg → Opens in Photoshop{
'pattern': '*.avi',
'open|view': ['open', '-a', 'VLC'],
'edit': None # No editor configured
}
Usage:
movie.avi → Opens in VLCmovie.avi → Opens in VLCmovie.avi → Shows “No editor configured” message{
'pattern': '*.txt',
'open': ['open', '-e'], # TextEdit
'edit': ['vim'] # Terminal editor
# 'view' omitted - will use built-in text viewer
}
Usage:
readme.txt → Opens in TextEditreadme.txt → Opens in built-in text viewer (with syntax highlighting)readme.txt → Opens in vimNote: Omitting the view action allows XeFM to use the built-in text viewer for text files, which provides syntax highlighting and is optimized for viewing code and text files.
If a file has no configured association:
TEXT_EDITOR config settingXeFM detects text by reading the bytes, not by looking at the extension.
There is deliberately no list of “text extensions” anywhere in XeFM: such a list
is wrong for files with no extension (Makefile, README), an unknown one, or
a misleading one — and inspecting the content gets all three right for free.
The built-in viewer decides like this:
utf-8, then latin-1, then cp1252[Binary file — cannot display as text] is
shownlatin-1 with replacement charactersThe rule of thumb across XeFM is detect capability from the bytes; configure preference by extension. Extensions decide which application you prefer — never whether a file is readable as text.
For the view action there are three cases, and two of them land in the same place:
view value |
Effect |
|---|---|
a command, e.g. ['less'] |
Launch that external viewer |
None |
Use the built-in viewer (text shown, binary → placeholder) |
| (no rule matches) | Same as None — the built-in viewer, via fallback |
So for view, None and “no rule at all” are equivalent. The distinction only
matters for open and edit, where None means “this action is
unavailable for this file type” and stops the fallback. Set 'view': None
explicitly only when you want to guarantee the built-in viewer even though a
later, more general entry might otherwise supply a command.
| Action | With association | No association — text | No association — binary |
|---|---|---|---|
| Enter | Built-in handler or configured open | Built-in viewer | Warns to use the open-externally key |
| V (View) | Configured viewer (or built-in if None) |
Built-in viewer | Built-in viewer shows placeholder |
| E (Edit) | Configured editor | TEXT_EDITOR config |
TEXT_EDITOR config |
Beyond the configurable actions above, XeFM has two fixed gestures that hand a file to the operating system.
Key: Cmd-Enter (macOS) / Ctrl-Enter (Linux/Windows) — the same key as
the open action.
This is the deliberate-open tier described under
Cmd/Ctrl-Enter: XeFM first looks for an
open command in your associations, and if there is none (and it is not
explicitly None) it falls back to the OS default application — open on
macOS, xdg-open on Linux, start on Windows. Selected files are opened; if
nothing is selected, the focused file is used. This handoff is not
configurable per file the way the open command is — it is whatever the OS
has registered for that type.
Key: Alt-Enter (macOS/Linux) / Ctrl-Shift-E (Windows).
Opens the OS file manager with the item selected:
open -RexplorerThis action always uses the focused item, not the selection. When a directory is focused it is revealed in its parent (shown as a selected item), not opened to show its contents — useful for jumping out to the OS to drag-and-drop or reach a native context menu.
To run one of your own configured tools instead of the OS default, open the external programs menu with X (see External Programs).
Group multiple image extensions and use the same program for opening and viewing:
{
'pattern': ['*.jpg', '*.jpeg', '*.png', '*.gif'],
'open|view': ['open', '-a', 'Preview'],
'edit': ['open', '-a', 'Photoshop']
}
{
'pattern': ['*.mp4', '*.mov'],
'open|view': ['open', '-a', 'QuickTime Player'],
'edit': ['open', '-a', 'Final Cut Pro']
},
{
'pattern': '*.avi',
'open|view': ['open', '-a', 'VLC'],
'edit': None # No editor configured
}
{
'pattern': '*.pdf',
'open|view': ['open', '-a', 'Preview'],
'edit': ['open', '-a', 'Adobe Acrobat']
}
{
'pattern': '*.txt',
'open': ['open', '-e'], # TextEdit on macOS
'edit': ['vim']
# 'view' omitted - uses built-in text viewer
},
{
'pattern': ['*.py', '*.js'],
'open': ['open', '-a', 'Visual Studio Code'],
'edit': ['vim']
# 'view' omitted - uses built-in text viewer with syntax highlighting
}
File associations use wildcard pattern matching:
*.pdf - matches all PDF files*.jpg - matches all JPG files*.tar.gz - matches compressed tar archivesPattern matching is case-insensitive, so *.PDF and *.pdf are treated the same.
You can configure different programs for different platforms:
import platform
FILE_ASSOCIATIONS = []
if platform.system() == 'Darwin': # macOS
FILE_ASSOCIATIONS.append({
'pattern': ['*.jpg', '*.png'],
'open|view': ['open', '-a', 'Preview'],
'edit': ['open', '-a', 'Photoshop']
})
elif platform.system() == 'Linux':
FILE_ASSOCIATIONS.append({
'pattern': ['*.jpg', '*.png'],
'open': ['xdg-open'],
'view': ['eog'], # Eye of GNOME
'edit': ['gimp']
})
XeFM comes with default file associations for common file types:
You can override any of these defaults in your configuration file.
Same program for multiple actions: It’s common to use the same program for both ‘open’ and ‘view’ actions, especially for media files.
Specialized editors: Use the ‘edit’ action for specialized editing software that’s different from your viewing application.
No action available: Set an action to None if you don’t want that action available for a file type.
Test your commands: Make sure the commands work from your terminal before adding them to the configuration.
Use absolute paths: If a program isn’t in your PATH, use the absolute path to the executable.
Specific patterns first: Always put more specific patterns before general ones in your configuration.
Document your intent: Add comments to explain why entries are ordered a certain way.
If XeFM can’t find the program:
If the wrong program opens:
If an action doesn’t appear:
None)If a file doesn’t match the expected pattern:
*.ext format)Run the test to verify everything works:
python3 test/test_file_associations.py
doc/dev/FILE_ASSOCIATIONS_IMPLEMENTATION.mddoc/XEFM_USER_GUIDE.md