Skip to content

Commit 349d47e

Browse files
authored
Add Function to convert data types to their display value (#2480)
* feat: add display function * documentation for default function
1 parent 9876db2 commit 349d47e

File tree

5 files changed

+74
-2
lines changed

5 files changed

+74
-2
lines changed

docs/docs/reference/functions.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -704,6 +704,23 @@ default(list(1, 2, null), 3) = list(1, 2, 3)
704704
ldefault(list(1, 2, null), 3) = list(1, 2, null)
705705
```
706706

707+
### `display()`
708+
709+
Display function converts the input into a string representation while trying to
710+
preserve the display property of data types.
711+
This means that links and urls will be replaced by their display value.
712+
713+
714+
```js
715+
display("Hello World") = "Hello World"
716+
display("**Hello** World") = "Hello World"
717+
display("[Hello](https://example.com) [[World]]") = "Hello World"
718+
display(link("path/to/file.md")) = "file"
719+
display(link("path/to/file.md", "displayname")) = "displayname"
720+
display(date("2024-11-18")) = "November 18, 2024"
721+
display(list("Hello", "World")) = "Hello, World"
722+
```
723+
707724
### `choice(bool, left, right)`
708725

709726
A primitive if statement - if the first argument is truthy, returns left; otherwise, returns right.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
"obsidian-calendar-ui": "^0.3.12",
5757
"papaparse": "^5.3.1",
5858
"parsimmon": "^1.18.0",
59-
"preact": "^10.6.5"
59+
"preact": "^10.6.5",
60+
"remove-markdown": "^0.5.5"
6061
}
6162
}

src/expression/functions.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { LiteralReprAll, LiteralTypeOrAll } from "./binaryop";
77
import { Context } from "./context";
88
import { Fields } from "./field";
99
import { EXPRESSION } from "./parse";
10-
import { escapeRegex } from "util/normalize";
10+
import { escapeRegex, normalizeMarkdown } from "util/normalize";
1111
import { DataArray } from "api/data-array";
1212
import { cyrb53 } from "util/hash";
1313

@@ -701,6 +701,25 @@ export namespace DefaultFunctions {
701701
.add2("*", "*", (v, bk) => (Values.isNull(v) ? bk : v))
702702
.build();
703703

704+
// Returns the display name of the element.
705+
export const display = new FunctionBuilder("display")
706+
.add1("null", (): Literal => "")
707+
.add1("array", (a: Literal[], ctx: Context): Literal => {
708+
return a.map(e => display(ctx, e)).join(", ");
709+
})
710+
.add1("string", (str: string): Literal => normalizeMarkdown(str))
711+
.add1("link", (a: Link, ctx: Context): Literal => {
712+
if (a.display) {
713+
return display(ctx, a.display);
714+
} else {
715+
return Values.toString(a, ctx.settings).replace(/\[\[.*\|(.*)\]\]/, "$1")
716+
}
717+
})
718+
.add1("*", (a: Literal, ctx: Context): Literal => {
719+
return Values.toString(a, ctx.settings);
720+
})
721+
.build();
722+
704723
export const choice = new FunctionBuilder("choice")
705724
.add3("*", "*", "*", (b, left, right) => (Values.isTruthy(b) ? left : right))
706725
.vectorize(3, [0])
@@ -930,6 +949,7 @@ export const DEFAULT_FUNCTIONS: Record<string, FunctionImpl> = {
930949
// Utility Operations
931950
default: DefaultFunctions.fdefault,
932951
ldefault: DefaultFunctions.ldefault,
952+
display: DefaultFunctions.display,
933953
choice: DefaultFunctions.choice,
934954
striptime: DefaultFunctions.striptime,
935955
dateformat: DefaultFunctions.dateformat,

src/test/function/functions.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,25 @@ describe("sort()", () => {
104104
});
105105
});
106106

107+
// <-- display() -->
108+
109+
test("Evaluate display()", () => {
110+
expect(parseEval('display("test")')).toEqual("test");
111+
expect(parseEval('display("[displayname](http://example.com)")')).toEqual("displayname");
112+
expect(parseEval('display("[[test]]")')).toEqual("test");
113+
expect(parseEval('display("[[test|displayname]]")')).toEqual("displayname");
114+
expect(parseEval('display("long [[test]] **with** [[test2|multiple]] [links](http://example.com)")')).toEqual("long test with multiple links");
115+
expect(parseEval('display(1)')).toEqual("1");
116+
expect(parseEval('display(true)')).toEqual("true");
117+
expect(parseEval('display(null)')).toEqual("");
118+
expect(parseEval('display(date("2024-11-18"))')).toEqual("November 18, 2024");
119+
expect(parseEval('display(dur("7 hours"))')).toEqual("7 hours");
120+
expect(parseEval('display(link("path/to/file.md"))')).toEqual("file");
121+
expect(parseEval('display(link("path/to/file.md", "displayname"))')).toEqual("displayname");
122+
expect(parseEval('display(list("test", 2, link("file.md")))')).toEqual('test, 2, file');
123+
});
124+
125+
107126
// <-- default() -->
108127

109128
test("Evaluate default()", () => {

src/util/normalize.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Result } from "api/result";
33
import * as P from "parsimmon";
44
import emojiRegex from "emoji-regex";
55
import { QuerySettings } from "settings";
6+
import removeMd from "remove-markdown";
67

78
/** Normalize a duration to all of the proper units. */
89
export function normalizeDuration(dur: Duration) {
@@ -159,3 +160,17 @@ export function setsEqual<T>(first: Set<T>, second: Set<T>): boolean {
159160

160161
return true;
161162
}
163+
164+
/** Normalize a markdown string. Removes all markdown tags and obsidian links. */
165+
export function normalizeMarkdown(str: string): string {
166+
// [[test]] -> test
167+
let interim = str.replace(/\[\[([^\|]*?)\]\]/g, "$1")
168+
169+
// [[test|test]] -> test
170+
interim = interim.replace(/\[\[.*?\|(.*?)\]\]/, "$1")
171+
172+
// remove markdown tags
173+
interim = removeMd(interim);
174+
175+
return interim
176+
}

0 commit comments

Comments
 (0)