mirror of
https://github.com/esm7/obsidian-map-view.git
synced 2026-07-22 05:40:27 +00:00
This commit is contained in:
parent
fb81b9b5d8
commit
c9edcc7124
4 changed files with 112 additions and 10 deletions
|
|
@ -47,7 +47,9 @@ export default {
|
|||
exportConditions: ['svelte'],
|
||||
extensions: ['.mjs', '.js', '.json', '.node', '.ts', '.svelte'],
|
||||
}),
|
||||
typescript(),
|
||||
typescript({
|
||||
outDir: process.env.BUILD === 'development' ? '.' : './dist',
|
||||
}),
|
||||
image(),
|
||||
commonjs(),
|
||||
postcss({
|
||||
|
|
|
|||
38
src/regex.ts
38
src/regex.ts
|
|
@ -1,11 +1,26 @@
|
|||
const r = String.raw;
|
||||
|
||||
// Shared character class content for tag characters: unicode letters, numbers,
|
||||
// emoji (including variation selectors like U+FE0F), and punctuation allowed in tags.
|
||||
const TAG_CHARS = r`\p{L}\p{N}\p{Extended_Pictographic}\p{So}\uFE00-\uFE0F_\/\-`;
|
||||
// A complete tag pattern used inside larger regexes (no '#', no wildcard)
|
||||
const INLINE_TAG_PATTERN = r`tag:[${TAG_CHARS}]+(?:[\s,.]+|$)`;
|
||||
|
||||
// The pound sign is optional here
|
||||
export const TAG_NAME_WITH_HEADER =
|
||||
/tag:(#?[\p{L}\p{N}\p{Extended_Pictographic}\p{So}_\/\-]*)/gu;
|
||||
export const TAG_NAME_WITH_HEADER = new RegExp(
|
||||
r`tag:(#?[${TAG_CHARS}]*)`,
|
||||
'gu',
|
||||
);
|
||||
// Same as above, but also supporting wildcards for query purposes (not used for inline tags)
|
||||
export const TAG_NAME_WITH_HEADER_AND_WILDCARD =
|
||||
/tag:(#?[\p{L}\p{N}\p{Extended_Pictographic}\p{So}_\/\-\*]*)/gu;
|
||||
export const TAG_NAME_WITH_HEADER_AND_WILDCARD = new RegExp(
|
||||
r`tag:(#?[${TAG_CHARS}\*]*)`,
|
||||
'gu',
|
||||
);
|
||||
// Note no '#' sign
|
||||
export const INLINE_TAG_IN_NOTE = /tag:(?<tag>[\p{L}\p{N}_\/\-]+)/gu;
|
||||
export const INLINE_TAG_IN_NOTE = new RegExp(
|
||||
r`tag:(?<tag>[${TAG_CHARS}]+)`,
|
||||
'gu',
|
||||
);
|
||||
export const PATH = "['p{L}p{N}_,&()/-\\.]+?";
|
||||
// path:"..."
|
||||
export const PATH_QUERY_WITH_HEADER = /path:"((?:[^"]|\\")+?)"/gu;
|
||||
|
|
@ -25,8 +40,10 @@ export const INLINE_LOCATION_OLD_SYNTAX =
|
|||
/`location:\s*\[?(?<lat>[+-]?([0-9]*[.])?[0-9]+)\s*,\s*(?<lng>[+-]?([0-9]*[.])?[0-9]+)\]?/g;
|
||||
// A link name is defined here as [^\]]* to prevent a previous link in the same line to count as the beginning
|
||||
// of the link name
|
||||
export const INLINE_LOCATION_WITH_TAGS =
|
||||
/(?<link>\[(?<name>[^\]]*?)\]\(geo:(?<lat>[+-]?([0-9]*[.])?[0-9]+),(?<lng>[+-]?([0-9]*[.])?[0-9]+)\))[ \t]*(?<tags>(tag:[\p{L}\p{N}_\/\-]+(?:[\s,.]+|$))*)/gu;
|
||||
export const INLINE_LOCATION_WITH_TAGS = new RegExp(
|
||||
r`(?<link>\[(?<name>[^\]]*?)\]\(geo:(?<lat>[+-]?([0-9]*[.])?[0-9]+),(?<lng>[+-]?([0-9]*[.])?[0-9]+)\))[ \t]*(?<tags>(${INLINE_TAG_PATTERN})*)`,
|
||||
'gu',
|
||||
);
|
||||
// Should be exactly like above but without the tags
|
||||
export const INLINE_LOCATION_WITHOUT_TAGS =
|
||||
/(?<link>\[(?<name>[^\]]*?)\]\(geo:(?<lat>[+-]?([0-9]*[.])?[0-9]+),(?<lng>[+-]?([0-9]*[.])?[0-9]+)\))/gu;
|
||||
|
|
@ -38,5 +55,8 @@ export const FRONT_MATTER_LOCATION_V2 =
|
|||
// location: [32.84577588420059,35.36074429750443]
|
||||
export const FRONT_MATTER_LOCATION =
|
||||
/(?<header>^---.*)(?<loc>location:[ \t]*\[(?<lat>[+-]?([0-9]*[.])?[0-9]+),(?<lng>[+-]?([0-9]*[.])?[0-9]+)\]).*^---/ms;
|
||||
export const INLINE_GEOJSON =
|
||||
/```geojson\n(?<content>[^`]*)```\s*\n?(?<tags>(tag:[\p{L}\p{N}_\/\-]+(?:[\s,.]+|$))*)/gu;
|
||||
// Note: backtick (\x60) can't appear in a template literal, so we use \x60 (hex) to represent it.
|
||||
export const INLINE_GEOJSON = new RegExp(
|
||||
r`\x60\x60\x60geojson\n(?<content>[^\x60]*)\x60\x60\x60\s*\n?(?<tags>(${INLINE_TAG_PATTERN})*)`,
|
||||
'gu',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -215,4 +215,30 @@ describe('Query.testLayer', () => {
|
|||
const q = new Query(null as any, 'tag:#trip*');
|
||||
expect(q.testLayer(mockLayer({ tags: ['#trip-🏕️'] }))).toBe(true);
|
||||
});
|
||||
|
||||
// Emoji tags with variation selectors (U+FE0F) — issue #385
|
||||
// These emojis are two code points: base emoji + variation selector-16 (️).
|
||||
// The query preprocessor regex must include variation selectors, otherwise the
|
||||
// variation selector ends up outside the quoted token and crashes boon-js.
|
||||
it('does not crash when constructing query with emoji+variation-selector tag (☕️)', () => {
|
||||
expect(() => new Query(null as any, 'tag:#☕️')).not.toThrow();
|
||||
});
|
||||
|
||||
it('does not crash when constructing query with emoji+variation-selector tag (⚽️)', () => {
|
||||
expect(() => new Query(null as any, 'tag:#⚽️')).not.toThrow();
|
||||
});
|
||||
|
||||
it('does not crash when constructing query with emoji+variation-selector tag (🏔️)', () => {
|
||||
expect(() => new Query(null as any, 'tag:#🏔️')).not.toThrow();
|
||||
});
|
||||
|
||||
it('emoji+variation-selector tag query matches layer with that tag', () => {
|
||||
const q = new Query(null as any, 'tag:#☕️');
|
||||
expect(q.testLayer(mockLayer({ tags: ['#☕️'] }))).toBe(true);
|
||||
});
|
||||
|
||||
it('emoji+variation-selector tag query does not match layer without that tag', () => {
|
||||
const q = new Query(null as any, 'tag:#☕️');
|
||||
expect(q.testLayer(mockLayer({ tags: ['#🍵'] }))).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -150,6 +150,21 @@ describe('TAG_NAME_WITH_HEADER', () => {
|
|||
expect(matches[0][1]).toBe('#🌍');
|
||||
});
|
||||
|
||||
it('captures emoji tag with variation selector (e.g. ☕️)', () => {
|
||||
// ☕️ = U+2615 + U+FE0F (variation selector-16)
|
||||
const input = 'tag:#☕️';
|
||||
const matches = [...input.matchAll(regex.TAG_NAME_WITH_HEADER)];
|
||||
expect(matches).toHaveLength(1);
|
||||
expect(matches[0][1]).toBe('#☕️');
|
||||
});
|
||||
|
||||
it('captures emoji tag with variation selector (e.g. ⚽️)', () => {
|
||||
const input = 'tag:#⚽️';
|
||||
const matches = [...input.matchAll(regex.TAG_NAME_WITH_HEADER)];
|
||||
expect(matches).toHaveLength(1);
|
||||
expect(matches[0][1]).toBe('#⚽️');
|
||||
});
|
||||
|
||||
it('captures accented tag', () => {
|
||||
const input = 'tag:#café';
|
||||
const matches = [...input.matchAll(regex.TAG_NAME_WITH_HEADER)];
|
||||
|
|
@ -167,6 +182,15 @@ describe('TAG_NAME_WITH_HEADER_AND_WILDCARD', () => {
|
|||
expect(matches).toHaveLength(1);
|
||||
expect(matches[0][1]).toBe('#trip*');
|
||||
});
|
||||
|
||||
it('captures emoji tag with variation selector', () => {
|
||||
const input = 'tag:#☕️';
|
||||
const matches = [
|
||||
...input.matchAll(regex.TAG_NAME_WITH_HEADER_AND_WILDCARD),
|
||||
];
|
||||
expect(matches).toHaveLength(1);
|
||||
expect(matches[0][1]).toBe('#☕️');
|
||||
});
|
||||
});
|
||||
|
||||
describe('PATH_QUERY_WITH_HEADER', () => {
|
||||
|
|
@ -192,4 +216,34 @@ describe('INLINE_TAG_IN_NOTE', () => {
|
|||
expect(matches).toHaveLength(1);
|
||||
expect(matches[0].groups?.tag).toBe('旅行');
|
||||
});
|
||||
|
||||
it('captures an emoji tag (no variation selector)', () => {
|
||||
const input = 'tag:🌍';
|
||||
const matches = [...input.matchAll(regex.INLINE_TAG_IN_NOTE)];
|
||||
expect(matches).toHaveLength(1);
|
||||
expect(matches[0].groups?.tag).toBe('🌍');
|
||||
});
|
||||
|
||||
it('captures an emoji tag with variation selector (e.g. ☕️)', () => {
|
||||
const input = 'tag:☕️';
|
||||
const matches = [...input.matchAll(regex.INLINE_TAG_IN_NOTE)];
|
||||
expect(matches).toHaveLength(1);
|
||||
expect(matches[0].groups?.tag).toBe('☕️');
|
||||
});
|
||||
});
|
||||
|
||||
describe('INLINE_LOCATION_WITH_TAGS emoji tags', () => {
|
||||
it('extracts emoji tag without variation selector', () => {
|
||||
const input = '[Café](geo:32.1,35.2) tag:🌍';
|
||||
const matches = [...input.matchAll(regex.INLINE_LOCATION_WITH_TAGS)];
|
||||
expect(matches).toHaveLength(1);
|
||||
expect(matches[0].groups?.tags).toContain('tag:🌍');
|
||||
});
|
||||
|
||||
it('extracts emoji tag with variation selector (e.g. ☕️)', () => {
|
||||
const input = '[Café](geo:32.1,35.2) tag:☕️';
|
||||
const matches = [...input.matchAll(regex.INLINE_LOCATION_WITH_TAGS)];
|
||||
expect(matches).toHaveLength(1);
|
||||
expect(matches[0].groups?.tags).toContain('tag:☕️');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue