paranext / paranext-core

Electron client, extension host, and C# library for Paranext
https://paranext.github.io/paranext-core/
MIT License
14 stars 3 forks source link

Checks: Dev design and implement anatomy of a check #449

Closed katherinejensen00 closed 3 days ago

katherinejensen00 commented 9 months ago

As an extension developer, I need to be able to create a custom check so that I can find the things I care about when proofing a text.

Design the anatomy of a check. Consider things like character and punctuation inventories (will these come through data providers?) and how will a check be run

FoolRunning commented 9 months ago

I'm not sure of where the best place to put this, but the following is some sample code that can be used to run the checks in C#. The ParatextChecks NuGet package will be needed.

const string projectName = "TPTS";

// NOTE: Platform.Bible already does this initialization stuff.
Console.WriteLine("Initializing...");
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
ParatextData.Initialize();

Console.WriteLine("Running Check(s)...");
ScrText scr = ScrTextCollection.Get(projectName);
ChapterVerseCheck check = new();
List<VerseListItem> errors = RunBasicChecks.Run(new List<ScriptureCheckBase> { check }, scr, BookSet.AllBooks, false);

if (errors.Count == 0)
    Console.WriteLine("No Errors Found!!!!");
else
{
    Console.WriteLine("Errors Found :(");
    foreach (VerseListItem item in errors)
        Console.WriteLine($"   {item.VerseReference} {item.Message}");
}
tjcouch-sil commented 1 month ago

Playing with TypeScript types to determine if project interfaces seem like a viable replacement for project types:

Put this in `hello-world-2.web-view.tsx` or some other reasonable location

```typescript const runGarbage = false; // @ts-ignore globalThis.runGarbage = runGarbage; // @ts-ignore if (globalThis.runGarbage) { /* eslint-disable @typescript-eslint/no-unused-vars */ // Thanks to jcalz at https://stackoverflow.com/a/50375286 // eslint-disable-next-line @typescript-eslint/no-explicit-any type UnionToIntersection = (U extends any ? (x: U) => void : never) extends ( x: infer I, ) => void ? I : never; // eslint-disable-next-line no-type-assertion/no-type-assertion const useProjectDataProviderFromInterfaces = (() => {}) as unknown as < ProjectTypeArray extends ProjectTypes[], >( projectTypes: ProjectTypeArray, projectDataProviderSource: | string | UnionToIntersection | undefined, ) => UnionToIntersection | undefined; // eslint-disable-next-line react-hooks/rules-of-hooks const pdpI = useProjectDataProviderFromInterfaces( ['ParatextStandard', 'helloWorld', 'platform.notesOnly'], 'asdf', ); const stuff1 = pdpI?.getBookUSJ(new VerseRef(3, 3, 3)); const stuff8 = await pdpI?.setBookUSJ(new VerseRef(3, 3, 3), {}); if (Array.isArray(stuff8) && stuff8.includes('BookUSFM')) logger.log( `The return from setData seems to include only the data types the original PDP had. Don't think this is fixable since the PDPs would probably have to be generic and have the other data types put in, but object types in interfaces can't be generic`, ); // eslint-disable-next-line react-hooks/rules-of-hooks const pdp = useProjectDataProvider('ParatextStandard', 'asdf'); const stuff2 = pdp?.getBookUSJ(new VerseRef(3, 3, 3)); const doublePDP = useProjectDataProviderFromInterfaces( ['platform.notesOnly', 'platform.secondNotesOnly', 'platform.NotesAndPlaceholder'], 'asfd', ); doublePDP?.getNotes(); const stuff9 = await doublePDP?.setNotes('asdf', 'asdf'); if (stuff9 === 'Notes') logger.log( `Looks like the only data types that are available are data types that are common to all the implementers of the function. So when it's just platform.NotesAndPlaceholder, it does let us see Placeholder, but not when combined with these others.`, ); const breakingPDP = useProjectDataProviderFromInterfaces( ['platform.notesOnly', 'platform.breakingNotesOnlySameSelector'], 'asf', ); // Seems to choose one return type arbitrarily const stuff5 = await breakingPDP?.getNotes('things'); const breakingPDP2 = useProjectDataProviderFromInterfaces( ['platform.notesOnly', 'platform.breakingNotesOnlyDifferentSelector'], 'asf', ); // Actually has overloads const stuff6 = await breakingPDP2?.getNotes('asdf'); const stuff7 = await breakingPDP2?.getNotes({ stuff: 3 }); type UseProjectDataFromInterfacesHook = { ( projectTypes: ProjectTypeArray, projectDataProviderSource: | string | UnionToIntersection | undefined, ): { [TDataType in keyof UnionToIntersection]: ( // @ts-ignore TypeScript pretends it can't find `selector`, but it works just fine selector: UnionToIntersection< ProjectDataTypes[ProjectTypeArray[number]] >[TDataType]['selector'], // @ts-ignore TypeScript pretends it can't find `getData`, but it works just fine defaultValue: UnionToIntersection< ProjectDataTypes[ProjectTypeArray[number]] >[TDataType]['getData'], subscriberOptions?: DataProviderSubscriberOptions, ) => [ // @ts-ignore TypeScript pretends it can't find `getData`, but it works just fine UnionToIntersection[TDataType]['getData'], ( | (( // @ts-ignore TypeScript pretends it can't find `setData`, but it works just fine newData: UnionToIntersection< ProjectDataTypes[ProjectTypeArray[number]] >[TDataType]['setData'], ) => Promise< DataProviderUpdateInstructions< UnionToIntersection > >) | undefined ), boolean, ]; }; }; // eslint-disable-next-line no-type-assertion/no-type-assertion const useProjectDataFromInterfaces = (() => {}) as unknown as UseProjectDataFromInterfacesHook; // eslint-disable-next-line react-hooks/rules-of-hooks const [stuff3, setStuff3] = useProjectDataFromInterfaces( ['ParatextStandard', 'helloWorld'], 'asdf', ).BookUSJ(); // @ts-expect-error top-level await const thing = await setStuff3?.(15); if (Array.isArray(thing) && thing.includes('BookUSJ')) logger.log('The return from setData seems to work!'); // eslint-disable-next-line react-hooks/rules-of-hooks const [stuff, setStuff] = useProjectData('ParatextStandard', 'asdf').BookUSJ(); // eslint-disable-next-line react-hooks/rules-of-hooks const [doubleStuff, setDoubleStuff] = useProjectDataFromInterfaces( ['platform.notesOnly', 'platform.secondNotesOnly', 'platform.NotesAndPlaceholder'], 'asdf', ).Notes(); // Doesn't seem to work // eslint-disable-next-line react-hooks/rules-of-hooks const [breakingStuff, setBreakingStuff] = useProjectDataFromInterfaces( ['platform.notesOnly', 'platform.breakingNotesOnlySameSelector'], 'asdf', ).Notes('asdf', 'asdf'); // Doesn't seem to work as the params are &'ed together // eslint-disable-next-line react-hooks/rules-of-hooks const [breakingStuff2, setBreakingStuff2] = useProjectDataFromInterfaces( ['platform.notesOnly', 'platform.breakingNotesOnlyDifferentSelector'], 'asdf', ).Notes(); /* eslint-enable */ } ```

tjcouch-sil commented 1 month ago