Skip to content

Commit

Permalink
Add commands to open plugins' settings and hotkeys
Browse files Browse the repository at this point in the history
(Also, fix an issue with the "Core plugins" tab not rendering properly.)
  • Loading branch information
pjeby committed Jul 28, 2022
1 parent 9797cf2 commit 3834916
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 24 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

> New in...
>
> * 0.3.13: Commands to let you open hotkeys or settings by typing the plugin name
> * 0.3.12: Jump to edit a hotkey with Ctrl- or Cmd-Enter from the command palette!
> * 0.3.8: Hit enter while typing in the community plugin search box to open the plugin catalog with that search
> * 0.3.5: Plugin searches are saved and carry through when browsing the plugin catalog
Expand Down
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "hotkey-helper",
"name": "Hotkey Helper",
"version": "0.3.12",
"version": "0.3.13",
"minAppVersion": "0.15.9",
"description": "Easily see and access any plugin's settings or hotkey assignments (and conflicts) from the Community Plugins tab",
"author": "PJ Eby",
Expand Down
8 changes: 4 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

86 changes: 69 additions & 17 deletions src/hotkey-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
ExtraButtonComponent, Hotkey, Command, SearchComponent
} from "obsidian";
import {around, serialize} from "monkey-around";
import {defer, onElement} from "@ophidian/core";
import {defer, modalSelect, onElement} from "@ophidian/core";
import "./obsidian-internals";

function hotkeyToString(hotkey: Hotkey) {
Expand Down Expand Up @@ -204,6 +204,52 @@ export default class HotkeyHelper extends Plugin {
name: "Browse or search the Community Plugins catalog",
callback: () => this.gotoPlugin()
})
const alphaSort = new Intl.Collator(undefined, {usage: "sort", sensitivity: "base", numeric: true}).compare;
this.addCommand({
id: "open-settings",
name: "Open settings for plugin...",
callback: async () => {
const {item} = await modalSelect(
app.setting.pluginTabs.concat(app.setting.settingTabs).sort((a, b) => alphaSort(a.name, b.name)),
t => t.name,
"Select a plugin to open its settings...",
);
if (item) {
app.setting.open()
app.setting.openTabById(item.id);
}
}
});
this.addCommand({
id: "open-hotkeys",
name: "Open hotkeys for plugin...",
callback: async () => {
const commandsByPlugin = this.refreshCommands();
const plugins = Object.values(app.plugins.plugins)
.map(p => p.manifest as Partial<PluginManifest>)
.concat(
Object.entries(app.internalPlugins.plugins)
.map(
([id, {instance: {name}, _loaded:enabled}]) => {return {id, name, enabled};}
)
.filter(p => p.enabled)
)
.concat([
{id: "app", name: "App"},
{id: "editor", name: this.getSettingsTab("editor")?.name || "Editor"},
{id: "workspace", name: this.getSettingsTab("file")?.name || "Files & Links"}
])
.filter(m => commandsByPlugin[m.id]?.length);
;
const {item} = await modalSelect(
plugins.sort((a, b) => alphaSort(a.name, b.name)),
t => t.name,
"Select a plugin to open its hotkeys...");
if (item) {
this.showHotkeysFor(item.id+":");
}
}
});
}

createExtraButtons(setting: Setting, manifest: {id: string, name: string}, enabled: boolean) {
Expand Down Expand Up @@ -390,12 +436,12 @@ export default class HotkeyHelper extends Plugin {
let manifests: {id: string, name: string, enabled?: boolean}[];
if (tabId === "plugins") {
manifests = Object.entries(app.internalPlugins.plugins).map(
([id, {instance: {name}, _loaded:enabled}]) => {return {id, name, enabled};}
);
([id, {instance: {name, hiddenFromList}, _loaded:enabled}]) => {return !hiddenFromList && {id, name, enabled};}
).filter(m => m);
} else {
manifests = Object.values(app.plugins.manifests);
manifests.sort((e, t) => e.name.localeCompare(t.name));
}
manifests.sort((e, t) => e.name.localeCompare(t.name));
let which = 0;

// Trap the addition of the "uninstall" buttons next to each plugin
Expand Down Expand Up @@ -487,24 +533,30 @@ export default class HotkeyHelper extends Plugin {
return app.internalPlugins.plugins[id]?._loaded || app.plugins.plugins[id];
}

refreshButtons(force=false) {
// Don't refresh when not displaying, unless rendering is in progress
if (!pluginSettingsAreOpen() && !force) return;
commandsByPlugin = {} as Record<string, {hotkeys: string[], cmd: Command}[]>;
assignedKeyCount = {} as Record<string, number>;

refreshCommands() {
const hkm = app.hotkeyManager;
const assignedKeyCount = {} as Record<string, number>;

// Get a list of commands by plugin
const commands = Object.values(this.app.commands.commands).reduce((cmds, cmd)=>{
this.assignedKeyCount = {};
return this.commandsByPlugin = Object.values(app.commands.commands).reduce((cmds, cmd)=>{
const pid = cmd.id.split(":",2).shift();
const hotkeys = (hkm.getHotkeys(cmd.id) || hkm.getDefaultHotkeys(cmd.id) || []).map(hotkeyToString);
hotkeys.forEach(k => assignedKeyCount[k] = 1 + (assignedKeyCount[k]||0));
hotkeys.forEach(k => this.assignedKeyCount[k] = 1 + (this.assignedKeyCount[k]||0));
(cmds[pid] || (cmds[pid]=[])).push({hotkeys, cmd});
return cmds;
}, {} as Record<string, {hotkeys: string[], cmd: Command}[]>);
}

refreshButtons(force=false) {
// Don't refresh when not displaying, unless rendering is in progress
if (!pluginSettingsAreOpen() && !force) return;

// Get a list of commands by plugin
this.refreshCommands();

// Plugin setting tabs by plugin
const tabs = Object.values(this.app.setting.pluginTabs).reduce((tabs, tab)=> {
const tabs = Object.values(app.setting.pluginTabs).reduce((tabs, tab)=> {
tabs[tab.id] = tab; return tabs
}, {} as Record<string, SettingTab|boolean>);
tabs["workspace"] = tabs["editor"] = true;
Expand All @@ -520,16 +572,16 @@ export default class HotkeyHelper extends Plugin {

for(const id of Object.keys(this.hotkeyButtons || {})) {
const btn = this.hotkeyButtons[id];
if (!commands[id]) {
if (!this.commandsByPlugin[id]) {
// Plugin is disabled or has no commands
btn.extraSettingsEl.hide();
continue;
}
const assigned = commands[id].filter(info => info.hotkeys.length);
const conflicts = assigned.filter(info => info.hotkeys.filter(k => assignedKeyCount[k]>1).length).length;
const assigned = this.commandsByPlugin[id].filter(info => info.hotkeys.length);
const conflicts = assigned.filter(info => info.hotkeys.filter(k => this.assignedKeyCount[k]>1).length).length;

btn.setTooltip(
`Configure hotkeys${"\n"}(${assigned.length}/${commands[id].length} assigned${
`Configure hotkeys${"\n"}(${assigned.length}/${this.commandsByPlugin[id].length} assigned${
conflicts ? "; "+conflicts+" conflicting" : ""
})`
);
Expand Down
4 changes: 3 additions & 1 deletion src/obsidian-internals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ declare module "obsidian" {

interface SettingTab {
id: string
name: string
searchInputEl?: HTMLInputElement; // XXX should be subtypes for hotkey and plugin tabs
updateHotkeyVisibility?(): void;
}
Expand All @@ -59,7 +60,7 @@ declare module "obsidian" {

interface Plugins {
manifests: Record<string, PluginManifest>;
plugins: Record<string, Plugin>;
plugins: Record<string, Plugin_2>;

enablePlugin(pluginId: string): Promise<boolean>;
disblePlugin(pluginId: string): Promise<void>;
Expand All @@ -82,6 +83,7 @@ declare module "obsidian" {

type InternalPluginInstance<T> = T & {
name: string
hiddenFromList: boolean
}

type ViewFactory = (leaf: WorkspaceLeaf) => View
Expand Down
2 changes: 1 addition & 1 deletion versions.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"0.3.12": "0.15.9",
"0.3.13": "0.15.9",
"0.3.11": "0.13.19",
"0.3.8": "0.12.3",
"0.3.0": "0.12.1",
Expand Down

0 comments on commit 3834916

Please sign in to comment.