Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,27 @@ This repo provides a visual studio code extension for Nautilus.
wget -qO- https://raw.githubusercontent.com/harry-cpp/code-nautilus/master/install.sh | bash
```

During the install process you will be prompted to choose whether you want to register the stable `code` binary, `code-insiders`, or both inside Nautilus. Your choices are stored in `~/.config/code-nautilus/targets.conf`, so you can rerun the installer any time to update them.

## Local Installation (for development)

If you want to install from a local copy (for testing changes or contributing):

```bash
# Clone the repository
git clone https://github.com/harry-cpp/code-nautilus.git
cd code-nautilus

# Install using local files
bash install.sh --local
```

The `--local` (or `-l`) flag tells the installer to use your local `code-nautilus.py` file instead of downloading from GitHub. This is useful when:

- Testing local changes before contributing
- Developing new features
- Running the extension from a forked repository

## Uninstall Extension

```
Expand Down
177 changes: 151 additions & 26 deletions code-nautilus.py
Original file line number Diff line number Diff line change
@@ -1,62 +1,187 @@
# VSCode Nautilus Extension
#
# Place me in ~/.local/share/nautilus-python/extensions/,
# ensure you have python-nautilus package, restart Nautilus, and enjoy :)
# Adds "Open in Code" context menu items to Nautilus file manager.
# Supports both VS Code stable and Insiders versions.
#
# Installation:
# Place in ~/.local/share/nautilus-python/extensions/
# Ensure python-nautilus package is installed
# Restart Nautilus
#
# Configuration:
# Edit ~/.config/code-nautilus/targets.conf to enable/disable specific VS Code versions
#
# This script is released to the public domain.

from gi.repository import Nautilus, GObject
from subprocess import call
import os
import shutil

# Configuration file location
CONFIG_DIR = os.path.join(os.path.expanduser('~'), '.config', 'code-nautilus')
CONFIG_FILE = os.path.join(CONFIG_DIR, 'targets.conf')

# Available VS Code targets
# Format: 'config-key': ('Display Name', 'Environment Variable', 'Default Command')
TARGET_OPTIONS = {
'code': ('Code', 'VSCODE_BIN', 'code'),
'code-insiders': ('Code - Insiders', 'VSCODE_INSIDERS_BIN', 'code-insiders'),
}


def _register_editor(name, env_var, default_cmd, targets):
"""
Register a VS Code editor if it exists on the system.

Args:
name: Display name for the menu item (e.g., "Code", "Code - Insiders")
env_var: Environment variable to check for custom command path
default_cmd: Default command to use if env var is not set
targets: List to append the registered editor to
"""
cmd = os.environ.get(env_var)
cmd = cmd.strip() if cmd else default_cmd
if not cmd:
return
# Only register if the command is actually available on the system
if shutil.which(cmd):
targets.append((name, cmd))


def _load_configured_target_keys():
"""
Load which VS Code targets are enabled from the configuration file.

Reads ~/.config/code-nautilus/targets.conf and returns a list of
enabled target keys (e.g., ['code', 'code-insiders']).

# path to vscode
VSCODE = 'code'
Configuration format:
code=1
code-insiders=0

# what name do you want to see in the context menu?
VSCODENAME = 'Code'
Returns:
List of enabled target keys. Defaults to ['code'] if config is missing
or no targets are enabled.
"""
selected = []
if os.path.exists(CONFIG_FILE):
try:
with open(CONFIG_FILE, 'r') as config:
for raw_line in config:
line = raw_line.strip()
# Skip empty lines and comments
if not line or line.startswith('#') or '=' not in line:
continue
key, value = line.split('=', 1)
# Only process valid target keys
if key.strip() not in TARGET_OPTIONS:
continue
# Check if target is enabled (value is truthy)
if value.strip().lower() in ('1', 'true', 'yes', 'y'):
selected.append(key.strip())
except OSError:
pass

# always create new window?
# Default to stable VS Code if no configuration found
if not selected:
selected.append('code')
return selected


# Build list of available VS Code targets based on configuration and system availability
VSCODE_TARGETS = []
for target_key in _load_configured_target_keys():
option = TARGET_OPTIONS.get(target_key)
if option:
_register_editor(option[0], option[1], option[2], VSCODE_TARGETS)

# Fallback: if no configured targets are available, default to stable VS Code
if not VSCODE_TARGETS:
fallback = TARGET_OPTIONS['code']
VSCODE_TARGETS.append((fallback[0], fallback[2]))

# Set to True to always open files in a new VS Code window
# When False, files/folders will open in existing window unless a folder is opened
NEWWINDOW = False


class VSCodeExtension(GObject.GObject, Nautilus.MenuProvider):
"""
Nautilus extension that adds VS Code context menu items.

Provides two types of menu items:
1. File items: When right-clicking on files/folders
2. Background items: When right-clicking on empty space in a directory
"""

def launch_vscode(self, menu, files):
def launch_vscode(self, menu, data):
"""
Launch VS Code with the selected files/folders.

Args:
menu: The menu item that was clicked (unused)
data: Tuple of (files, executable) where:
- files: List of Nautilus file objects
- executable: Path to VS Code executable
"""
files, executable = data
safepaths = ''
args = ''

for file in files:
filepath = file.get_location().get_path()
# Quote paths to handle spaces and special characters
safepaths += '"' + filepath + '" '

# If one of the files we are trying to open is a folder
# create a new instance of vscode
# If opening a folder, always create a new VS Code window
# This prevents folders from opening as workspace additions
if os.path.isdir(filepath) and os.path.exists(filepath):
args = '--new-window '

# Force new window if NEWWINDOW is enabled
if NEWWINDOW:
args = '--new-window '

call(VSCODE + ' ' + args + safepaths + '&', shell=True)
# Execute VS Code in background
call(executable + ' ' + args + safepaths + '&', shell=True)

def get_file_items(self, *args):
"""
Create context menu items when right-clicking on files/folders.

Returns:
List of Nautilus.MenuItem objects, one for each enabled VS Code variant
"""
files = args[-1]
item = Nautilus.MenuItem(
name='VSCodeOpen',
label='Open in ' + VSCODENAME,
tip='Opens the selected files with VSCode'
)
item.connect('activate', self.launch_vscode, files)
items = []
for idx, (name, executable) in enumerate(VSCODE_TARGETS):
item = Nautilus.MenuItem(
name='VSCodeOpen{0}'.format(idx),
label='Open in ' + name,
tip='Opens the selected files with VSCode'
)
item.connect('activate', self.launch_vscode, (files, executable))
items.append(item)

return [item]
return items

def get_background_items(self, *args):
"""
Create context menu items when right-clicking on empty space in a directory.

Returns:
List of Nautilus.MenuItem objects for opening the current directory
"""
file_ = args[-1]
item = Nautilus.MenuItem(
name='VSCodeOpenBackground',
label='Open in ' + VSCODENAME,
tip='Opens the current directory in VSCode'
)
item.connect('activate', self.launch_vscode, [file_])

return [item]
items = []
for idx, (name, executable) in enumerate(VSCODE_TARGETS):
item = Nautilus.MenuItem(
name='VSCodeOpenBackground{0}'.format(idx),
label='Open in ' + name,
tip='Opens the current directory in VSCode'
)
item.connect('activate', self.launch_vscode, ([file_], executable))
items.append(item)

return items
114 changes: 104 additions & 10 deletions install.sh
Original file line number Diff line number Diff line change
@@ -1,28 +1,111 @@
#!/bin/bash

# Install python-nautilus
# Check if running in local test mode
# When --local or -l flag is provided, the script will copy the local code-nautilus.py
# instead of downloading from GitHub. This is useful for testing local changes.
LOCAL_MODE=false
if [ "$1" = "--local" ] || [ "$1" = "-l" ]; then
LOCAL_MODE=true
echo "Local test mode enabled"
fi

# Helper function to ask yes/no questions with default answer
# Args:
# $1 - The prompt message to display
# $2 - The default answer (Y or N)
# Returns:
# 0 for yes, 1 for no
ask_yes_no() {
local prompt="$1"
local default_answer="$2"
local reply
while true
do
read -r -p "$prompt" reply
if [ -z "$reply" ]
then
reply="$default_answer"
fi
case "$reply" in
[yY]) return 0 ;;
[nN]) return 1 ;;
*) echo "Please enter y or n." ;;
esac
done
}

# Configure which VS Code versions to register in Nautilus context menu
# Creates a configuration file that the Python extension reads to determine
# which VS Code variants should appear in the right-click menu.
# Configuration is saved to ~/.config/code-nautilus/targets.conf
configure_targets() {
local config_dir="$HOME/.config/code-nautilus"
local config_file="$config_dir/targets.conf"
mkdir -p "$config_dir"

echo "Select VS Code version(s) to register in Nautilus:"
local register_code
local register_insiders

# Ask if user wants to register stable VS Code
if ask_yes_no " - Register stable VS Code (code)? [Y/n] " "Y"
then
register_code=1
else
register_code=0
fi

# Ask if user wants to register VS Code Insiders
if ask_yes_no " - Register VS Code Insiders (code-insiders)? [y/N] " "N"
then
register_insiders=1
else
register_insiders=0
fi

# If neither version is selected, default to stable version
if [ "$register_code" -eq 0 ] && [ "$register_insiders" -eq 0 ]
then
echo "No version selected. Defaulting to stable VS Code."
register_code=1
fi

# Write configuration file
# Format: key=value where value is 1 (enabled) or 0 (disabled)
cat > "$config_file" <<EOF
# VS Code Nautilus Extension Configuration
# Set to 1 to enable, 0 to disable
code=$register_code
code-insiders=$register_insiders
EOF

echo "Configuration saved to $config_file"
}

# Install python-nautilus dependency
# This package is required for Nautilus to load Python extensions
echo "Installing python-nautilus..."
if type "pacman" > /dev/null 2>&1
then
# check if already install, else install
pacman -Qi python-nautilus &> /dev/null
if [ `echo $?` -eq 1 ]
# Arch Linux / Manjaro
if ! pacman -Qi python-nautilus &> /dev/null
then
sudo pacman -S --noconfirm python-nautilus
else
echo "python-nautilus is already installed"
fi
elif type "apt-get" > /dev/null 2>&1
then
# Find Ubuntu python-nautilus package
# Debian / Ubuntu
# Package name varies by Ubuntu version (python-nautilus vs python3-nautilus)
package_name="python-nautilus"
found_package=$(apt-cache search --names-only $package_name)
if [ -z "$found_package" ]
then
package_name="python3-nautilus"
fi

# Check if the package needs to be installed and install it
# Check if the package is already installed
installed=$(apt list --installed $package_name -qq 2> /dev/null)
if [ -z "$installed" ]
then
Expand All @@ -32,6 +115,7 @@ then
fi
elif type "dnf" > /dev/null 2>&1
then
# Fedora / RHEL
installed=`dnf list --installed nautilus-python 2> /dev/null`
if [ -z "$installed" ]
then
Expand All @@ -43,17 +127,27 @@ else
echo "Failed to find python-nautilus, please install it manually."
fi

# Remove previous version and setup folder
# Remove previous versions and ensure extension directory exists
# VSCodeExtension.py is the old filename, code-nautilus.py is the new one
echo "Removing previous version (if found)..."
mkdir -p ~/.local/share/nautilus-python/extensions
rm -f ~/.local/share/nautilus-python/extensions/VSCodeExtension.py
rm -f ~/.local/share/nautilus-python/extensions/code-nautilus.py

# Download and install the extension
echo "Downloading newest version..."
wget -q -O ~/.local/share/nautilus-python/extensions/code-nautilus.py https://raw.githubusercontent.com/harry-cpp/code-nautilus/master/code-nautilus.py
# In local mode, copy from current directory; otherwise download from GitHub
if [ "$LOCAL_MODE" = true ]; then
echo "Using local version for testing..."
cp "$(dirname "$0")/code-nautilus.py" ~/.local/share/nautilus-python/extensions/code-nautilus.py
else
echo "Downloading newest version..."
wget -q -O ~/.local/share/nautilus-python/extensions/code-nautilus.py https://raw.githubusercontent.com/harry-cpp/code-nautilus/master/code-nautilus.py
fi

# Prompt user to configure which VS Code versions to register
configure_targets

# Restart nautilus
# Restart Nautilus to load the new extension
echo "Restarting nautilus..."
nautilus -q

Expand Down