mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +03:00
Refactoring: miscellaneous functions
This commit is contained in:
parent
e22c38d9f4
commit
8d240d1360
7
.vscode/settings.json
vendored
7
.vscode/settings.json
vendored
|
@ -32,6 +32,7 @@
|
||||||
],
|
],
|
||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
"ablt",
|
"ablt",
|
||||||
|
"acconcept",
|
||||||
"accs",
|
"accs",
|
||||||
"actv",
|
"actv",
|
||||||
"ADJF",
|
"ADJF",
|
||||||
|
@ -75,11 +76,13 @@
|
||||||
"Litr",
|
"Litr",
|
||||||
"loct",
|
"loct",
|
||||||
"moprho",
|
"moprho",
|
||||||
|
"multiword",
|
||||||
"mypy",
|
"mypy",
|
||||||
"nomn",
|
"nomn",
|
||||||
"nooverlap",
|
"nooverlap",
|
||||||
"NPRO",
|
"NPRO",
|
||||||
"NUMR",
|
"NUMR",
|
||||||
|
"Opencorpora",
|
||||||
"perfectivity",
|
"perfectivity",
|
||||||
"ponomarev",
|
"ponomarev",
|
||||||
"PRCL",
|
"PRCL",
|
||||||
|
@ -87,6 +90,7 @@
|
||||||
"PRTS",
|
"PRTS",
|
||||||
"pssv",
|
"pssv",
|
||||||
"pyconcept",
|
"pyconcept",
|
||||||
|
"Pylance",
|
||||||
"pylint",
|
"pylint",
|
||||||
"pymorphy",
|
"pymorphy",
|
||||||
"Quantor",
|
"Quantor",
|
||||||
|
@ -104,10 +108,13 @@
|
||||||
"signup",
|
"signup",
|
||||||
"Slng",
|
"Slng",
|
||||||
"SMALLPR",
|
"SMALLPR",
|
||||||
|
"tagset",
|
||||||
"tailwindcss",
|
"tailwindcss",
|
||||||
"tanstack",
|
"tanstack",
|
||||||
"toastify",
|
"toastify",
|
||||||
"tooltipic",
|
"tooltipic",
|
||||||
|
"tsdoc",
|
||||||
|
"unknwn",
|
||||||
"Upvote",
|
"Upvote",
|
||||||
"Viewset",
|
"Viewset",
|
||||||
"viewsets",
|
"viewsets",
|
||||||
|
|
|
@ -23,7 +23,7 @@ function Root() {
|
||||||
<NavigationState>
|
<NavigationState>
|
||||||
<div className='min-w-[30rem] clr-app antialiased'>
|
<div className='min-w-[30rem] clr-app antialiased'>
|
||||||
<ConceptToaster
|
<ConceptToaster
|
||||||
className='mt-[4rem] text-sm' //
|
className='mt-[4rem] text-sm' // prettier: split lines
|
||||||
autoClose={3000}
|
autoClose={3000}
|
||||||
draggable={false}
|
draggable={false}
|
||||||
pauseOnFocusLoss={false}
|
pauseOnFocusLoss={false}
|
||||||
|
|
|
@ -18,7 +18,7 @@ function NavigationButton({ icon, title, onClick, text }: NavigationButtonProps)
|
||||||
data-tooltip-content={title}
|
data-tooltip-content={title}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'mr-1 h-full', //
|
'mr-1 h-full', // prettier: split lines
|
||||||
'flex items-center gap-1',
|
'flex items-center gap-1',
|
||||||
'clr-btn-nav',
|
'clr-btn-nav',
|
||||||
'font-controls whitespace-nowrap',
|
'font-controls whitespace-nowrap',
|
||||||
|
|
|
@ -10,7 +10,6 @@ interface RequireAuthProps {
|
||||||
|
|
||||||
function RequireAuth({ children }: RequireAuthProps) {
|
function RequireAuth({ children }: RequireAuthProps) {
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
return children;
|
return children;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -16,7 +16,7 @@ function ConstituentaBadge({ value, prefixID, theme }: ConstituentaBadgeProps) {
|
||||||
<div
|
<div
|
||||||
id={`${prefixID}${value.alias}`}
|
id={`${prefixID}${value.alias}`}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'min-w-[3.1rem] max-w-[3.1rem]', //
|
'min-w-[3.1rem] max-w-[3.1rem]', // prettier: split lines
|
||||||
'px-1',
|
'px-1',
|
||||||
'border rounded-md',
|
'border rounded-md',
|
||||||
'text-center font-medium whitespace-nowrap'
|
'text-center font-medium whitespace-nowrap'
|
||||||
|
|
|
@ -16,7 +16,7 @@ function GrammemeBadge({ key, grammeme }: GrammemeBadgeProps) {
|
||||||
<div
|
<div
|
||||||
key={key}
|
key={key}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'min-w-[3rem]', //
|
'min-w-[3rem]', // prettier: split lines
|
||||||
'px-1',
|
'px-1',
|
||||||
'border rounded-md',
|
'border rounded-md',
|
||||||
'text-sm font-medium text-center whitespace-nowrap'
|
'text-sm font-medium text-center whitespace-nowrap'
|
||||||
|
|
|
@ -22,7 +22,7 @@ function InfoCstStatus({ title }: InfoCstStatusProps) {
|
||||||
<p key={`${prefixes.cst_status_list}${index}`}>
|
<p key={`${prefixes.cst_status_list}${index}`}>
|
||||||
<span
|
<span
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'inline-block', //
|
'inline-block', // prettier: split lines
|
||||||
'min-w-[7rem]',
|
'min-w-[7rem]',
|
||||||
'px-1',
|
'px-1',
|
||||||
'border',
|
'border',
|
||||||
|
|
|
@ -11,9 +11,9 @@ import TextInput from '@/components/Common/TextInput';
|
||||||
import { useLibrary } from '@/context/LibraryContext';
|
import { useLibrary } from '@/context/LibraryContext';
|
||||||
import { useConceptNavigation } from '@/context/NavigationContext';
|
import { useConceptNavigation } from '@/context/NavigationContext';
|
||||||
import { ILibraryItem } from '@/models/library';
|
import { ILibraryItem } from '@/models/library';
|
||||||
|
import { cloneTitle } from '@/models/libraryAPI';
|
||||||
import { IRSFormCreateData } from '@/models/rsform';
|
import { IRSFormCreateData } from '@/models/rsform';
|
||||||
import { classnames } from '@/utils/constants';
|
import { classnames } from '@/utils/constants';
|
||||||
import { cloneTitle } from '@/utils/misc';
|
|
||||||
|
|
||||||
interface DlgCloneLibraryItemProps extends Pick<ModalProps, 'hideWindow'> {
|
interface DlgCloneLibraryItemProps extends Pick<ModalProps, 'hideWindow'> {
|
||||||
base: ILibraryItem;
|
base: ILibraryItem;
|
||||||
|
|
|
@ -11,9 +11,9 @@ import HelpButton from '@/components/Help/HelpButton';
|
||||||
import usePartialUpdate from '@/hooks/usePartialUpdate';
|
import usePartialUpdate from '@/hooks/usePartialUpdate';
|
||||||
import { HelpTopic } from '@/models/miscellaneous';
|
import { HelpTopic } from '@/models/miscellaneous';
|
||||||
import { CstType, ICstCreateData, IRSForm } from '@/models/rsform';
|
import { CstType, ICstCreateData, IRSForm } from '@/models/rsform';
|
||||||
|
import { generateAlias, validateNewAlias } from '@/models/rsformAPI';
|
||||||
import { inferTemplatedType, substituteTemplateArgs } from '@/models/rslangAPI';
|
import { inferTemplatedType, substituteTemplateArgs } from '@/models/rslangAPI';
|
||||||
import { classnames } from '@/utils/constants';
|
import { classnames } from '@/utils/constants';
|
||||||
import { createAliasFor, validateCstAlias } from '@/utils/misc';
|
|
||||||
|
|
||||||
import ArgumentsTab, { IArgumentsState } from './ArgumentsTab';
|
import ArgumentsTab, { IArgumentsState } from './ArgumentsTab';
|
||||||
import ConstituentaTab from './ConstituentaTab';
|
import ConstituentaTab from './ConstituentaTab';
|
||||||
|
@ -54,11 +54,11 @@ function DlgConstituentaTemplate({ hideWindow, schema, onCreate, insertAfter }:
|
||||||
const handleSubmit = () => onCreate(constituenta);
|
const handleSubmit = () => onCreate(constituenta);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
updateConstituenta({ alias: createAliasFor(constituenta.cst_type, schema) });
|
updateConstituenta({ alias: generateAlias(constituenta.cst_type, schema) });
|
||||||
}, [constituenta.cst_type, updateConstituenta, schema]);
|
}, [constituenta.cst_type, updateConstituenta, schema]);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
setValidated(validateCstAlias(constituenta.alias, constituenta.cst_type, schema));
|
setValidated(validateNewAlias(constituenta.alias, constituenta.cst_type, schema));
|
||||||
}, [constituenta.alias, constituenta.cst_type, schema]);
|
}, [constituenta.alias, constituenta.cst_type, schema]);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
|
|
|
@ -10,9 +10,9 @@ import TextInput from '@/components/Common/TextInput';
|
||||||
import RSInput from '@/components/RSInput';
|
import RSInput from '@/components/RSInput';
|
||||||
import usePartialUpdate from '@/hooks/usePartialUpdate';
|
import usePartialUpdate from '@/hooks/usePartialUpdate';
|
||||||
import { CstType, ICstCreateData, IRSForm } from '@/models/rsform';
|
import { CstType, ICstCreateData, IRSForm } from '@/models/rsform';
|
||||||
|
import { generateAlias, validateNewAlias } from '@/models/rsformAPI';
|
||||||
import { classnames } from '@/utils/constants';
|
import { classnames } from '@/utils/constants';
|
||||||
import { labelCstType } from '@/utils/labels';
|
import { labelCstType } from '@/utils/labels';
|
||||||
import { createAliasFor, validateCstAlias } from '@/utils/misc';
|
|
||||||
import { SelectorCstType } from '@/utils/selectors';
|
import { SelectorCstType } from '@/utils/selectors';
|
||||||
|
|
||||||
interface DlgCreateCstProps extends Pick<ModalProps, 'hideWindow'> {
|
interface DlgCreateCstProps extends Pick<ModalProps, 'hideWindow'> {
|
||||||
|
@ -39,11 +39,11 @@ function DlgCreateCst({ hideWindow, initial, schema, onCreate }: DlgCreateCstPro
|
||||||
const handleSubmit = () => onCreate(cstData);
|
const handleSubmit = () => onCreate(cstData);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
updateCstData({ alias: createAliasFor(cstData.cst_type, schema) });
|
updateCstData({ alias: generateAlias(cstData.cst_type, schema) });
|
||||||
}, [cstData.cst_type, updateCstData, schema]);
|
}, [cstData.cst_type, updateCstData, schema]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setValidated(validateCstAlias(cstData.alias, cstData.cst_type, schema));
|
setValidated(validateNewAlias(cstData.alias, cstData.cst_type, schema));
|
||||||
}, [cstData.alias, cstData.cst_type, schema]);
|
}, [cstData.alias, cstData.cst_type, schema]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -53,7 +53,7 @@ function SyntacticTab({ initial, setIsValid, setReference }: SyntacticTabProps)
|
||||||
onChange={event => setOffset(event.target.valueAsNumber)}
|
onChange={event => setOffset(event.target.valueAsNumber)}
|
||||||
/>
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
disabled //
|
disabled // prettier: split lines
|
||||||
dense
|
dense
|
||||||
noBorder
|
noBorder
|
||||||
label='Основная ссылка'
|
label='Основная ссылка'
|
||||||
|
|
|
@ -9,8 +9,8 @@ import TextInput from '@/components/Common/TextInput';
|
||||||
import { useRSForm } from '@/context/RSFormContext';
|
import { useRSForm } from '@/context/RSFormContext';
|
||||||
import usePartialUpdate from '@/hooks/usePartialUpdate';
|
import usePartialUpdate from '@/hooks/usePartialUpdate';
|
||||||
import { CstType, ICstRenameData } from '@/models/rsform';
|
import { CstType, ICstRenameData } from '@/models/rsform';
|
||||||
|
import { generateAlias, validateNewAlias } from '@/models/rsformAPI';
|
||||||
import { labelCstType } from '@/utils/labels';
|
import { labelCstType } from '@/utils/labels';
|
||||||
import { createAliasFor, validateCstAlias } from '@/utils/misc';
|
|
||||||
import { SelectorCstType } from '@/utils/selectors';
|
import { SelectorCstType } from '@/utils/selectors';
|
||||||
|
|
||||||
interface DlgRenameCstProps extends Pick<ModalProps, 'hideWindow'> {
|
interface DlgRenameCstProps extends Pick<ModalProps, 'hideWindow'> {
|
||||||
|
@ -27,13 +27,13 @@ function DlgRenameCst({ hideWindow, initial, onRename }: DlgRenameCstProps) {
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
if (schema && initial && cstData.cst_type !== initial.cst_type) {
|
if (schema && initial && cstData.cst_type !== initial.cst_type) {
|
||||||
updateData({ alias: createAliasFor(cstData.cst_type, schema) });
|
updateData({ alias: generateAlias(cstData.cst_type, schema) });
|
||||||
}
|
}
|
||||||
}, [initial, cstData.cst_type, updateData, schema]);
|
}, [initial, cstData.cst_type, updateData, schema]);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
setValidated(
|
setValidated(
|
||||||
!!schema && cstData.alias !== initial.alias && validateCstAlias(cstData.alias, cstData.cst_type, schema)
|
!!schema && cstData.alias !== initial.alias && validateNewAlias(cstData.alias, cstData.cst_type, schema)
|
||||||
);
|
);
|
||||||
}, [cstData.cst_type, cstData.alias, initial, schema]);
|
}, [cstData.cst_type, cstData.alias, initial, schema]);
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,10 @@ import { useCallback, useState } from 'react';
|
||||||
|
|
||||||
import { type ErrorData } from '@/components/InfoError';
|
import { type ErrorData } from '@/components/InfoError';
|
||||||
import { CstType, IConstituenta, type IRSForm } from '@/models/rsform';
|
import { CstType, IConstituenta, type IRSForm } from '@/models/rsform';
|
||||||
|
import { getDefinitionPrefix } from '@/models/rsformAPI';
|
||||||
import { IArgumentInfo, IExpressionParse } from '@/models/rslang';
|
import { IArgumentInfo, IExpressionParse } from '@/models/rslang';
|
||||||
import { RSErrorType } from '@/models/rslang';
|
import { RSErrorType } from '@/models/rslang';
|
||||||
import { DataCallback, postCheckExpression } from '@/utils/backendAPI';
|
import { DataCallback, postCheckExpression } from '@/utils/backendAPI';
|
||||||
import { getCstExpressionPrefix } from '@/utils/misc';
|
|
||||||
|
|
||||||
const LOGIC_TYPIFICATION = 'LOGIC';
|
const LOGIC_TYPIFICATION = 'LOGIC';
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ function useCheckExpression({ schema }: { schema?: IRSForm }) {
|
||||||
onError: error => setError(error),
|
onError: error => setError(error),
|
||||||
onSuccess: parse => {
|
onSuccess: parse => {
|
||||||
if (activeCst) {
|
if (activeCst) {
|
||||||
adjustResults(parse, expression.trim() === getCstExpressionPrefix(activeCst), activeCst.cst_type);
|
adjustResults(parse, expression.trim() === getDefinitionPrefix(activeCst), activeCst.cst_type);
|
||||||
}
|
}
|
||||||
setParseData(parse);
|
setParseData(parse);
|
||||||
if (onSuccess) onSuccess(parse);
|
if (onSuccess) onSuccess(parse);
|
||||||
|
|
|
@ -16,3 +16,14 @@ export function matchLibraryItem(target: ILibraryItem, query: string): boolean {
|
||||||
const matcher = new TextMatcher(query);
|
const matcher = new TextMatcher(query);
|
||||||
return matcher.test(target.alias) || matcher.test(target.title);
|
return matcher.test(target.alias) || matcher.test(target.title);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate title for clone {@link ILibraryItem}.
|
||||||
|
*/
|
||||||
|
export function cloneTitle(target: ILibraryItem): string {
|
||||||
|
if (!target.title.includes('[клон]')) {
|
||||||
|
return target.title + ' [клон]';
|
||||||
|
} else {
|
||||||
|
return target.title + '+';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* Module: Models for formal representation for systems of concepts.
|
* Module: Models for formal representation for systems of concepts.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Graph } from '@/utils/Graph';
|
import { Graph } from '@/models/Graph';
|
||||||
|
|
||||||
import { ILibraryItemEx, ILibraryUpdateData } from './library';
|
import { ILibraryItemEx, ILibraryUpdateData } from './library';
|
||||||
import { IArgumentInfo, ParsingStatus, ValueClass } from './rslang';
|
import { IArgumentInfo, ParsingStatus, ValueClass } from './rslang';
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* Module: API for formal representation for systems of concepts.
|
* Module: API for formal representation for systems of concepts.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Graph } from '@/utils/Graph';
|
import { Graph } from '@/models/Graph';
|
||||||
import { TextMatcher } from '@/utils/utils';
|
import { TextMatcher } from '@/utils/utils';
|
||||||
|
|
||||||
import { CstMatchMode } from './miscellaneous';
|
import { CstMatchMode } from './miscellaneous';
|
||||||
|
@ -169,23 +169,16 @@ export function inferClass(type: CstType, isTemplate: boolean): CstClass {
|
||||||
if (isTemplate) {
|
if (isTemplate) {
|
||||||
return CstClass.TEMPLATE;
|
return CstClass.TEMPLATE;
|
||||||
}
|
}
|
||||||
|
// prettier-ignore
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case CstType.BASE:
|
case CstType.BASE: return CstClass.BASIC;
|
||||||
return CstClass.BASIC;
|
case CstType.CONSTANT: return CstClass.BASIC;
|
||||||
case CstType.CONSTANT:
|
case CstType.STRUCTURED: return CstClass.BASIC;
|
||||||
return CstClass.BASIC;
|
case CstType.TERM: return CstClass.DERIVED;
|
||||||
case CstType.STRUCTURED:
|
case CstType.FUNCTION: return CstClass.DERIVED;
|
||||||
return CstClass.BASIC;
|
case CstType.AXIOM: return CstClass.STATEMENT;
|
||||||
case CstType.TERM:
|
case CstType.PREDICATE: return CstClass.DERIVED;
|
||||||
return CstClass.DERIVED;
|
case CstType.THEOREM: return CstClass.STATEMENT;
|
||||||
case CstType.FUNCTION:
|
|
||||||
return CstClass.DERIVED;
|
|
||||||
case CstType.AXIOM:
|
|
||||||
return CstClass.STATEMENT;
|
|
||||||
case CstType.PREDICATE:
|
|
||||||
return CstClass.DERIVED;
|
|
||||||
case CstType.THEOREM:
|
|
||||||
return CstClass.STATEMENT;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,9 +220,58 @@ export function isMockCst(cst: IConstituenta) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: description
|
* Apply filter based on start {@link IConstituenta} type.
|
||||||
*/
|
*/
|
||||||
export function applyFilterCategory(start: IConstituenta, schema: IRSFormData): IConstituenta[] {
|
export function applyFilterCategory(start: IConstituenta, schema: IRSFormData): IConstituenta[] {
|
||||||
const nextCategory = schema.items.find(cst => cst.order > start.order && cst.cst_type === CATEGORY_CST_TYPE);
|
const nextCategory = schema.items.find(cst => cst.order > start.order && cst.cst_type === CATEGORY_CST_TYPE);
|
||||||
return schema.items.filter(cst => cst.order > start.order && (!nextCategory || cst.order <= nextCategory.order));
|
return schema.items.filter(cst => cst.order > start.order && (!nextCategory || cst.order <= nextCategory.order));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prefix for alias indicating {@link CstType}.
|
||||||
|
*/
|
||||||
|
export function getCstTypePrefix(type: CstType) {
|
||||||
|
// prettier-ignore
|
||||||
|
switch (type) {
|
||||||
|
case CstType.BASE: return 'X';
|
||||||
|
case CstType.CONSTANT: return 'C';
|
||||||
|
case CstType.STRUCTURED: return 'S';
|
||||||
|
case CstType.AXIOM: return 'A';
|
||||||
|
case CstType.TERM: return 'D';
|
||||||
|
case CstType.FUNCTION: return 'F';
|
||||||
|
case CstType.PREDICATE: return 'P';
|
||||||
|
case CstType.THEOREM: return 'T';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate new alias against {@link CstType} and {@link IRSForm}.
|
||||||
|
*/
|
||||||
|
export function validateNewAlias(alias: string, type: CstType, schema: IRSForm): boolean {
|
||||||
|
return alias.length >= 2 && alias[0] == getCstTypePrefix(type) && !schema.items.find(cst => cst.alias === alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Definition prefix for {@link IConstituenta}.
|
||||||
|
*/
|
||||||
|
export function getDefinitionPrefix(cst: IConstituenta): string {
|
||||||
|
return cst.alias + (cst.cst_type === CstType.STRUCTURED ? '::=' : ':==');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate alias for new {@link IConstituenta} of a given {@link CstType} for current {@link IRSForm}.
|
||||||
|
*/
|
||||||
|
export function generateAlias(type: CstType, schema: IRSForm): string {
|
||||||
|
const prefix = getCstTypePrefix(type);
|
||||||
|
if (!schema.items || schema.items.length <= 0) {
|
||||||
|
return `${prefix}1`;
|
||||||
|
}
|
||||||
|
const index = schema.items.reduce((prev, cst, index) => {
|
||||||
|
if (cst.cst_type !== type) {
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
index = Number(cst.alias.slice(1 - cst.alias.length)) + 1;
|
||||||
|
return Math.max(prev, index);
|
||||||
|
}, 1);
|
||||||
|
return `${prefix}${index}`;
|
||||||
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
import { applyPattern } from '@/utils/utils';
|
import { applyPattern } from '@/utils/utils';
|
||||||
|
|
||||||
import { CstType } from './rsform';
|
import { CstType } from './rsform';
|
||||||
import { IArgumentValue, RSErrorClass, RSErrorType } from './rslang';
|
import { IArgumentValue, IRSErrorDescription, RSErrorClass, RSErrorType } from './rslang';
|
||||||
|
|
||||||
const LOCALS_REGEXP = /[_a-zα-ω][a-zα-ω]*\d*/g;
|
const LOCALS_REGEXP = /[_a-zα-ω][a-zα-ω]*\d*/g;
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ const LOCALS_REGEXP = /[_a-zα-ω][a-zα-ω]*\d*/g;
|
||||||
* Extracts global variable names from a given expression.
|
* Extracts global variable names from a given expression.
|
||||||
*/
|
*/
|
||||||
export function extractGlobals(expression: string): Set<string> {
|
export function extractGlobals(expression: string): Set<string> {
|
||||||
|
// cspell:disable-next-line
|
||||||
return new Set(expression.match(/[XCSADFPT]\d+/g) ?? []);
|
return new Set(expression.match(/[XCSADFPT]\d+/g) ?? []);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,3 +120,17 @@ export function inferErrorClass(error: RSErrorType): RSErrorClass {
|
||||||
return RSErrorClass.UNKNOWN;
|
return RSErrorClass.UNKNOWN;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate ErrorID label.
|
||||||
|
*/
|
||||||
|
export function getRSErrorPrefix(error: IRSErrorDescription): string {
|
||||||
|
const id = error.errorType.toString(16);
|
||||||
|
// prettier-ignore
|
||||||
|
switch(inferErrorClass(error.errorType)) {
|
||||||
|
case RSErrorClass.LEXER: return 'L' + id;
|
||||||
|
case RSErrorClass.PARSER: return 'P' + id;
|
||||||
|
case RSErrorClass.SEMANTIC: return 'S' + id;
|
||||||
|
case RSErrorClass.UNKNOWN: return 'U' + id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -109,8 +109,8 @@ function CreateRSFormPage() {
|
||||||
label='Сокращение'
|
label='Сокращение'
|
||||||
placeholder={file && 'Загрузить из файла'}
|
placeholder={file && 'Загрузить из файла'}
|
||||||
className='w-[14rem]'
|
className='w-[14rem]'
|
||||||
pattern={patterns.alias}
|
pattern={patterns.library_alias}
|
||||||
title={`не более ${limits.alias_len} символов`}
|
title={`не более ${limits.library_alias_len} символов`}
|
||||||
value={alias}
|
value={alias}
|
||||||
onChange={event => setAlias(event.target.value)}
|
onChange={event => setAlias(event.target.value)}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -46,7 +46,7 @@ function SearchPanel({ total, filtered, query, setQuery, strategy, setFilter }:
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'sticky top-0', //
|
'sticky top-0', // prettier: split lines
|
||||||
'w-full max-h-[2.3rem]',
|
'w-full max-h-[2.3rem]',
|
||||||
'pr-40 flex',
|
'pr-40 flex',
|
||||||
'border-b',
|
'border-b',
|
||||||
|
@ -55,7 +55,7 @@ function SearchPanel({ total, filtered, query, setQuery, strategy, setFilter }:
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'min-w-[10rem]', //
|
'min-w-[10rem]', // prettier: split lines
|
||||||
'px-2 self-center',
|
'px-2 self-center',
|
||||||
'select-none',
|
'select-none',
|
||||||
'whitespace-nowrap'
|
'whitespace-nowrap'
|
||||||
|
|
|
@ -17,10 +17,10 @@ import DlgShowAST from '@/dialogs/DlgShowAST';
|
||||||
import useCheckExpression from '@/hooks/useCheckExpression';
|
import useCheckExpression from '@/hooks/useCheckExpression';
|
||||||
import useLocalStorage from '@/hooks/useLocalStorage';
|
import useLocalStorage from '@/hooks/useLocalStorage';
|
||||||
import { IConstituenta } from '@/models/rsform';
|
import { IConstituenta } from '@/models/rsform';
|
||||||
|
import { getDefinitionPrefix } from '@/models/rsformAPI';
|
||||||
import { IExpressionParse, IRSErrorDescription, SyntaxTree } from '@/models/rslang';
|
import { IExpressionParse, IRSErrorDescription, SyntaxTree } from '@/models/rslang';
|
||||||
import { TokenID } from '@/models/rslang';
|
import { TokenID } from '@/models/rslang';
|
||||||
import { labelTypification } from '@/utils/labels';
|
import { labelTypification } from '@/utils/labels';
|
||||||
import { getCstExpressionPrefix } from '@/utils/misc';
|
|
||||||
|
|
||||||
import ParsingResult from './ParsingResult';
|
import ParsingResult from './ParsingResult';
|
||||||
import RSEditorControls from './RSEditControls';
|
import RSEditorControls from './RSEditControls';
|
||||||
|
@ -78,7 +78,7 @@ function EditorRSExpression({
|
||||||
if (!activeCst) {
|
if (!activeCst) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const prefix = getCstExpressionPrefix(activeCst);
|
const prefix = getDefinitionPrefix(activeCst);
|
||||||
const expression = prefix + value;
|
const expression = prefix + value;
|
||||||
checkExpression(expression, activeCst, parse => {
|
checkExpression(expression, activeCst, parse => {
|
||||||
if (parse.errors.length > 0) {
|
if (parse.errors.length > 0) {
|
||||||
|
@ -103,7 +103,7 @@ function EditorRSExpression({
|
||||||
if (!activeCst || !rsInput.current) {
|
if (!activeCst || !rsInput.current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const prefix = getCstExpressionPrefix(activeCst);
|
const prefix = getDefinitionPrefix(activeCst);
|
||||||
let errorPosition = error.position - prefix.length;
|
let errorPosition = error.position - prefix.length;
|
||||||
if (errorPosition < 0) errorPosition = 0;
|
if (errorPosition < 0) errorPosition = 0;
|
||||||
rsInput.current?.view?.dispatch({
|
rsInput.current?.view?.dispatch({
|
||||||
|
@ -137,7 +137,7 @@ function EditorRSExpression({
|
||||||
toast.error('Невозможно построить дерево разбора');
|
toast.error('Невозможно построить дерево разбора');
|
||||||
} else {
|
} else {
|
||||||
setSyntaxTree(parse.ast);
|
setSyntaxTree(parse.ast);
|
||||||
setExpression(getCstExpressionPrefix(activeCst!) + value);
|
setExpression(getDefinitionPrefix(activeCst!) + value);
|
||||||
setShowAST(true);
|
setShowAST(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,9 +4,9 @@ import clsx from 'clsx';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
|
|
||||||
import { IExpressionParse, IRSErrorDescription } from '@/models/rslang';
|
import { IExpressionParse, IRSErrorDescription } from '@/models/rslang';
|
||||||
|
import { getRSErrorPrefix } from '@/models/rslangAPI';
|
||||||
import { animateParseResults } from '@/utils/animations';
|
import { animateParseResults } from '@/utils/animations';
|
||||||
import { describeRSError } from '@/utils/labels';
|
import { describeRSError } from '@/utils/labels';
|
||||||
import { getRSErrorPrefix } from '@/utils/misc';
|
|
||||||
|
|
||||||
interface ParsingResultProps {
|
interface ParsingResultProps {
|
||||||
data: IExpressionParse | undefined;
|
data: IExpressionParse | undefined;
|
||||||
|
|
|
@ -98,8 +98,8 @@ function FormRSForm({ id, disabled, isModified, setIsModified }: FormRSFormProps
|
||||||
required
|
required
|
||||||
label='Сокращение'
|
label='Сокращение'
|
||||||
className='w-[14rem]'
|
className='w-[14rem]'
|
||||||
pattern={patterns.alias}
|
pattern={patterns.library_alias}
|
||||||
title={`не более ${limits.alias_len} символов`}
|
title={`не более ${limits.library_alias_len} символов`}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
value={alias}
|
value={alias}
|
||||||
onChange={event => setAlias(event.target.value)}
|
onChange={event => setAlias(event.target.value)}
|
||||||
|
|
|
@ -11,9 +11,9 @@ import HelpButton from '@/components/Help/HelpButton';
|
||||||
import useDropdown from '@/hooks/useDropdown';
|
import useDropdown from '@/hooks/useDropdown';
|
||||||
import { HelpTopic } from '@/models/miscellaneous';
|
import { HelpTopic } from '@/models/miscellaneous';
|
||||||
import { CstType } from '@/models/rsform';
|
import { CstType } from '@/models/rsform';
|
||||||
|
import { getCstTypePrefix } from '@/models/rsformAPI';
|
||||||
import { prefixes } from '@/utils/constants';
|
import { prefixes } from '@/utils/constants';
|
||||||
import { labelCstType } from '@/utils/labels';
|
import { getCstTypeShortcut, labelCstType } from '@/utils/labels';
|
||||||
import { getCstTypePrefix, getCstTypeShortcut } from '@/utils/misc';
|
|
||||||
|
|
||||||
interface RSListToolbarProps {
|
interface RSListToolbarProps {
|
||||||
isMutable?: boolean;
|
isMutable?: boolean;
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { useLayoutEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { GraphFilterParams } from '@/models/miscellaneous';
|
import { GraphFilterParams } from '@/models/miscellaneous';
|
||||||
import { CstType, IRSForm } from '@/models/rsform';
|
import { CstType, IRSForm } from '@/models/rsform';
|
||||||
import { Graph } from '@/utils/Graph';
|
import { Graph } from '@/models/Graph';
|
||||||
|
|
||||||
function useGraphFilter(schema: IRSForm | undefined, params: GraphFilterParams, toggleUpdate: boolean) {
|
function useGraphFilter(schema: IRSForm | undefined, params: GraphFilterParams, toggleUpdate: boolean) {
|
||||||
const [filtered, setFiltered] = useState<Graph>(new Graph());
|
const [filtered, setFiltered] = useState<Graph>(new Graph());
|
||||||
|
|
|
@ -28,8 +28,8 @@ import DlgUploadRSForm from '@/dialogs/DlgUploadRSForm';
|
||||||
import useQueryStrings from '@/hooks/useQueryStrings';
|
import useQueryStrings from '@/hooks/useQueryStrings';
|
||||||
import { UserAccessMode } from '@/models/miscellaneous';
|
import { UserAccessMode } from '@/models/miscellaneous';
|
||||||
import { IConstituenta, ICstCreateData, ICstRenameData, ICstUpdateData, TermForm } from '@/models/rsform';
|
import { IConstituenta, ICstCreateData, ICstRenameData, ICstUpdateData, TermForm } from '@/models/rsform';
|
||||||
|
import { generateAlias } from '@/models/rsformAPI';
|
||||||
import { EXTEOR_TRS_FILE, prefixes, TIMEOUT_UI_REFRESH } from '@/utils/constants';
|
import { EXTEOR_TRS_FILE, prefixes, TIMEOUT_UI_REFRESH } from '@/utils/constants';
|
||||||
import { createAliasFor } from '@/utils/misc';
|
|
||||||
|
|
||||||
import EditorConstituenta from './EditorConstituenta';
|
import EditorConstituenta from './EditorConstituenta';
|
||||||
import EditorRSForm from './EditorRSForm';
|
import EditorRSForm from './EditorRSForm';
|
||||||
|
@ -179,7 +179,7 @@ function RSTabs() {
|
||||||
if (!schema?.items) {
|
if (!schema?.items) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
data.alias = data.alias || createAliasFor(data.cst_type, schema);
|
data.alias = data.alias || generateAlias(data.cst_type, schema);
|
||||||
cstCreate(data, newCst => {
|
cstCreate(data, newCst => {
|
||||||
toast.success(`Конституента добавлена: ${newCst.alias}`);
|
toast.success(`Конституента добавлена: ${newCst.alias}`);
|
||||||
navigateTab(activeTab, newCst.id);
|
navigateTab(activeTab, newCst.id);
|
||||||
|
|
|
@ -193,7 +193,12 @@ export function domTooltipEntityReference(ref: IEntityReference, cst: IConstitue
|
||||||
parseGrammemes(ref.form).forEach(gramStr => {
|
parseGrammemes(ref.form).forEach(gramStr => {
|
||||||
const gram = document.createElement('div');
|
const gram = document.createElement('div');
|
||||||
gram.id = `tooltip-${gramStr}`;
|
gram.id = `tooltip-${gramStr}`;
|
||||||
gram.className = clsx('min-w-[3rem]', 'px-1', 'border rounded-md', 'text-sm text-center whitespace-nowrap');
|
gram.className = clsx(
|
||||||
|
'min-w-[3rem]', // prettier: split lines
|
||||||
|
'px-1',
|
||||||
|
'border rounded-md',
|
||||||
|
'text-sm text-center whitespace-nowrap'
|
||||||
|
);
|
||||||
gram.style.borderWidth = '1px';
|
gram.style.borderWidth = '1px';
|
||||||
gram.style.borderColor = colorFgGrammeme(gramStr, colors);
|
gram.style.borderColor = colorFgGrammeme(gramStr, colors);
|
||||||
gram.style.color = colorFgGrammeme(gramStr, colors);
|
gram.style.color = colorFgGrammeme(gramStr, colors);
|
||||||
|
|
|
@ -37,7 +37,7 @@ export const resources = {
|
||||||
* Numeric limitations.
|
* Numeric limitations.
|
||||||
*/
|
*/
|
||||||
export const limits = {
|
export const limits = {
|
||||||
alias_len: 12
|
library_alias_len: 12
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -45,7 +45,7 @@ export const limits = {
|
||||||
*/
|
*/
|
||||||
export const patterns = {
|
export const patterns = {
|
||||||
login: '^[a-zA-Z][a-zA-Z0-9_\\-]{1,}[a-zA-Z0-9]$',
|
login: '^[a-zA-Z][a-zA-Z0-9_\\-]{1,}[a-zA-Z0-9]$',
|
||||||
alias: `.{1,${limits.alias_len}}`
|
library_alias: `.{1,${limits.library_alias_len}}`
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -111,7 +111,25 @@ export function labelToken(id: TokenID): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates description for {@link TokenID}..
|
* Return shortcut description for {@link CstType}.
|
||||||
|
*/
|
||||||
|
export function getCstTypeShortcut(type: CstType) {
|
||||||
|
const prefix = labelCstType(type) + ' [Alt + ';
|
||||||
|
// prettier-ignore
|
||||||
|
switch (type) {
|
||||||
|
case CstType.BASE: return prefix + '1]';
|
||||||
|
case CstType.STRUCTURED: return prefix + '2]';
|
||||||
|
case CstType.TERM: return prefix + '3]';
|
||||||
|
case CstType.AXIOM: return prefix + '4]';
|
||||||
|
case CstType.FUNCTION: return prefix + 'Q]';
|
||||||
|
case CstType.PREDICATE: return prefix + 'W]';
|
||||||
|
case CstType.CONSTANT: return prefix + '5]';
|
||||||
|
case CstType.THEOREM: return prefix + '6]';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates description for {@link TokenID}.
|
||||||
*/
|
*/
|
||||||
export function describeToken(id: TokenID): string {
|
export function describeToken(id: TokenID): string {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
|
|
|
@ -1,81 +0,0 @@
|
||||||
/**
|
|
||||||
* Module: miscellaneous static functions to generate UI resources.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { ILibraryItem } from '@/models/library';
|
|
||||||
import { CstType, IConstituenta, IRSForm } from '@/models/rsform';
|
|
||||||
import { IRSErrorDescription, RSErrorClass } from '@/models/rslang';
|
|
||||||
import { inferErrorClass } from '@/models/rslangAPI';
|
|
||||||
|
|
||||||
import { labelCstType } from './labels';
|
|
||||||
|
|
||||||
export function getCstTypePrefix(type: CstType) {
|
|
||||||
// prettier-ignore
|
|
||||||
switch (type) {
|
|
||||||
case CstType.BASE: return 'X';
|
|
||||||
case CstType.CONSTANT: return 'C';
|
|
||||||
case CstType.STRUCTURED: return 'S';
|
|
||||||
case CstType.AXIOM: return 'A';
|
|
||||||
case CstType.TERM: return 'D';
|
|
||||||
case CstType.FUNCTION: return 'F';
|
|
||||||
case CstType.PREDICATE: return 'P';
|
|
||||||
case CstType.THEOREM: return 'T';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function validateCstAlias(alias: string, type: CstType, schema: IRSForm): boolean {
|
|
||||||
return alias.length >= 2 && alias[0] == getCstTypePrefix(type) && !schema.items.find(cst => cst.alias === alias);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getCstExpressionPrefix(cst: IConstituenta): string {
|
|
||||||
return cst.alias + (cst.cst_type === CstType.STRUCTURED ? '::=' : ':==');
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getCstTypeShortcut(type: CstType) {
|
|
||||||
const prefix = labelCstType(type) + ' [Alt + ';
|
|
||||||
// prettier-ignore
|
|
||||||
switch (type) {
|
|
||||||
case CstType.BASE: return prefix + '1]';
|
|
||||||
case CstType.STRUCTURED: return prefix + '2]';
|
|
||||||
case CstType.TERM: return prefix + '3]';
|
|
||||||
case CstType.AXIOM: return prefix + '4]';
|
|
||||||
case CstType.FUNCTION: return prefix + 'Q]';
|
|
||||||
case CstType.PREDICATE: return prefix + 'W]';
|
|
||||||
case CstType.CONSTANT: return prefix + '5]';
|
|
||||||
case CstType.THEOREM: return prefix + '6]';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createAliasFor(type: CstType, schema: IRSForm): string {
|
|
||||||
const prefix = getCstTypePrefix(type);
|
|
||||||
if (!schema.items || schema.items.length <= 0) {
|
|
||||||
return `${prefix}1`;
|
|
||||||
}
|
|
||||||
const index = schema.items.reduce((prev, cst, index) => {
|
|
||||||
if (cst.cst_type !== type) {
|
|
||||||
return prev;
|
|
||||||
}
|
|
||||||
index = Number(cst.alias.slice(1 - cst.alias.length)) + 1;
|
|
||||||
return Math.max(prev, index);
|
|
||||||
}, 1);
|
|
||||||
return `${prefix}${index}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function cloneTitle(target: ILibraryItem): string {
|
|
||||||
if (!target.title.includes('[клон]')) {
|
|
||||||
return target.title + ' [клон]';
|
|
||||||
} else {
|
|
||||||
return target.title + '+';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getRSErrorPrefix(error: IRSErrorDescription): string {
|
|
||||||
const id = error.errorType.toString(16);
|
|
||||||
// prettier-ignore
|
|
||||||
switch(inferErrorClass(error.errorType)) {
|
|
||||||
case RSErrorClass.LEXER: return 'L' + id;
|
|
||||||
case RSErrorClass.PARSER: return 'P' + id;
|
|
||||||
case RSErrorClass.SEMANTIC: return 'S' + id;
|
|
||||||
case RSErrorClass.UNKNOWN: return 'U' + id;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* Module: Mappings for selector UI elements.
|
* Module: Mappings for selector UI elements. Do not confuse with html selectors
|
||||||
*/
|
*/
|
||||||
import { LayoutTypes } from 'reagraph';
|
import { LayoutTypes } from 'reagraph';
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user