Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 28 additions & 2 deletions datacore.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@ export class Canvas implements Linkable, File_2, Linkbearing, Taggable, Indexabl
get $file(): string;
// (undocumented)
get $id(): string;
// (undocumented)
$infields: Record<string, InlineField>;
$infieldsMulti: Record<string, InlineFieldList>;
get $link(): Link;
// (undocumented)
$links: Link[];
Expand Down Expand Up @@ -193,8 +193,8 @@ export class CanvasTextCard extends BaseCanvasCard implements Linkbearing, Tagga
$frontmatter?: Record<string, FrontmatterEntry>;
// (undocumented)
$id: string;
// (undocumented)
$infields: Record<string, InlineField>;
$infieldsMulti: Record<string, InlineFieldList>;
// (undocumented)
$links: Link[];
// (undocumented)
Expand Down Expand Up @@ -610,6 +610,7 @@ export namespace Expressions {
export namespace Extractors {
export function frontmatter<T extends Indexable>(front: (object: T) => Record<string, FrontmatterEntry> | undefined): FieldExtractor<T>;
export function inlineFields<T extends Indexable>(inlineMap: (object: T) => Record<string, InlineField> | undefined): FieldExtractor<T>;
export function inlineFieldsMulti<T extends Indexable>(inlineMap: (object: T) => Record<string, InlineFieldList> | undefined): FieldExtractor<T>;
export function intrinsics<T extends Indexable>(except?: Set<string>): FieldExtractor<T>;
export function merge<T extends Fieldbearing>(...extractors: FieldExtractor<T>[]): FieldExtractor<T>;
}
Expand Down Expand Up @@ -941,6 +942,9 @@ export interface InlineField {
wrapping?: string;
}

// @public
export type InlineFieldList = InlineField[];

// @public
export type Intent = "error" | "warn" | "info" | "success";

Expand All @@ -955,6 +959,24 @@ export const INTENT_CLASSES: Record<Intent, string>;
// @internal
export function jsonFrontmatterEntry(raw: FrontmatterEntry): JsonFrontmatterEntry;

// @public
export interface JsonInlineField {
key: string;
position: {
line: number;
start: number;
startValue: number;
end: number;
};
raw: string;
// Warning: (ae-forgotten-export) The symbol "JsonLiteral" needs to be exported by the entry point index.d.ts
value: JsonLiteral;
wrapping?: string;
}

// @public
export type JsonInlineFieldList = JsonInlineField[];

// @public
export interface LambdaExpression {
arguments: string[];
Expand Down Expand Up @@ -1124,6 +1146,7 @@ export class MarkdownBlock implements Indexable, Linkbearing, Taggable, Fieldbea
// (undocumented)
$id: string;
$infields: Record<string, InlineField>;
$infieldsMulti: Record<string, InlineFieldList>;
get $link(): Link | undefined;
$links: Link[];
$ordinal: number;
Expand Down Expand Up @@ -1275,6 +1298,7 @@ export class MarkdownListItem implements Indexable, Linkbearing, Taggable, Field
// (undocumented)
$id: string;
$infields: Record<string, InlineField>;
$infieldsMulti: Record<string, InlineFieldList>;
get $line(): number;
get $lineCount(): number;
$links: Link[];
Expand Down Expand Up @@ -1323,6 +1347,7 @@ export class MarkdownPage implements File_2, Linkbearing, Taggable, Indexable, F
// (undocumented)
get $id(): string;
$infields: Record<string, InlineField>;
$infieldsMulti: Record<string, InlineFieldList>;
get $lineCount(): number;
get $link(): Link;
$links: Link[];
Expand Down Expand Up @@ -1366,6 +1391,7 @@ export class MarkdownSection implements Indexable, Taggable, Linkable, Linkbeari
// (undocumented)
$id: string;
$infields: Record<string, InlineField>;
$infieldsMulti: Record<string, InlineFieldList>;
$level: number;
get $lineCount(): number;
get $link(): Link;
Expand Down
4 changes: 2 additions & 2 deletions manifest.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"id": "datacore",
"name": "Datacore",
"version": "0.1.28",
"version": "0.1.29",
"minAppVersion": "1.4.11",
"description": "Reactive query engine backed by Javascript or a custom query language.",
"author": "Michael Brenan",
"authorUrl": "https://github.com/blacksmithgu",
"isDesktopOnly": false
}
}
66 changes: 65 additions & 1 deletion src/expression/field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*/
import { DataObject, Literal, Literals } from "expression/literal";
import { Indexable } from "../index/types/indexable";
import { InlineField } from "index/import/inline-field";
import { InlineField, InlineFieldList } from "index/import/inline-field";
import { FrontmatterEntry } from "index/types/markdown";

/** The source of a field, used when determining what files to overwrite and how. */
Expand Down Expand Up @@ -221,6 +221,70 @@ export namespace Extractors {
};
}

/** Field extractor which shows all inline fields from a multi-value inline-field map.
* Values are always lists in appearance order.
*/
export function inlineFieldsMulti<T extends Indexable>(
inlineMap: (object: T) => Record<string, InlineFieldList> | undefined
): FieldExtractor<T> {
return (object: T, key?: string) => {
const map = inlineMap(object);
if (!map) return [];

function aggregate(fields: InlineFieldList): { key: string; value: Literal; raw?: string; line: number } {
if (fields.length == 0) return { key: "", value: [], raw: "", line: 0 };
const first = fields[0];
return {
key: first.key,
value: fields.map((f) => f.value),
raw: fields.map((f) => f.raw).join(", "),
line: first.position.line,
};
}

if (key == null) {
const out: Field[] = [];
for (const fields of Object.values(map)) {
const agg = aggregate(fields);
if (!agg.key) continue;

out.push({
key: agg.key.toLowerCase(),
value: agg.value,
raw: agg.raw,
provenance: {
type: "inline-field",
file: object.$file!,
line: agg.line,
key: agg.key,
revision: object.$revision ?? 0,
},
});
}
return out;
} else {
key = key.toLowerCase();
if (!(key in map)) return [];

const agg = aggregate(map[key]);
return [
{
key,
value: agg.value,
raw: agg.raw,
provenance: {
type: "inline-field",
file: object.$file!,
line: agg.line,
key: agg.key,
revision: object.$revision ?? 0,
},
},
];
}
};
}

/** Merge multiple field extractors into one; if multiple extractors produce identical keys, keys from the earlier extractor will be preferred. */
export function merge<T extends Fieldbearing>(...extractors: FieldExtractor<T>[]): FieldExtractor<T> {
return (object: T, key?: string) => {
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export type * from "index/types/index-query";
export type * from "index/types/files";
export type * from "index/types/canvas";

export { InlineField } from "index/import/inline-field";
export type { InlineField, InlineFieldList, JsonInlineField, JsonInlineFieldList } from "index/import/inline-field";
export { SearchResult } from "index/datastore";

export { CardPos, CardDimensions } from "index/types/json/canvas";
Expand Down
11 changes: 8 additions & 3 deletions src/index/import/canvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ export function canvasImport(
canvas.card(card);
for (const tag in metadata.tags) canvas.metadata.tag(tag);
for (const link of metadata.links ?? []) canvas.metadata.link(link);
for (const field of iterateInlineFields(lines)) canvas.metadata.inlineField(field);
for (const field of iterateInlineFields(lines)) {
canvas.metadata.inlineField(field);
canvas.metadata.inlineFieldMulti(field);
}
} else {
const card = new CanvasCardData(path, c.id, c);
canvas.card(card);
Expand All @@ -50,7 +53,7 @@ abstract class AbstractCanvasCardData {
public path: string,
public id: string,
protected nodeJson: CanvasTextData | CanvasLinkData | CanvasFileData
) {}
) { }

public build(): JsonBaseCanvasCard {
return {
Expand Down Expand Up @@ -93,6 +96,7 @@ export class CanvasCardData extends AbstractCanvasCardData {
return {
...(super.build() as JsonBaseCanvasCard),
$infields: this.metadata.finishInlineFields(),
$infieldsMulti: this.metadata.finishInlineFieldsMulti(),
$frontmatter: this.frontmatter,
$sections: this.sections.map((x) => x.build()),
$tags: this.metadata.finishTags(),
Expand Down Expand Up @@ -120,7 +124,7 @@ export class CanvasData {
public cards: CanvasCardData[] = [];
public metadata: Metadata = new Metadata();

public constructor(public path: string, public stats: FileStats) {}
public constructor(public path: string, public stats: FileStats) { }

public card(d: CanvasCardData): CanvasCardData {
this.cards.push(d);
Expand All @@ -133,6 +137,7 @@ export class CanvasData {
$ctime: this.stats.ctime,
$mtime: this.stats.mtime,
$infields: this.metadata.finishInlineFields(),
$infieldsMulti: this.metadata.finishInlineFieldsMulti(),
$links: this.metadata.finishLinks(),
$tags: this.metadata.finishTags(),
$path: this.path,
Expand Down
19 changes: 19 additions & 0 deletions src/index/import/inline-field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,16 +68,35 @@ export interface JsonInlineField {
wrapping?: string;
}

/**
* Inline field values may appear multiple times with the same key. When that happens, callers may represent
* them as a list of fields in appearance order.
*/
export type InlineFieldList = InlineField[];

/** JSON, serializable representation of an inline field list (size >= 1 in normal usage). */
export type JsonInlineFieldList = JsonInlineField[];

/** Convert an inline field to a JSON format. */
export function jsonInlineField(field: InlineField): JsonInlineField {
return Object.assign({}, field, { value: JsonConversion.json(field.value) });
}

/** Convert an inline field list to a JSON format. */
export function jsonInlineFieldList(fields: InlineFieldList): JsonInlineFieldList {
return fields.map(jsonInlineField);
}

/** Convert a JSON inline field back to a regular field. */
export function valueInlineField(field: JsonInlineField): InlineField {
return Object.assign({}, field, { value: JsonConversion.value(field.value) });
}

/** Convert a JSON inline field list back to a regular field representation. */
export function valueInlineFieldList(fields: JsonInlineFieldList): InlineFieldList {
return fields.map(valueInlineField);
}

export function asInlineField(local: LocalInlineField, lineno: number): InlineField;
export function asInlineField(local: LocalInlineField[], lineno: number): InlineField[];
/** Convert a local inline field into a full inline field by performing parsing and adding the correct line number. */
Expand Down
Loading