-
Notifications
You must be signed in to change notification settings - Fork 673
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Simplified ResourceLink model and added utility functions to manipula…
…te it
- Loading branch information
1 parent
88227d4
commit 5b7a2ab
Showing
13 changed files
with
289 additions
and
60 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
200 changes: 200 additions & 0 deletions
200
packages/foam-vscode/src/core/services/markdown-link.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
import { getRandomURI } from '../../test/test-utils'; | ||
import { ResourceLink } from '../model/note'; | ||
import { Range } from '../model/range'; | ||
import { createMarkdownParser } from '../services/markdown-parser'; | ||
import { MarkdownLink } from './markdown-link'; | ||
|
||
describe('MarkdownLink', () => { | ||
const parser = createMarkdownParser([]); | ||
describe('parse wikilink', () => { | ||
it('should parse target', () => { | ||
const link = parser.parse(getRandomURI(), `this is a [[wikilink]]`) | ||
.links[0]; | ||
const parsed = MarkdownLink.analyzeLink(link); | ||
expect(parsed.target).toEqual('wikilink'); | ||
expect(parsed.section).toBeUndefined(); | ||
expect(parsed.alias).toBeUndefined(); | ||
}); | ||
it('should parse target and section', () => { | ||
const link = parser.parse( | ||
getRandomURI(), | ||
`this is a [[wikilink#section]]` | ||
).links[0]; | ||
const parsed = MarkdownLink.analyzeLink(link); | ||
expect(parsed.target).toEqual('wikilink'); | ||
expect(parsed.section).toEqual('section'); | ||
expect(parsed.alias).toBeUndefined(); | ||
}); | ||
it('should parse target and alias', () => { | ||
const link = parser.parse(getRandomURI(), `this is a [[wikilink|alias]]`) | ||
.links[0]; | ||
const parsed = MarkdownLink.analyzeLink(link); | ||
expect(parsed.target).toEqual('wikilink'); | ||
expect(parsed.section).toBeUndefined(); | ||
expect(parsed.alias).toEqual('alias'); | ||
}); | ||
it('should parse target and alias with escaped separator', () => { | ||
const link = parser.parse( | ||
getRandomURI(), | ||
`this is a [[wikilink\\|alias]]` | ||
).links[0]; | ||
const parsed = MarkdownLink.analyzeLink(link); | ||
expect(parsed.target).toEqual('wikilink'); | ||
expect(parsed.section).toBeUndefined(); | ||
expect(parsed.alias).toEqual('alias'); | ||
}); | ||
it('should parse target section and alias', () => { | ||
const link = parser.parse( | ||
getRandomURI(), | ||
`this is a [[wikilink with spaces#section with spaces|alias with spaces]]` | ||
).links[0]; | ||
const parsed = MarkdownLink.analyzeLink(link); | ||
expect(parsed.target).toEqual('wikilink with spaces'); | ||
expect(parsed.section).toEqual('section with spaces'); | ||
expect(parsed.alias).toEqual('alias with spaces'); | ||
}); | ||
it('should parse section', () => { | ||
const link = parser.parse(getRandomURI(), `this is a [[#section]]`) | ||
.links[0]; | ||
const parsed = MarkdownLink.analyzeLink(link); | ||
expect(parsed.target).toBeUndefined(); | ||
expect(parsed.section).toEqual('section'); | ||
expect(parsed.alias).toBeUndefined(); | ||
}); | ||
}); | ||
|
||
describe('parse direct link', () => { | ||
it('should parse target', () => { | ||
const link = parser.parse(getRandomURI(), `this is a [link](to/path.md)`) | ||
.links[0]; | ||
const parsed = MarkdownLink.analyzeLink(link); | ||
expect(parsed.target).toEqual('to/path.md'); | ||
expect(parsed.section).toBeUndefined(); | ||
expect(parsed.alias).toEqual('link'); | ||
}); | ||
it('should parse target and section', () => { | ||
const link = parser.parse( | ||
getRandomURI(), | ||
`this is a [link](to/path.md#section)` | ||
).links[0]; | ||
const parsed = MarkdownLink.analyzeLink(link); | ||
expect(parsed.target).toEqual('to/path.md'); | ||
expect(parsed.section).toEqual('section'); | ||
expect(parsed.alias).toEqual('link'); | ||
}); | ||
it('should parse section only', () => { | ||
const link: ResourceLink = { | ||
type: 'link', | ||
rawText: '[link](#section)', | ||
range: Range.create(0, 0), | ||
}; | ||
const parsed = MarkdownLink.analyzeLink(link); | ||
expect(parsed.target).toBeUndefined(); | ||
expect(parsed.section).toEqual('section'); | ||
expect(parsed.alias).toEqual('link'); | ||
}); | ||
}); | ||
|
||
describe('rename wikilink', () => { | ||
it.skip('should rename the target only', () => { | ||
const link = parser.parse( | ||
getRandomURI(), | ||
`this is a [[wikilink#section]]` | ||
).links[0]; | ||
const edit = MarkdownLink.createUpdateLinkEdit(link, { | ||
target: 'new-link', | ||
}); | ||
expect(edit.newText).toEqual(`[[new-link#section]]`); | ||
expect(edit.selection).toEqual(link.range); | ||
}); | ||
it('should rename the section only', () => { | ||
const link = parser.parse( | ||
getRandomURI(), | ||
`this is a [[wikilink#section]]` | ||
).links[0]; | ||
const edit = MarkdownLink.createUpdateLinkEdit(link, { | ||
section: 'new-section', | ||
}); | ||
expect(edit.newText).toEqual(`[[wikilink#new-section]]`); | ||
expect(edit.selection).toEqual(link.range); | ||
}); | ||
it('should rename both target and section', () => { | ||
const link = parser.parse( | ||
getRandomURI(), | ||
`this is a [[wikilink#section]]` | ||
).links[0]; | ||
const edit = MarkdownLink.createUpdateLinkEdit(link, { | ||
target: 'new-link', | ||
section: 'new-section', | ||
}); | ||
expect(edit.newText).toEqual(`[[new-link#new-section]]`); | ||
expect(edit.selection).toEqual(link.range); | ||
}); | ||
it('should be able to remove the section', () => { | ||
const link = parser.parse( | ||
getRandomURI(), | ||
`this is a [[wikilink#section]]` | ||
).links[0]; | ||
const edit = MarkdownLink.createUpdateLinkEdit(link, { | ||
section: '', | ||
}); | ||
expect(edit.newText).toEqual(`[[wikilink]]`); | ||
expect(edit.selection).toEqual(link.range); | ||
}); | ||
it('should be able to rename the alias', () => { | ||
const link = parser.parse(getRandomURI(), `this is a [[wikilink|alias]]`) | ||
.links[0]; | ||
const edit = MarkdownLink.createUpdateLinkEdit(link, { | ||
alias: 'new-alias', | ||
}); | ||
expect(edit.newText).toEqual(`[[wikilink|new-alias]]`); | ||
expect(edit.selection).toEqual(link.range); | ||
}); | ||
}); | ||
|
||
describe('rename direct link', () => { | ||
it('should rename the target only', () => { | ||
const link = parser.parse(getRandomURI(), `this is a [link](to/path.md)`) | ||
.links[0]; | ||
const edit = MarkdownLink.createUpdateLinkEdit(link, { | ||
target: 'to/another-path.md', | ||
}); | ||
expect(edit.newText).toEqual(`[link](to/another-path.md)`); | ||
expect(edit.selection).toEqual(link.range); | ||
}); | ||
it('should rename the section only', () => { | ||
const link = parser.parse( | ||
getRandomURI(), | ||
`this is a [link](to/path.md#section)` | ||
).links[0]; | ||
const edit = MarkdownLink.createUpdateLinkEdit(link, { | ||
section: 'section2', | ||
}); | ||
expect(edit.newText).toEqual(`[link](to/path.md#section2)`); | ||
expect(edit.selection).toEqual(link.range); | ||
}); | ||
it('should rename both target and section', () => { | ||
const link = parser.parse( | ||
getRandomURI(), | ||
`this is a [link](to/path.md#section)` | ||
).links[0]; | ||
const edit = MarkdownLink.createUpdateLinkEdit(link, { | ||
target: 'to/another-path.md', | ||
section: 'section2', | ||
}); | ||
expect(edit.newText).toEqual(`[link](to/another-path.md#section2)`); | ||
expect(edit.selection).toEqual(link.range); | ||
}); | ||
it('should be able to remove the section', () => { | ||
const link = parser.parse( | ||
getRandomURI(), | ||
`this is a [link](to/path.md#section)` | ||
).links[0]; | ||
const edit = MarkdownLink.createUpdateLinkEdit(link, { | ||
section: '', | ||
}); | ||
expect(edit.newText).toEqual(`[link](to/path.md)`); | ||
expect(edit.selection).toEqual(link.range); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import { ResourceLink } from '../model/note'; | ||
|
||
export abstract class MarkdownLink { | ||
private static wikilinkRegex = new RegExp( | ||
/\[\[([^#\|]+)?#?([^\|]+)?\|?(.*)?\]\]/ | ||
); | ||
private static directLinkRegex = new RegExp( | ||
/\[([^\]]+)\]\(([^#]*)?#?([^\]]+)?\)/ | ||
); | ||
|
||
public static analyzeLink(link: ResourceLink) { | ||
if (link.type === 'wikilink') { | ||
const [_, target, section, alias] = this.wikilinkRegex.exec(link.rawText); | ||
return { | ||
target: target?.replace(/\\/g, ''), | ||
section, | ||
alias, | ||
}; | ||
} | ||
if (link.type === 'link') { | ||
const [_, alias, target, section] = this.directLinkRegex.exec( | ||
link.rawText | ||
); | ||
return { target, section, alias }; | ||
} | ||
throw new Error( | ||
`Unexpected state: link of type ${link.type} is not supported` | ||
); | ||
} | ||
|
||
public static createUpdateLinkEdit( | ||
link: ResourceLink, | ||
delta: { target?: string; section?: string; alias?: string } | ||
) { | ||
const { target, section, alias } = MarkdownLink.analyzeLink(link); | ||
const newTarget = delta.target ?? target; | ||
const newSection = delta.section ?? section ?? ''; | ||
const newAlias = delta.alias ?? alias ?? ''; | ||
const sectionDivider = newSection ? '#' : ''; | ||
const aliasDivider = newAlias ? '|' : ''; | ||
if (link.type === 'wikilink') { | ||
return { | ||
newText: `[[${newTarget}${sectionDivider}${newSection}${aliasDivider}${newAlias}]]`, | ||
selection: link.range, | ||
}; | ||
} | ||
if (link.type === 'link') { | ||
return { | ||
newText: `[${newAlias}](${newTarget}${sectionDivider}${newSection})`, | ||
selection: link.range, | ||
}; | ||
} | ||
throw new Error( | ||
`Unexpected state: link of type ${link.type} is not supported` | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.