Merge branch 'master' into force-cv

This commit is contained in:
Karl Voit 2026-02-11 21:25:43 +00:00 committed by GitHub
commit da1875a332
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 50 additions and 8 deletions

View file

@ -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

View file

@ -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]].

BIN
bin/dired-demo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

View file

@ -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)

View file

@ -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]

View file

@ -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):
"""