Skip to content

Commit

Permalink
Add a Move file to another folder command (closed #8)
Browse files Browse the repository at this point in the history
  • Loading branch information
tadashi-aikawa committed Nov 3, 2021
1 parent 4b8d058 commit 9a4552a
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 11 deletions.
26 changes: 17 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@

This is an Obsidian plugin which is another choice of Quick switcher.

- `Another Quick Switcher` can search **regardless of the appearance order of tokens**
- `Another Quick Switcher` shows suggestions order by prioritizing both last opened time and modified time **even after typing** (Recent search)
- `Another Quick Switcher` can search backlinks and move them **without leaving from a keyboard** (Backlink search)
- `Another Quick Switcher` can search to **consider prefix emoji**.
- `Another Quick Switcher` **only searches Markdown files**.
- `Another Quick Switcher` does not search very fuzzy
- `Another Quick Switcher` shows file names and directory names separately
- It can search **regardless of the appearance order of tokens**
- It shows suggestions order by prioritizing both last opened time and modified time **even after typing** (`Recent search`)
- It can search backlinks and move them **without leaving from a keyboard** (`Backlink search`)
- It can move a file to another folder (`Move file to another folder`)
- It can search to **consider prefix emoji**
- It **only searches Markdown files** except for the case of `Move file to another folder`
- It does not search very fuzzy
- It shows file names and directory names separately

At the moment, there are only a few options. However, if you would like to customize behavior, I will add options to make it better as well as I can :)

Expand Down Expand Up @@ -48,6 +49,15 @@ One of the following.

![Demo](https://raw.githubusercontent.com/tadashi-aikawa/obsidian-another-quick-switcher/master/demo/backlink.gif)

### Move file to another folder

One of the following.

- Run `Another Quick Switcher: Move file to another folder` on `Command palette`
- Push `Ctrl/Cmd + Shift + M` in the default case

![Demo](https://raw.githubusercontent.com/tadashi-aikawa/obsidian-another-quick-switcher/master/demo/move-to-folder.gif)

## 📱 Mobile support

It both supports desktop and mobile.
Expand All @@ -62,8 +72,6 @@ You can download from `Community plugins` in Obsidian settings.

## 🛣 Roadmap

- [x] Add support for mobile (#2)
- [x] Fix Layout issues
- [ ] Switch between ignore profiles (#3)

## 🖥️ For developers
Expand Down
Binary file added demo/move-to-folder.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 14 additions & 1 deletion src/app-helper.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
import { App, getLinkpath, LinkCache, MarkdownView, TFile } from "obsidian";
import {
App,
getLinkpath,
LinkCache,
MarkdownView,
TFile,
TFolder,
} from "obsidian";
import { flatten, uniq } from "./utils/collection-helper";
import { basename, dirname, extname } from "./utils/path";

export class AppHelper {
constructor(private app: App) {}

getFolders(): TFolder[] {
return this.app.vault
.getAllLoadedFiles()
.filter((x) => x instanceof TFolder) as TFolder[];
}

findFirstLinkOffset(file: TFile, linkFile: TFile): number {
return this.app.metadataCache
.getFileCache(file)
Expand Down
23 changes: 22 additions & 1 deletion src/commands.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
import {
Mode,
AnotherQuickSwitcherModal,
Mode,
} from "./ui/AnotherQuickSwitcherModal";
import { App, Command } from "obsidian";
import { Settings } from "./settings";
import { MoveModal } from "./ui/MoveModal";

export function showSearchDialog(app: App, mode: Mode, settings: Settings) {
const modal = new AnotherQuickSwitcherModal(app, mode, settings);
modal.open();
}

export function showMoveDialog(app: App, settings: Settings) {
if (!app.workspace.getActiveFile()) {
return;
}

const modal = new MoveModal(app, settings);
modal.open();
}

export function createCommands(app: App, settings: Settings): Command[] {
return [
{
Expand All @@ -36,5 +46,16 @@ export function createCommands(app: App, settings: Settings): Command[] {
showSearchDialog(app, "backlink", settings);
},
},
{
id: "move",
name: "Move file to another folder",
hotkeys: [{ modifiers: ["Mod", "Shift"], key: "m" }],
checkCallback: (checking: boolean) => {
if (checking) {
return Boolean(app.workspace.getActiveFile());
}
showMoveDialog(app, settings);
},
},
];
}
151 changes: 151 additions & 0 deletions src/ui/MoveModal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import { App, SuggestModal, TFolder } from "obsidian";
import { sorter } from "../utils/collection-helper";
import { FOLDER } from "./icons";
import { Settings } from "../settings";
import { AppHelper } from "../app-helper";
import { smartIncludes, smartStartsWith } from "../utils/strings";

interface SuggestionItem {
folder: TFolder;
matchType?: "name" | "prefix-name" | "directory";
}

function matchQuery(
item: SuggestionItem,
query: string,
matcher: (item: SuggestionItem, query: string) => boolean
): boolean {
const qs = query.split("/");
const folder = qs.pop();
return (
qs.every((dir) => smartIncludes(item.folder.parent.path, dir)) &&
matcher(item, folder)
);
}

function matchQueryAll(
item: SuggestionItem,
queries: string[],
matcher: (item: SuggestionItem, query: string) => boolean
): boolean {
return queries.every((q) => matchQuery(item, q, matcher));
}

function stampMatchType(
item: SuggestionItem,
queries: string[]
): SuggestionItem {
if (
matchQueryAll(item, queries, (item, query) =>
smartStartsWith(item.folder.name, query)
)
) {
return { ...item, matchType: "prefix-name" };
}

if (
matchQueryAll(item, queries, (item, query) =>
smartIncludes(item.folder.name, query)
)
) {
return { ...item, matchType: "name" };
}

if (
matchQueryAll(item, queries, (item, query) =>
smartIncludes(item.folder.path, query)
)
) {
return { ...item, matchType: "directory" };
}

return item;
}

export class MoveModal extends SuggestModal<SuggestionItem> {
originItems: SuggestionItem[];
appHelper: AppHelper;
settings: Settings;

constructor(app: App, settings: Settings) {
super(app);

this.appHelper = new AppHelper(app);
this.settings = settings;

this.setInstructions([
{ command: "[↑↓]", purpose: "navigate" },
{ command: "[↵]", purpose: "move to" },
{ command: "[esc]", purpose: "dismiss" },
]);

this.originItems = this.appHelper
.getFolders()
.filter((x) => !x.isRoot())
.map((x) => ({
folder: x,
}));
}

getSuggestions(query: string): SuggestionItem[] {
const qs = query.split(" ").filter((x) => x);

return this.originItems
.map((x) => stampMatchType(x, qs))
.filter((x) => x.matchType)
.sort(sorter((x) => (x.matchType === "directory" ? 1 : 0)))
.sort(
sorter(
(x) =>
x.matchType === "prefix-name" ? 1000 - x.folder.name.length : 0,
"desc"
)
)
.slice(0, 10);
}

renderSuggestion(item: SuggestionItem, el: HTMLElement) {
const itemDiv = createDiv({
cls: "another-quick-switcher__item",
});

const entryDiv = createDiv({
cls: "another-quick-switcher__item__entry",
});

const folderDiv = createDiv({
cls: "another-quick-switcher__item__file",
text: item.folder.name,
});
entryDiv.appendChild(folderDiv);

const directoryDiv = createDiv({
cls: "another-quick-switcher__item__directory",
});
directoryDiv.insertAdjacentHTML("beforeend", FOLDER);
directoryDiv.appendText(` ${item.folder.parent.name}`);
entryDiv.appendChild(directoryDiv);

const prefixIcon = createSpan({
cls: "another-quick-switcher__item__icon",
});
prefixIcon.insertAdjacentHTML("beforeend", FOLDER);
itemDiv.appendChild(prefixIcon);

itemDiv.appendChild(entryDiv);

el.appendChild(itemDiv);
}

async onChooseSuggestion(item: SuggestionItem): Promise<void> {
const activeFile = this.app.workspace.getActiveFile();
if (!activeFile) {
return;
}

await this.app.fileManager.renameFile(
activeFile,
`${item.folder.path}/${activeFile.name}`
);
}
}

0 comments on commit 9a4552a

Please sign in to comment.