Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Symlink support; Loockup into the PATH; Ability to hide the terminal name #3

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
26 changes: 26 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# EditorConfig is awesome: http://EditorConfig.org

# top-most EditorConfig file
root = true

# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8

# 4 space indentation
[*.{py,java,r,R}]
indent_style = space
indent_size = 4

# 2 space indentation
[*.{js,json,y{a,}ml,html,cwl}]
indent_style = space
indent_size = 2

[*.{md,Rmd,rst}]
trim_trailing_whitespace = false
indent_style = space
indent_size = 2
156 changes: 106 additions & 50 deletions src/nautiterm/open_terminal.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -12,95 +12,151 @@
# URI
# - Assume Python 3.x

"""Nautilus extension for opening a terminal window at the given/current location."""

import os
import os.path
import subprocess
import sys
import shutil
import yaml # for loading configuration
import gi
from gi.repository import Nautilus, GObject

gi.require_version('Nautilus', '3.0')
from gi.repository import Nautilus, GObject, Gio

PYTHON_MIN_MAJOR_VERSION = 3

if sys.version_info[0] < PYTHON_MIN_MAJOR_VERSION:
raise RuntimeError('Nautiterm requires Python version 3.x or greater')

CONFIG_FILE_NAME = 'nautiterm.yml'
CONFIG_FILE_DIR = os.environ.get('XDG_CONFIG_HOME',
os.path.join(os.environ['HOME'], '.config'))
CONFIG_FILE_DIR = os.environ.get(
'XDG_CONFIG_HOME', os.path.join(
os.environ['HOME'], '.config'))
CONFIG_FILE_PATH = os.path.join(CONFIG_FILE_DIR, CONFIG_FILE_NAME)
DEFAULT_TERMINAL_EXEC = 'gnome-terminal'

print("Starting Nautiterm")


class OpenTerminalExtension(Nautilus.MenuProvider, GObject.GObject):
class Configuration:
"""Configuration of module"""

def __init__(self):
pass

def _open_terminal(self, file):
gvfs = Gio.Vfs.get_default()
open_path = gvfs.get_file_for_uri(file.get_uri()).get_path()

exc = self._get_terminal_exec()
if 'gnome-terminal' in exc or 'terminator' in exc:
subprocess.Popen([self._get_terminal_exec(), '--working-directory={p}'.format(p=open_path)])
else:
os.chdir(open_path)
subprocess.Popen([exc])

def _get_terminal_exec(self):
"""
Returns the executable name of a terminal emulator to launch based on user
configuration, or gnome-terminal if nothing else has been specified.
"""

terminal = None

try:
with open(CONFIG_FILE_PATH) as conffile:
with open(CONFIG_FILE_PATH, encoding="utf-8") as conffile:
config = yaml.load(conffile, yaml.SafeLoader)
terminal = config.get('terminal', None)
self._display_name = config.get('display-name', True)
except yaml.YAMLError:
print("Nautiterm: invalid configuration file at {path}, falling back" +
" to {d}".format(path=CONFIG_FILE_PATH, d=DEFAULT_TERMINAL_EXEC),
file=sys.stderr)
except IOError as ioe:
print(
f'Nautiterm: invalid configuration file at {CONFIG_FILE_PATH}, ' +
f'falling back to {DEFAULT_TERMINAL_EXEC}',
file=sys.stderr)
except IOError:
# catch-all for permission errors and file not founds to be compatible
# with Python 2 which doesn't have FileNotFoundError or PermissionError
# with Python 2 which doesn't have FileNotFoundError or
# PermissionError
pass
ASolomatin marked this conversation as resolved.
Show resolved Hide resolved

if not terminal:
terminal = DEFAULT_TERMINAL_EXEC

return terminal
self._terminal = terminal

def get_terminal(self):
"""
Returns the executable name of a terminal emulator to launch based on user
configuration, or gnome-terminal if nothing else has been specified.
"""
return self._terminal

def is_display_name(self):
"""Returns flag that indicates is terminal name must be displayed into the context menu."""
return self._display_name


class Terminal:
"""Terminal logic"""

def __init__(self, configuration):
self._configuration = configuration
path = shutil.which(configuration.get_terminal())

def menu_activate_cb(self, menu, file):
self._open_terminal(file)
if not path:
raise RuntimeError(
f'Nautiterm: Unable to find configured terminal: {configuration.get_terminal()}')

def menu_background_activate_cb(self, menu, file):
self._open_terminal(file)
while os.path.islink(path):
path = shutil.which(os.readlink(path))

self._path = path
self._name = os.path.basename(path)

def open(self, file):
"""Launches the terminal"""
open_path = file.get_location().get_path()

if 'gnome-terminal' in self._name or 'terminator' in self._name:
subprocess.run(
[self._path, f'--working-directory={open_path}'], check=False)
else:
os.chdir(open_path)
subprocess.run([self._path], check=False)

def get_name(self):
'''Returns terminal executable name without full path'''
return self._name


class OpenTerminalExtension(Nautilus.MenuProvider, GObject.GObject):
"""Class implements Mautiterm module """

def __init__(self):
self._configuration = Configuration()
self._terminal = Terminal(self._configuration)

def _menu_activate_cb(self, _, file):
self._terminal.open(file)

def _menu_background_activate_cb(self, _, file):
self._terminal.open(file)

def get_file_items(self, _, files): # pylint: disable=arguments-differ
'''Adds menu item for files and directories'''

def get_file_items(self, window, files):
if len(files) != 1:
return
return None

file = files[0]
if not file.is_directory() or file.get_uri_scheme() != 'file':
return

item = Nautilus.MenuItem(name='NautilusPython::openterminal_file_item',
label='Open Terminal (%s)' % self._get_terminal_exec(),
tip='Open Terminal In %s' % file.get_name())
item.connect('activate', self.menu_activate_cb, file)
return item,

def get_background_items(self, window, file):
item = Nautilus.MenuItem(name='NautilusPython::openterminal_file_item2',
label='Open Terminal (%s)' % self._get_terminal_exec(),
tip='Open Terminal In %s' % file.get_name())
item.connect('activate', self.menu_background_activate_cb, file)
return item,
return None

label = 'Open Terminal'
if self._configuration.is_display_name():
label += f' ({self._terminal.get_name()})'

item = Nautilus.MenuItem(
name='NautilusPython::openterminal_file_item',
label=label,
tip=f'Open Terminal In {file.get_name()}')
item.connect('activate', self._menu_activate_cb, file)
return (item,)

def get_background_items(self, _, file): # pylint: disable=arguments-differ
'''Adds menu item for current directory'''

label = 'Open Terminal'
if self._configuration.is_display_name():
label += f' ({self._terminal.get_name()})'

item = Nautilus.MenuItem(
name='NautilusPython::openterminal_file_item2',
label=label,
tip=f'Open Terminal In {file.get_name()}')
item.connect('activate', self._menu_background_activate_cb, file)

return (item,)