Skip to content

Commit

Permalink
fix: remove illegal chars from heading link subpath #154
Browse files Browse the repository at this point in the history
  • Loading branch information
darlal committed May 18, 2024
1 parent 7e33cd3 commit bd74d90
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 24 deletions.
16 changes: 16 additions & 0 deletions src/utils/__tests__/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,22 @@ describe('utils', () => {
);
});

test('generated links for Heading subpath should not contain illegal link characters', () => {
const illegalChar = ':#|^\\%%[[]]';
const heading = `head ${illegalChar} tail`;
const expectedLinkHeadingStr = 'head tail';
const sugg = makeHeadingSuggestion(makeHeading(heading, 1), destFile);

generateMarkdownLink(mockFileManager, mockVault, sugg, activeFilePath);

expect(mockFileManager.generateMarkdownLink).toHaveBeenCalledWith(
destFile,
activeFilePath,
`#${expectedLinkHeadingStr}`,
expectedLinkHeadingStr,
);
});

test('with useHeadingAsAlias disabled, it should generate a link for Heading suggestions with file basename as alias', () => {
const heading = chance.sentence();
const sugg = makeHeadingSuggestion(makeHeading(heading, 1), destFile);
Expand Down
76 changes: 52 additions & 24 deletions src/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,30 +224,9 @@ export function generateMarkdownLink(
options = Object.assign({ useBasenameAsAlias: true, useHeadingAsAlias: true }, options);

if (sugg) {
let destFile: TFile = null;
let destFile = getDestinationFileForSuggestion(sugg);
let alias = null;
let subpath = null;
const fileSuggTypes = [
SuggestionType.Alias,
SuggestionType.Bookmark,
SuggestionType.HeadingsList,
SuggestionType.SymbolList,
SuggestionType.RelatedItemsList,
SuggestionType.EditorList,
SuggestionType.File,
];

// for file based suggestions, get the destination file
if (fileSuggTypes.includes(sugg.type)) {
destFile = (sugg as { file: TFile }).file;
}

const linkSubPathForHeading = (heading: string) => {
return {
subpath: `#${heading}`,
alias: options.useHeadingAsAlias ? heading : null,
};
};

switch (sugg.type) {
case SuggestionType.Unresolved:
Expand All @@ -265,7 +244,10 @@ export function generateMarkdownLink(
}
case SuggestionType.HeadingsList: {
const { heading } = sugg.item;
({ subpath, alias } = linkSubPathForHeading(heading));
({ subpath, alias } = sanitizeStringForLinkSubpath(
heading,
options.useHeadingAsAlias,
));
break;
}
case SuggestionType.SymbolList: {
Expand All @@ -274,7 +256,10 @@ export function generateMarkdownLink(
} = sugg;

if (isHeadingCache(symbol)) {
({ subpath, alias } = linkSubPathForHeading(symbol.heading));
({ subpath, alias } = sanitizeStringForLinkSubpath(
symbol.heading,
options.useHeadingAsAlias,
));
} else if (isOfType<ReferenceCache>(symbol, 'link')) {
// Test if the link matches the external link format [text](url)
const isExternalLink = new RegExp(/^\[(.*?)\]\((.+?)\)/).test(symbol.original);
Expand Down Expand Up @@ -319,6 +304,49 @@ export function generateMarkdownLink(
return linkStr;
}

function sanitizeStringForLinkSubpath(
input: string,
useInputAsAlias: boolean,
): { subpath: string; alias: string | null } {
// May 2024: shamelessly borrowed from Obsidian
const illegalLinkCharsRegex = /([:#|^\\\r\n]|%%|\[\[|]])/g;
const sanitizedInput = input
.replace(illegalLinkCharsRegex, ' ')
.replace(/\s+/g, ' ')
.trim();

return {
subpath: `#${sanitizedInput}`,
alias: useInputAsAlias ? sanitizedInput : null,
};
}

/**
* Determines if sugg is a file-based suggestion, and if so, returns the associated
* destination TFile. Otherwise returns null.
* @param {AnySuggestion} sugg
* @returns TFile|null
*/
function getDestinationFileForSuggestion(sugg: AnySuggestion): TFile | null {
let destFile: TFile = null;
const fileSuggTypes = [
SuggestionType.Alias,
SuggestionType.Bookmark,
SuggestionType.HeadingsList,
SuggestionType.SymbolList,
SuggestionType.RelatedItemsList,
SuggestionType.EditorList,
SuggestionType.File,
];

if (fileSuggTypes.includes(sugg.type)) {
// for file based suggestions, get the destination file
destFile = (sugg as { file: TFile }).file;
}

return destFile;
}

function generateMarkdownLinkForUnresolved(path: string, displayText?: string): string {
displayText = displayText?.length ? `|${displayText}` : '';
return `[[${path}${displayText}]]`;
Expand Down

0 comments on commit bd74d90

Please sign in to comment.