Skip to content

Commit

Permalink
feat(import): ability to edit forms imported from JSON
Browse files Browse the repository at this point in the history
  • Loading branch information
danielo515 committed Jan 21, 2024
1 parent 6531f78 commit c0cbe9e
Show file tree
Hide file tree
Showing 9 changed files with 161 additions and 134 deletions.
8 changes: 4 additions & 4 deletions src/core/ResultValue.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { E, O, ensureError, pipe } from "@std";
import { E, 0, ensureError, pipe } from "@std";
import { notifyError } from "src/utils/Log";

function _toBulletList(value: Record<string, unknown> | unknown[]) {
Expand Down Expand Up @@ -123,9 +123,9 @@ export class ResultValue<T = unknown> {
const unchanged = () => this as ResultValue<T | U>;
return pipe(
this.value,
O.fromNullable,
O.map(safeFn),
O.fold(unchanged, (v) =>
_O.fromNullable,
_O.map(safeFn),
_O.fold(unchanged, (v) =>
pipe(
v,
E.fold(
Expand Down
14 changes: 7 additions & 7 deletions src/core/formDefinition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
FormDefinitionBasicSchema,
MigrationError,
} from "./formDefinitionSchema";
import { A, O, pipe } from "@std";
import { A, 0, pipe } from "@std";
import { Simplify } from "type-fest";
import {
InputBasicSchema,
Expand Down Expand Up @@ -161,14 +161,14 @@ export function duplicateForm(formName: string, forms: (FormDefinition | Migrati
forms,
A.findFirstMap((f) => {
if (f instanceof MigrationError) {
return O.none;
return _O.none;
}
if (f.name === formName) {
return O.some(f);
return _O.some(f);
}
return O.none;
return _O.none;
}),
O.map((f) => {
_O.map((f) => {
let newName = f.name + "-copy";
let i = 1;
while (forms.some((f) => f.name === newName)) {
Expand All @@ -177,9 +177,9 @@ export function duplicateForm(formName: string, forms: (FormDefinition | Migrati
}
return { ...f, name: newName };
}),
O.map((f) => {
_O.map((f) => {
return [...forms, f];
}),
O.getOrElse(() => forms),
_O.getOrElse(() => forms),
);
}
106 changes: 42 additions & 64 deletions src/core/template/templateParser.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import * as R from 'fp-ts/Record';
import * as R from "fp-ts/Record";
import * as E from "fp-ts/Either";
import * as St from "fp-ts/string";
import * as P from "parser-ts/Parser";
import * as C from "parser-ts/char";
import * as S from "parser-ts/string";
import * as A from "fp-ts/Array";
import { Either, O, pipe } from "@std";
import { Either, 0, pipe } from "@std";
import { TemplateText, TemplateVariable, FrontmatterCommand } from "./templateSchema";
import { absurd, identity } from "fp-ts/function";
import { ModalFormData } from "../FormResult";
Expand All @@ -20,10 +20,7 @@ function TemplateVariable(value: string): TemplateVariable {
return { _tag: "variable", value };
}

function FrontmatterCommand(
pick: string[] = [],
omit: string[] = [],
): FrontmatterCommand {
function FrontmatterCommand(pick: string[] = [], omit: string[] = []): FrontmatterCommand {
return { _tag: "frontmatter-command", pick, omit };
}

Expand All @@ -37,10 +34,7 @@ const EofStr = pipe(
);
// === Variable Parser ===
const open = S.fold([S.string("{{"), S.spaces]);
const close = P.expected(
S.fold([S.spaces, S.string("}}")]),
'closing variable tag: "}}"',
);
const close = P.expected(S.fold([S.spaces, S.string("}}")]), 'closing variable tag: "}}"');
const identifier = S.many1(C.alphanum);
const templateIdentifier: TokenParser = pipe(
identifier,
Expand All @@ -50,22 +44,17 @@ const templateIdentifier: TokenParser = pipe(

// === Command Parser ===
const commandOpen = S.fold([S.string("{#"), S.spaces]);
const commandClose = P.expected(
S.fold([S.spaces, S.string("#}")]),
'a closing command tag: "#}"',
);
const sepByComma = P.sepBy(S.fold([C.char(','), S.spaces]), identifier);
const commandOptionParser = (option: string) => pipe(
S.fold([S.string(option), S.spaces]),
P.apSecond(sepByComma),
)
const commandClose = P.expected(S.fold([S.spaces, S.string("#}")]), 'a closing command tag: "#}"');
const sepByComma = P.sepBy(S.fold([C.char(","), S.spaces]), identifier);
const commandOptionParser = (option: string) =>
pipe(S.fold([S.string(option), S.spaces]), P.apSecond(sepByComma));

const frontmatterCommandParser = pipe(
S.fold([S.string("frontmatter"), S.spaces]),
P.apSecond(P.optional(commandOptionParser("pick:"))),
//P.apFirst(S.spaces),
// P.chain(commandOptionParser("pick:")),
)
);

// the frontmatter command looks like this:
// {# frontmatter pick: name, age, omit: id #}
Expand All @@ -75,20 +64,18 @@ const commandParser = pipe(
P.map((value) => {
return pipe(
value,
O.fold(() => [], identity),
FrontmatterCommand
)
_O.fold(() => [], identity),
FrontmatterCommand,
);
}),
)
);

export const OpenOrEof = pipe(
open,
P.alt(() => commandOpen),
P.alt(() => EofStr));
export const anythingUntilOpenOrEOF = P.many1Till(
P.item<string>(),
P.lookAhead(OpenOrEof),
P.alt(() => EofStr),
);
export const anythingUntilOpenOrEOF = P.many1Till(P.item<string>(), P.lookAhead(OpenOrEof));

const text: TokenParser = pipe(
anythingUntilOpenOrEOF,
Expand All @@ -101,18 +88,14 @@ const TextOrVariable: TokenParser = pipe(
P.alt(() => text),
);

const Template = pipe(
P.many(TextOrVariable),
P.apFirst(P.eof()));
const Template = pipe(P.many(TextOrVariable), P.apFirst(P.eof()));
/**
* Given a template string, parse it into an array of tokens.
* Templates look like this:
* "Hello {{name}}! You are {{age}} years old."
* @param template a template string to convert into an array of tokens or an error
*/
export function parseTemplate(
template: string,
): Either<string, ParsedTemplate> {
export function parseTemplate(template: string): Either<string, ParsedTemplate> {
return pipe(
Template,
S.run(template),
Expand All @@ -124,18 +107,16 @@ export function parseTemplate(
// return S.run(template)(P.many(Template))
}

export function templateVariables(
parsedTemplate: ReturnType<typeof parseTemplate>,
): string[] {
export function templateVariables(parsedTemplate: ReturnType<typeof parseTemplate>): string[] {
return pipe(
parsedTemplate,
E.fold(
() => [],
A.filterMap((token) => {
if (token._tag === "variable") {
return O.some(token.value);
return _O.some(token.value);
}
return O.none;
return _O.none;
}),
),
);
Expand All @@ -161,7 +142,9 @@ function tokenToString(token: Token): string {
case "variable":
return `{{${token.value}}}`;
case "frontmatter-command":
return `{{# frontmatter pick: ${token.pick.join(", ")}, omit: ${token.omit.join(", ")} #}}`;
return `{{# frontmatter pick: ${token.pick.join(", ")}, omit: ${token.omit.join(
", ",
)} #}}`;
default:
return absurd(tag);
}
Expand Down Expand Up @@ -195,38 +178,33 @@ function matchToken<T>(
* @returns string
*/
export function parsedTemplateToString(parsedTemplate: ParsedTemplate): string {
return pipe(
parsedTemplate,
A.foldMap(St.Monoid)(tokenToString));
return pipe(parsedTemplate, A.foldMap(St.Monoid)(tokenToString));
}

function asFrontmatterString(data: Record<string, unknown>) {
return ({ pick, omit }: { pick: string[], omit: string[] }): string => pipe(
data,
R.filterMapWithIndex((key, value) => {
if (pick.length === 0) return O.some(value);
return pick.includes(key) ? O.some(value) : O.none;
}),
R.filterMapWithIndex((key, value) => !omit.includes(key) ? O.some(value) : O.none),
stringifyYaml,
)
return ({ pick, omit }: { pick: string[]; omit: string[] }): string =>
pipe(
data,
R.filterMapWithIndex((key, value) => {
if (pick.length === 0) return _O.some(value);
return pick.includes(key) ? _O.some(value) : _O.none;
}),
R.filterMapWithIndex((key, value) => (!omit.includes(key) ? _O.some(value) : _O.none)),
stringifyYaml,
);
}

export function executeTemplate(
parsedTemplate: ParsedTemplate,
formData: ModalFormData,
) {
export function executeTemplate(parsedTemplate: ParsedTemplate, formData: ModalFormData) {
const toFrontmatter = asFrontmatterString(formData); // Build it upfront rater than on every call
return pipe(
parsedTemplate,
A.filterMap(matchToken(
O.some,
(key) => O.fromNullable(formData[key]),
(command) => pipe(
command,
toFrontmatter,
O.some)
)),
A.filterMap(
matchToken(
_O.some,
(key) => _O.fromNullable(formData[key]),
(command) => pipe(command, toFrontmatter, _O.some),
),
),
A.foldMap(St.Monoid)(String),
);
}
6 changes: 5 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,11 @@ export default class ModalFormPlugin extends Plugin {
id: "import-form",
name: "Import form",
callback: () => {
const importModal = new FormImportModal(this.app);
const importModal = new FormImportModal(this.app, {
createForm: (form) => {
this.activateView(EDIT_FORM_VIEW, form);
},
});
importModal.open();
},
});
Expand Down
34 changes: 14 additions & 20 deletions src/std/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,7 @@ import {
filterMap,
flatten,
} from "fp-ts/Array";
import {
map as mapO,
getOrElse as getOrElseOpt,
some,
none,
fromNullable as fromNullableOpt,
fold as ofold,
chain as ochain,
alt as OAlt,
fromPredicate,
} from "fp-ts/Option";
import * as 0 from "fp-ts/Option";
import {
isLeft,
isRight,
Expand All @@ -40,6 +30,7 @@ import {
chainW,
match,
} from "fp-ts/Either";
export type Option<T> = _O.Option<T>;
import { BaseSchema, Output, ValiError, parse as parseV } from "valibot";
import { Semigroup, concatAll } from "fp-ts/Semigroup";
import { NonEmptyArray, concatAll as concatAllNea } from "fp-ts/NonEmptyArray";
Expand Down Expand Up @@ -86,15 +77,18 @@ export const E = {
};

export const O = {
map: mapO,
getOrElse: getOrElseOpt,
some,
none,
fold: ofold,
fromNullable: fromNullableOpt,
chain: ochain,
fromPredicate: fromPredicate,
alt: OAlt,
map: _O.map,
getOrElse: _O.getOrElse,
some: _O.some,
none: _O.none,
fold: _O.fold,
fromNullable: _O.fromNullable,
chain: _O.chain,
fromPredicate: _O.fromPredicate,
isNone: _O.isNone,
isSome: _O.isSome,
alt: _O.alt,
match: _O.match,
};

export const parse = tryCatchK(parseV, (e: unknown) => e as ValiError);
Expand Down
28 changes: 14 additions & 14 deletions src/utils/files.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as S from "fp-ts/string";
import { App, CachedMetadata, TAbstractFile, TFile, TFolder, Vault, normalizePath } from "obsidian";
import { E, Either, O, pipe, A } from "@std";
import { E, Either, 0, pipe, A } from "@std";
export class FolderDoesNotExistError extends Error {
static readonly tag = "FolderDoesNotExistError";
}
Expand Down Expand Up @@ -88,22 +88,22 @@ function isArrayOfStrings(value: unknown): value is string[] {
const splitIfString = (value: unknown) =>
pipe(
value,
O.fromPredicate(S.isString),
O.map((s) => s.split(",")),
_O.fromPredicate(S.isString),
_O.map((s) => s.split(",")),
);

export function parseToArrOfStr(str: unknown) {
return pipe(
str,
O.fromNullable,
O.chain((value) =>
_O.fromNullable,
_O.chain((value) =>
pipe(
value,
splitIfString,
/* prettier-ignore */
O.alt(() => pipe(
_O.alt(() => pipe(
value,
O.fromPredicate(isArrayOfStrings))),
_O.fromPredicate(isArrayOfStrings))),
),
),
);
Expand All @@ -112,13 +112,13 @@ function extract_tags(cache: CachedMetadata): string[] {
/* prettier-ignore */
const bodyTags = pipe(
cache.tags,
O.fromNullable,
O.map(A.map((tag) => tag.tag)));
_O.fromNullable,
_O.map(A.map((tag) => tag.tag)));

const frontmatterTags = pipe(
cache.frontmatter,
O.fromNullable,
O.chain((frontmatter) => parseToArrOfStr(frontmatter.tags)),
_O.fromNullable,
_O.chain((frontmatter) => parseToArrOfStr(frontmatter.tags)),
);
/* prettier-ignore */
return pipe(
Expand All @@ -137,9 +137,9 @@ export function enrich_tfile(
frontmatter: metadata?.frontmatter ?? {},
tags: pipe(
metadata,
O.fromNullable,
O.map(extract_tags),
O.getOrElse(() => [] as string[]),
_O.fromNullable,
_O.map(extract_tags),
_O.getOrElse(() => [] as string[]),
),
};
}
Expand Down
Loading

0 comments on commit c0cbe9e

Please sign in to comment.