Refactoring: miscellaneous functions

This commit is contained in:
IRBorisov 2024-01-04 14:35:46 +03:00
parent e22c38d9f4
commit 8d240d1360
32 changed files with 157 additions and 141 deletions

View File

@ -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",

View File

@ -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}

View File

@ -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',

View File

@ -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 {

View File

@ -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'

View File

@ -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'

View File

@ -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',

View File

@ -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;

View File

@ -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(() => {

View File

@ -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 (

View File

@ -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='Основная ссылка'

View File

@ -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]);

View File

@ -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);

View File

@ -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 + '+';
}
}

View File

@ -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';

View File

@ -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}`;
}

View File

@ -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;
}
}

View File

@ -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)}
/> />

View File

@ -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'

View File

@ -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);
} }
}); });

View File

@ -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;

View File

@ -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)}

View File

@ -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;

View File

@ -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());

View File

@ -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);

View File

@ -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);

View File

@ -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}}`
}; };
/** /**

View File

@ -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

View File

@ -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;
}
}

View File

@ -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';