diff --git a/Integration.org b/Integration.org index 3bcb40b..003080b 100644 --- a/Integration.org +++ b/Integration.org @@ -413,7 +413,8 @@ If you want to assign a different keyboard shortcut than =Ctrl-1= like :END: [[https://en.wikipedia.org/wiki/Dired][Dired]] is the oldest file manager. It started in 1974 and offers a wide -range of efficient functionality. Here, we add filetags to Dired. +range of efficient functionality. Here, we add filetags to Dired using the +xfce4-terminal on Linux. #+BEGIN_SRC emacs-lisp (defun my-dired-filetags () @@ -432,8 +433,26 @@ range of efficient functionality. Here, we add filetags to Dired. ) #+END_SRC -I mapped it to =M-t= in my dired buffers: +An alternative implementation using the kitty terminal: +#+begin_src emacs-lisp +(defun my-dired-filetags () + "Run \"filetags\" on current or marked files" + (interactive) + (dired-do-shell-command + "kitty -o remember_window_size=no -o initial_window_width=60c -o initial_window_height=15c --position 535x300 -o window_padding_width='0 20 0 20' --title floating -- filetags -q --interactive *" + nil + (dired-get-marked-files)) + (revert-buffer nil t t)) +#+end_src +It can look like this: +[[file:bin/dired-demo.png]] +You can map it in your dired buffers: #+BEGIN_SRC emacs-lisp (define-key dired-mode-map (kbd "M-t") 'my-dired-filetags) +;; or +(use-package dired + :ensure nil + :bind (:map dired-mode-map + ("t" . my-dired-filetags))) #+END_SRC diff --git a/README.org b/README.org index f0eebc5..1cb1a85 100644 --- a/README.org +++ b/README.org @@ -76,6 +76,8 @@ This tool needs [[http://www.python.org/downloads/][Python 3 to be installed]]. You can install filetags either via [[https://packaging.python.org/tutorials/installing-packages/][pip]] or [[https://docs.astral.sh/uv/][uv]]. +On Arch Linux you can use the [[https://aur.archlinux.org/packages/filetags-git][AUR package]]: `yay -S filetags-git` + Or you can install filetags using the source code, e.g., by cloning the [[https://github.com/novoid/filetags/][GitHub repository of filetags]]. diff --git a/bin/dired-demo.png b/bin/dired-demo.png new file mode 100644 index 0000000..229b3de Binary files /dev/null and b/bin/dired-demo.png differ diff --git a/filetags/__init__.py b/filetags/__init__.py index f5b82b5..4585d3e 100755 --- a/filetags/__init__.py +++ b/filetags/__init__.py @@ -52,7 +52,6 @@ from tkinter import ttk ## for --gui safe_import('operator') # for sorting dicts safe_import('difflib') # for good enough matching words safe_import('readline') # for raw_input() reading from stdin -safe_import('codecs') # for handling Unicode content in .tagfiles safe_import('math') # (integer) calculations safe_import('clint') # for config file handling safe_import('itertools') # for calculating permutations of tagtrees @@ -2086,7 +2085,7 @@ def parse_controlled_vocabulary(filename): included_files.append(os.path.realpath(filename)) tags = [] - with codecs.open(filename, encoding='utf-8') as filehandle: + with open(filename, encoding='utf-8') as filehandle: logging.debug('parse_controlled_vocabulary: reading controlled vocabulary in [%s]' % filename) global controlled_vocabulary_filename @@ -2353,12 +2352,18 @@ def ask_for_tags_text_version(vocabulary, upto9_tags_for_shortcuts, hint_str, ta print_tag_shortcut_with_numbers(hint_str, tag_list) logging.debug("interactive mode: asking for tags ...") + if prompt_prefill: def _prefill(): readline.insert_text(prompt_prefill) readline.set_startup_hook(_prefill) - entered_tags = input(colorama.Style.DIM + 'Tags: ' + colorama.Style.RESET_ALL).strip() + try: + entered_tags = input(colorama.Style.DIM + 'Tags: ' + colorama.Style.RESET_ALL).strip() + except EOFError: + logging.info("Received EOF") + sys.exit(0) readline.set_startup_hook() + return extract_tags_from_argument(entered_tags) diff --git a/pyproject.toml b/pyproject.toml index aadd5c4..b9ebcf2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,6 @@ classifiers=[ "Development Status :: 5 - Production/Stable", "Environment :: Console", "Intended Audience :: End Users/Desktop", - "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", @@ -36,7 +35,8 @@ classifiers=[ ] authors = [{name="Karl Voit", email="tools@Karl-Voit.at"}] -license = {file = "LICENSE.txt"} +license = "GPL-3.0-or-later" +license-files = ["LICENSE.txt"] requires-python = ">=3.10" # see https://devguide.python.org/versions/ dependencies = [ "clint>=0.5.1", @@ -54,7 +54,7 @@ Issues = "https://github.com/novoid/filetags/issues" filetags="filetags:main" [build-system] -requires = ["setuptools>=61.0"] +requires = ["setuptools>=77.0"] build-backend = "setuptools.build_meta" [tool.setuptools.package-data] diff --git a/tests/unit_tests.py b/tests/unit_tests.py index c39d802..20f5308 100755 --- a/tests/unit_tests.py +++ b/tests/unit_tests.py @@ -473,6 +473,22 @@ class TestLocateAndParseControlledVocabulary(unittest.TestCase): cv = filetags.locate_and_parse_controlled_vocabulary(cv_file) self.assertEqual(set(cv), set(["foo", "bar", "baz", "tag"])) + def test_unicode_symbols_in_cv(self): + """ + Ensure unicode symbols and non-ASCII characters in controlled vocabulary + files are parsed correctly. + """ + tempdir = tempfile.mkdtemp(prefix='TestControlledVocabulary_Unicode_') + print("\ntempdir: " + tempdir + ' <<<' + '#' * 10) + assert(os.path.isdir(tempdir)) + + cv_file = os.path.join(tempdir, '.filetags') + self.create_file(cv_file, "café\nnaïve\npi_π\nstar_★\nsnow_雪\n") + assert(os.path.isfile(cv_file)) + + cv = filetags.locate_and_parse_controlled_vocabulary(cv_file) + self.assertEqual(set(cv), set(["café", "naïve", "pi_π", "star_★", "snow_雪"])) + def test_include_lines_in_cv_not_circular(self): """