Skip to content

Commit

Permalink
account for accents & use invalid char regex instead of valid char
Browse files Browse the repository at this point in the history
  • Loading branch information
Ebonsignori committed Feb 13, 2024
1 parent cc576a7 commit 59c29e8
Show file tree
Hide file tree
Showing 10 changed files with 100 additions and 66 deletions.
14 changes: 0 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,6 @@ Supports [aliases](https://help.obsidian.md/Linking+notes+and+files/Aliases) (ni

Uses the link type specified by your Obsidian `"Files & Links" -> "Use [[Wikiliks]]"` setting.

<details>

<summary>Supporting unicode characters in filenames </summary>

If you'd like to support unicode or other non-standard characters in filenames, update the **Valid character Regex** option and **Valid character Regex flags** options to the values below:

```
Valid character Regex = [\p{Letter}0-9$-_!%"'.,*&@()/;{}<>?~\`=+]
Valid character Regex flags = iu
```
> Regex courtesy of @theotheo
</details>
## Hotkeys

Supports `up`, `down`, `enter`, and `escape` keys for navigating the link search popup.
Expand Down
7 changes: 6 additions & 1 deletion src/compatibility-mode-extension/extension-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Platform, type App, type EditorPosition } from "obsidian";
import { AtSymbolLinkingSettings } from "src/settings/settings";
import { LinkSuggest } from "./extension-popup";
import { isValidFileNameCharacter } from "src/utils/valid-file-name";
import { removeAccents } from "src/utils/remove-accents";

// Max parents we will iterate through to determine if a click event is outside the suggestion popup
const maxParentDepth = 5;
Expand Down Expand Up @@ -113,7 +114,7 @@ export function atSymbolTriggerExtension(
}

// Build query when open
const key = event.key.toLowerCase();
const key = event.key.toLocaleLowerCase();
if (typedChar === "Backspace") {
if (this.openQuery.length === 0) {
return this.closeSuggestion();
Expand Down Expand Up @@ -145,6 +146,10 @@ export function atSymbolTriggerExtension(
return this.closeSuggestion();
}

if (settings.removeAccents) {
this.openQuery = removeAccents(this.openQuery);
}

if (!this.suggestionEl && this.firstOpenedCursor && this.view) {
const container = (<any>app).dom
.appContainerEl as HTMLElement;
Expand Down
4 changes: 2 additions & 2 deletions src/native-suggestion/hotkeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export function applyHotKeyHack(_this: any, app: any) {
/* Original isMatch function:
var n = e.modifiers
, i = e.key;
return (null === n || n === t.modifiers) && (!i || (i === t.vkey || !(!t.key || i.toLowerCase() !== t.key.toLowerCase())))
return (null === n || n === t.modifiers) && (!i || (i === t.vkey || !(!t.key || i.toLocaleLowerCase() !== t.key.toLocaleLowerCase())))
*/

const modifiers = hotkey.modifiers,
Expand All @@ -35,7 +35,7 @@ export function applyHotKeyHack(_this: any, app: any) {
return (
!key ||
key === context.vkey ||
!(!context.key || key.toLowerCase() !== context.key.toLowerCase())
!(!context.key || key.toLocaleLowerCase() !== context.key.toLocaleLowerCase())
);
};
_this.app.scope.register(
Expand Down
8 changes: 6 additions & 2 deletions src/native-suggestion/suggest-popup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { sharedSelectSuggestion } from "src/shared-suggestion/sharedSelectSugges
import sharedRenderSuggestion from "src/shared-suggestion/sharedRenderSuggestion";
import { sharedGetSuggestions } from "src/shared-suggestion/sharedGetSuggestions";
import { isValidFileNameCharacter } from "src/utils/valid-file-name";
import { removeAccents } from "src/utils/remove-accents";

export default class SuggestionPopup extends EditorSuggest<
Fuzzysort.KeysResult<fileOption>
Expand Down Expand Up @@ -134,7 +135,7 @@ export default class SuggestionPopup extends EditorSuggest<
return {
start: { ...cursor, ch: cursor.ch - 1 },
end: cursor,
query,
query: this.settings.removeAccents ? removeAccents(query) : query,
};
}

Expand Down Expand Up @@ -165,7 +166,10 @@ export default class SuggestionPopup extends EditorSuggest<

this.context?.editor.replaceRange(
linkText,
{ line: this.context.start.line, ch: line.lastIndexOf(this.settings.triggerSymbol) },
{
line: this.context.start.line,
ch: line.lastIndexOf(this.settings.triggerSymbol),
},
this.context.end
);

Expand Down
2 changes: 1 addition & 1 deletion src/settings/file-suggest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { TextInputSuggest } from "../utils/suggest";
export class FileSuggest extends TextInputSuggest<Fuzzysort.KeyResult<TFile>> {
getSuggestions(inputStr: string): Fuzzysort.KeyResult<TFile>[] {
let abstractFiles = this.app.vault.getAllLoadedFiles();
const lowerCaseInputStr = inputStr.toLowerCase();
const lowerCaseInputStr = inputStr.toLocaleLowerCase();

abstractFiles = abstractFiles.filter((file) => {
return file.path.endsWith(".md");
Expand Down
4 changes: 2 additions & 2 deletions src/settings/folder-suggest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ export class FolderSuggest extends TextInputSuggest<
getSuggestions(inputStr: string): Fuzzysort.KeyResult<TFolder>[] {
const abstractFiles = this.app.vault.getAllLoadedFiles();
const folders: TFolder[] = [];
const lowerCaseInputStr = inputStr.toLowerCase();
const lowerCaseInputStr = inputStr.toLocaleLowerCase();

abstractFiles.forEach((folder: TAbstractFile) => {
if (
folder instanceof TFolder &&
folder.path.toLowerCase()?.contains(lowerCaseInputStr)
folder.path.toLocaleLowerCase()?.contains(lowerCaseInputStr)
) {
folders.push(folder);
}
Expand Down
94 changes: 57 additions & 37 deletions src/settings/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ export interface AtSymbolLinkingSettings {
useCompatibilityMode: boolean;
leavePopupOpenForXSpaces: number;

validCharacterRegex: string;
validCharacterRegexFlags: string;
invalidCharacterRegex: string;
invalidCharacterRegexFlags: string;

removeAccents: boolean;
}

export const DEFAULT_SETTINGS: AtSymbolLinkingSettings = {
Expand All @@ -37,8 +39,11 @@ export const DEFAULT_SETTINGS: AtSymbolLinkingSettings = {
useCompatibilityMode: false,
leavePopupOpenForXSpaces: 0,

validCharacterRegex: `[a-z0-9$-_!%"'.,*&@()/;{}<>?~\`=+]`,
validCharacterRegexFlags: "i",
// eslint-disable-next-line no-useless-escape
invalidCharacterRegex: `[\[\]^|#]`,
invalidCharacterRegexFlags: "i",

removeAccents: true,
};

const arrayMove = <T>(array: T[], fromIndex: number, toIndex: number): void => {
Expand Down Expand Up @@ -355,55 +360,70 @@ export class SettingsTab extends PluginSettingTab {

new Setting(this.containerEl).setName("Advanced settings").setHeading();

// Begin valid character regex option
const validCharacterRegexDesc = document.createDocumentFragment();
validCharacterRegexDesc.append(
"JavaScript Regular Expression used to determine if a character in autocomplete is valid for looking up or creating new files.",
validCharacterRegexDesc.createEl("br"),
"Characters typed that don't match this regex will not be included in the final search query in compatibility mode.",
validCharacterRegexDesc.createEl("br"),
// Begin invalid character regex option
const invalidCharacterRegexDesc = document.createDocumentFragment();
invalidCharacterRegexDesc.append(
invalidCharacterRegexDesc.createEl("br"),
"Characters typed that match this regex will not be included in the final search query in compatibility mode.",
invalidCharacterRegexDesc.createEl("br"),
"In normal mode, the popup will close when an invalid character is typed."
);

new Setting(this.containerEl)
.setName("Valid character Regex")
.setDesc(validCharacterRegexDesc)
.setName("Invalid character Regex")
.setDesc(invalidCharacterRegexDesc)
.addText((text) => {
text.setPlaceholder(this.plugin.settings.validCharacterRegex)
.setValue(this.plugin.settings.validCharacterRegex)
text.setPlaceholder(this.plugin.settings.invalidCharacterRegex)
.setValue(this.plugin.settings.invalidCharacterRegex)
.onChange(async (value) => {
this.plugin.settings.validCharacterRegex = value;
this.plugin.settings.invalidCharacterRegex = value;
await this.plugin.saveSettings();
});
text.inputEl.onblur = () => {
this.validate("validCharacterRegex");
this.validate("invalidCharacterRegex");
};
});
// End valid character regex option

// Begin valid character regex flags option
const validCharacterRegexFlagsDesc = document.createDocumentFragment();
validCharacterRegexFlagsDesc.append(
"Flags to use with the valid character regex."
const invalidCharacterRegexFlagsDesc =
document.createDocumentFragment();
invalidCharacterRegexFlagsDesc.append(
"Flags to use with the invalid character regex."
);

new Setting(this.containerEl)
.setName("Valid character Regex flags")
.setDesc(validCharacterRegexFlagsDesc)
.setName("Invalid character Regex flags")
.setDesc(invalidCharacterRegexFlagsDesc)
.addText((text) => {
text.setPlaceholder(
this.plugin.settings.validCharacterRegexFlags
this.plugin.settings.invalidCharacterRegexFlags
)
.setValue(this.plugin.settings.validCharacterRegexFlags)
.setValue(this.plugin.settings.invalidCharacterRegexFlags)
.onChange(async (value) => {
this.plugin.settings.validCharacterRegexFlags = value;
this.plugin.settings.invalidCharacterRegexFlags = value;
await this.plugin.saveSettings();
});
text.inputEl.onblur = () => {
this.validate("validCharacterRegexFlags");
this.validate("invalidCharacterRegexFlags");
};
});
// End valid character regex flags option

// Begin remove accents option
new Setting(this.containerEl)
.setName("Remove accents from search query")
.setDesc(
"e.g. é -> e when searching or creating links via the popup."
)
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.removeAccents)
.onChange((value: boolean) => {
this.plugin.settings.removeAccents = value;
this.plugin.saveSettings();
})
);
}

async validate(editedSetting?: string) {
Expand Down Expand Up @@ -479,28 +499,28 @@ export class SettingsTab extends PluginSettingTab {
}

// Regex should be valid and not be empty
if (settings.validCharacterRegex?.trim() === "") {
if (settings.invalidCharacterRegex?.trim() === "") {
await updateSetting(
"validCharacterRegex",
DEFAULT_SETTINGS.validCharacterRegex
"invalidCharacterRegex",
DEFAULT_SETTINGS.invalidCharacterRegex
);
}
try {
new RegExp(
settings.validCharacterRegex,
settings.validCharacterRegexFlags
settings.invalidCharacterRegex,
settings.invalidCharacterRegexFlags
);
} catch (e) {
new Notice(`Invalid regex or flags`);
if (editedSetting === "validCharacterRegex") {
if (editedSetting === "invalidCharacterRegex") {
await updateSetting(
"validCharacterRegex",
DEFAULT_SETTINGS.validCharacterRegex
"invalidCharacterRegex",
DEFAULT_SETTINGS.invalidCharacterRegex
);
} else if (editedSetting === "validCharacterRegexFlags") {
} else if (editedSetting === "invalidCharacterRegexFlags") {
await updateSetting(
"validCharacterRegexFlags",
DEFAULT_SETTINGS.validCharacterRegexFlags
"invalidCharacterRegexFlags",
DEFAULT_SETTINGS.invalidCharacterRegexFlags
);
}
}
Expand Down
20 changes: 15 additions & 5 deletions src/shared-suggestion/sharedGetSuggestions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import fuzzysort from "fuzzysort";
import { type TFile } from "obsidian";
import { type AtSymbolLinkingSettings } from "src/settings/settings";
import { type fileOption } from "src/types";
import { removeAccents } from "src/utils/remove-accents";

export function sharedGetSuggestions(
files: TFile[],
Expand All @@ -26,7 +27,9 @@ export function sharedGetSuggestions(
const meta = app.metadataCache.getFileCache(file);
if (meta?.frontmatter?.alias) {
options.push({
fileName: file.basename,
fileName: settings.removeAccents
? removeAccents(file.basename)
: file.basename,
filePath: file.path,
alias: meta.frontmatter.alias,
});
Expand All @@ -39,15 +42,21 @@ export function sharedGetSuggestions(
}
for (const alias of aliases) {
options.push({
fileName: file.basename,
fileName: settings.removeAccents
? removeAccents(file.basename)
: file.basename,
filePath: file.path,
alias: alias,
alias: settings.removeAccents
? removeAccents(alias)
: alias,
});
}
}
// Include fileName without alias as well
options.push({
fileName: file.basename,
fileName: settings.removeAccents
? removeAccents(file.basename)
: file.basename,
filePath: file.path,
});
}
Expand All @@ -73,7 +82,8 @@ export function sharedGetSuggestions(
// Don't show if it has the same filename as an existing note
const hasExistingNote = results.some(
(result: Fuzzysort.KeysResult<fileOption>) =>
result?.obj?.fileName.toLowerCase() === query?.toLowerCase()
result?.obj?.fileName.toLocaleLowerCase() ===
query?.toLocaleLowerCase()
);
if (!hasExistingNote) {
results = results.filter(
Expand Down
3 changes: 3 additions & 0 deletions src/utils/remove-accents.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function removeAccents(str: string) {
return str.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
}
10 changes: 8 additions & 2 deletions src/utils/valid-file-name.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { AtSymbolLinkingSettings } from "src/settings/settings";

export const isValidFileNameCharacter = (char: string, settings: AtSymbolLinkingSettings) => {
export const isValidFileNameCharacter = (
char: string,
settings: AtSymbolLinkingSettings
) => {
if (char === " ") {
return true;
}
if (char === "\\") {
return false;
}
return new RegExp(settings.validCharacterRegex).test(char);
return !new RegExp(
settings.invalidCharacterRegex,
settings.invalidCharacterRegexFlags
).test(char);
};

0 comments on commit 59c29e8

Please sign in to comment.