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

Auto-reload filetypes when filetypes.toml is saved #1177

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
23 changes: 20 additions & 3 deletions porcupine/plugins/filetypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,6 @@ def load_filetypes() -> None:
assert "filetype_name" not in filetype
filetype["filetype_name"] = name


def set_filedialog_kwargs() -> None:
filedialog_kwargs["filetypes"] = [
(
name,
Expand All @@ -126,6 +124,7 @@ def get_filetype_from_matches(
if not matches:
return None
if len(matches) >= 2:
# Last match, because it's more likely from the user's configuration rather than default.
names = ", ".join(matches.keys())
log.warning(
f"{len(matches)} file types match {they_match_what}: {names}. The last match will be"
Expand Down Expand Up @@ -214,6 +213,8 @@ def apply_filetype_to_tab(filetype: FileType, tab: tabs.FileTab) -> None:

with tab.settings.defer_change_events():
# Reset all options whose values came from the previous filetype.
# This is needed because previous filetype could have set some option that
# the new filetype does not set.
for name in tab.settings.get_options_by_tag("from_filetype"):
tab.settings.reset(name)

Expand All @@ -228,10 +229,27 @@ def on_path_changed(tab: tabs.FileTab, junk: object = None) -> None:
apply_filetype_to_tab(get_filetype_for_tab(tab), tab)


def after_save(tab: tabs.FileTab, junk: object) -> None:
if tab.path == Path(dirs.user_config_dir) / "filetypes.toml":
# Reload all filetypes when the user saves filetypes.toml.
# If user has chosen a custom filetype, leave the tab alone.
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just find a filetype with whatever filetype_name is currently used? It still needs a fallback to get_filetype_for_tab() in case the user deletes a filetype.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the user hasn't selected a custom filetype, we should call get_filetype_for_tab(), because it is possible that filename_patterns was modified.

tabs_with_default_filetype = [
tab
for tab in get_tab_manager().tabs()
if isinstance(tab, tabs.FileTab)
and tab.settings.get("filetype_name", str) == get_filetype_for_tab(tab)["filetype_name"]
]
filetypes.clear()
load_filetypes()
for tab in tabs_with_default_filetype:
apply_filetype_to_tab(get_filetype_for_tab(tab), tab)


def on_new_filetab(tab: tabs.FileTab) -> None:
tab.settings.add_option("filetype_name", None, type_=Optional[str])
on_path_changed(tab)
tab.bind("<<PathChanged>>", partial(on_path_changed, tab), add=True)
tab.bind("<<AfterSave>>", partial(after_save, tab), add=True)


def setup_argument_parser(parser: argparse.ArgumentParser) -> None:
Expand Down Expand Up @@ -263,7 +281,6 @@ def setup() -> None:
"Default filetype for new files:",
values=sorted(filetypes.keys(), key=str.casefold),
)
set_filedialog_kwargs()

for name in sorted(filetypes.keys(), key=str.casefold):
escaped_name = name.replace("/", "//") # doesn't work in all corner cases
Expand Down
42 changes: 39 additions & 3 deletions tests/test_filetypes_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
from tkinter import filedialog

import pytest
from pygments.lexer import LexerMeta
from pygments.lexers import JsonLexer, RustLexer, TextLexer

from porcupine import dirs, filedialog_kwargs, get_main_window
from porcupine import dirs, filedialog_kwargs, get_main_window, menubar
from porcupine.plugins import filetypes


Expand All @@ -34,13 +36,11 @@ def custom_filetypes():
)
filetypes.filetypes.clear()
filetypes.load_filetypes()
filetypes.set_filedialog_kwargs()

yield
user_filetypes.unlink()
filetypes.filetypes.clear()
filetypes.load_filetypes()
filetypes.set_filedialog_kwargs()


def test_filedialog_patterns_got_stripped():
Expand Down Expand Up @@ -125,6 +125,42 @@ def test_settings_reset_when_filetype_changes(filetab, tmp_path):
assert len(filetab.settings.get("example_commands", object)) == 0


@pytest.fixture
def filetypes_toml_filetab(tabmanager):
tab = tabmanager.open_file(Path(dirs.user_config_dir) / "filetypes.toml")
yield tab
tabmanager.close_tab(tab)


def test_saving_filetypes_toml_reloads_filetypes(
filetab, filetypes_toml_filetab, tabmanager, tmp_path
):
filetypes_toml_filetab.textwidget.insert(
"end",
"[Foo]\nfilename_patterns = ['*.foo']\npygments_lexer = 'pygments.lexers.RustLexer'\n",
)
filetypes_toml_filetab.textwidget.edit_separator()
filetab.save_as(tmp_path / "asd.foo")
assert filetab.settings.get("pygments_lexer", LexerMeta) is TextLexer # default

filetypes_toml_filetab.save()
tabmanager.update()
assert filetab.settings.get("pygments_lexer", LexerMeta) is RustLexer

filetypes_toml_filetab.textwidget.edit_undo()
filetypes_toml_filetab.save()
tabmanager.update()
assert filetab.settings.get("pygments_lexer", LexerMeta) is TextLexer

# If user sets custom filetype, respect that
tabmanager.select(filetab)
menubar.get_menu("Filetypes").invoke("JSON")
filetypes_toml_filetab.textwidget.edit_redo()
filetypes_toml_filetab.save()
tabmanager.update()
assert filetab.settings.get("pygments_lexer", LexerMeta) is JsonLexer


def test_merging_settings():
default = {
"Plain Text": {"filename_patterns": ["*.txt"]},
Expand Down