kotaindah55_extended-markdo.../src/types.d.ts

413 lines
No EOL
12 KiB
TypeScript

import type { SyntaxNode, Tree } from "@lezer/common";
import type { Format, MarkdownViewMode, TokenLevel, Delimiter, TokenStatus, Field, DisplayBehaviour } from "src/enums";
import { Line, RangeSet, RangeValue, Text, Range, SelectionRange, EditorState } from "@codemirror/state";
import {} from "@codemirror/language";
import { Decoration, DecorationSet } from "@codemirror/view";
import { ColorComponent, Command, DropdownComponent, Editor, ExtraButtonComponent, IconName, MarkdownFileInfo, MarkdownView, Setting, SliderComponent, TextAreaComponent, TextComponent, ToggleComponent } from "obsidian";
import ExtendedMarkdownSyntax from "main";
import { TagMenu } from "src/editor-mode/ui-components";
import { ExtendedSettingTab } from "src/settings/ui/setting-tab";
declare module "@codemirror/state" {
/**
* MIT licensed, copyright (c) by Marijn Haverbeke and others at
* CodeMirror.
*
* @see https://github.com/codemirror/state/blob/main/src/text.ts
*/
interface TextNode extends Text {
readonly children: readonly Text[];
}
/**
* MIT licensed, copyright (c) by Marijn Haverbeke and others at
* CodeMirror.
*
* @see https://github.com/codemirror/state/blob/main/src/text.ts
*/
interface TextLeaf extends Text {
readonly children: null;
text: string[];
}
}
declare module "@codemirror/language" {
/**
* MIT licensed, copyright (c) by Marijn Haverbeke and others at
* CodeMirror.
*
* @see https://github.com/codemirror/language/blob/main/src/language.ts
*/
class LanguageState {
readonly tree: Tree;
readonly context: ParseContext;
}
/**
* MIT licensed, copyright (c) by Marijn Haverbeke and others at
* CodeMirror.
*
* @see https://github.com/codemirror/language/blob/main/src/language.ts
*/
interface ParseContext {
tree: Tree;
treeLen: number;
}
}
declare module "obsidian" {
interface MarkdownPostProcessorContext {
el: HTMLElement;
containerEl: HTMLElement;
}
}
declare global {
interface Window {
globalThis: typeof globalThis;
}
type Writable<T> = {
-readonly [P in keyof T]: T[P];
}
}
/** Token interface */
export type Token = {
/** Format type */
type: Format, // 3-bit
/** Either block or inline */
level: TokenLevel, // 1 bit
/** Token status */
status: TokenStatus, // 2-bit
/** Start offset */
from: number, // 16-bit
/** End offset */
to: number, // 16-bit
/** Length of its opening delimiter. */
openLen: number,
/** Length of its closing delimiter if any, otherwise it should be zero. */
closeLen: number,
/** Length of its tag if any, otherwise it should be zero. */
tagLen: number,
/** Whether the range of tag is covered by the range of content or not. */
tagAsContent: boolean;
/** The tag was considered as invalid when it does not satisfy its rule. */
validTag: boolean;
/**
* Closed by blank line instead of closing delimiter. If `closeLen` is 0, the
* token's type isn't space-restricted format, and `closedByBlankLine` is `false`,
* then indicates that the token definitely was located in the end of the document
* (`Token.to == Doc.length`).
*/
closedByBlankLine: boolean;
}
/** Accumulation of `ChangeSet` generated by the `composeChange` function. */
export type ChangedRange = {
/**
* Start offset of a changed range, measured from
* the text both before and after the change.
*/
from: number,
/** End offset of a changed range, measured from the doc before the change. */
initTo: number,
/** End offset of a changed range, measured from the doc after the change. */
changedTo: number;
/**
* Length difference. Can be negative value, indicating that replaced text
* is longer than the substitute.
*/
length: number;
}
/** Describe the rule that has to be satisfied by the delimiter */
export type DelimSpec = {
/** Should be single character */
char: string,
/** Delimiter length */
length: number,
/**
* If true, then delimiter length must be the same as
* in the predetermined rule. Otherwise, the defined length
* act as minimum length.
*/
exactLen: boolean,
/** Should be `Delimiter.OPEN` or `Delimiter.CLOSE` */
role: Delimiter,
/**
* When true, any space after the opening delimiter or before the closing
* one doesn't make the delimiter invalid. Default is false.
*/
allowSpaceOnDelim?: boolean
}
/**
* Used for (re)configuring the state, especially in
* the case of document or tree change
*/
export type StateConfig = {
doc: Text,
tree: Tree,
offset: number,
settings: PluginSettings,
maxLine?: number
}
/**
* Region defined here as a `PlainRange` collection arranged in an array.
* Each range inside should be sorted in order according to its `from`.
*/
export type Region = PlainRange[];
export type NodeSpec = {
node: SyntaxNode,
type: string
}
export type InlineFormat = Extract<Format, Format.INSERTION | Format.SPOILER | Format.SUPERSCRIPT | Format.SUBSCRIPT | Format.HIGHLIGHT | Format.CUSTOM_SPAN>;
export type BlockFormat = Exclude<Format, InlineFormat>;
export type NonBuiltinInlineFormat = Exclude<InlineFormat, Format.HIGHLIGHT>;
export type TagSupportFormat = Format.HIGHLIGHT | Format.CUSTOM_SPAN | Format.FENCED_DIV;
export type TokenGroup = Token[];
export type ColorConfig = {
tag: string,
name: string,
color: string,
showInMenu: boolean
};
export type TagConfig = {
tag: string,
name: string,
showInMenu: boolean
};
export type PluginSettings = {
// General
insertion: MarkdownViewMode;
spoiler: MarkdownViewMode;
superscript: MarkdownViewMode;
subscript: MarkdownViewMode;
customHighlight: MarkdownViewMode;
customSpan: MarkdownViewMode;
fencedDiv: MarkdownViewMode;
// Tag behavior
hlTagDisplayBehaviour: DisplayBehaviour;
spanTagDisplayBehaviour: DisplayBehaviour;
showHlTagInPreviewMode: boolean;
showSpanTagInPreviewMode: boolean;
alwaysShowFencedDivTag: MarkdownViewMode;
// Formatting
tidyFormatting: boolean;
openTagMenuAfterFormat: boolean;
noStyledDivInSourceMode: boolean;
// Custom highlight
colorButton: boolean;
showAccentColor: boolean;
showDefaultColor: boolean;
showRemoveColor: boolean;
lightModeHlOpacity: number;
darkModeHlOpacity: number;
colorConfigs: ColorConfig[];
// Custom span
showDefaultSpanTag: boolean;
showRemoveSpanTag: boolean;
predefinedSpanTag: TagConfig[];
// Fenced div
showDefaultDivTag: boolean;
showRemoveDivTag: boolean;
predefinedDivTag: TagConfig[];
// Others
editorEscape: boolean;
decoratePDF: boolean;
}
export type PlainRange = { from: number, to: number };
export type InlineFormatRule = {
char: string,
length: number,
exactLen: boolean,
allowSpace: boolean,
mustBeClosed: boolean,
class: string,
getEl: (cls?: string) => Element,
builtin: boolean
}
export type BlockFormatRule = {
char: string,
length: number,
exactLen: boolean,
class: string,
}
export type FormatRule = InlineFormatRule & BlockFormatRule;
export interface TokenDecoration extends Decoration {
spec: {
class: string,
token: Token
};
}
export type TokenDecorationSet = RangeSet<TokenDecoration>;
export type IndexCache = { number: number }
export type DecoSetCollection = Record<"inlineSet" | "blockSet" | "omittedSet" | "colorBtnSet" | "revealedSpoilerSet", DecorationSet>;
export type IterLineSpec = {
doc: Text,
fromLn: number,
toLn?: number,
callback: (line: Line, doc: Text) => boolean | void
}
export type IterTokenGroupSpec = {
tokens: TokenGroup,
ranges: PlainRange[] | readonly PlainRange[],
indexCache: IndexCache
callback: (token: Token) => unknown,
}
export type RangeSetUpdate<T extends RangeValue> = {
add?: readonly Range<T>[];
sort?: boolean;
filter?: (from: number, to: number, value: T) => boolean;
filterFrom?: number;
filterTo?: number;
}
export type OptionType = number;
export type Options<T, U = string> = T extends OptionType ? Record<OptionType, U> : never;
export type FieldComponentRecord = {
[Field.TOGGLE]: ToggleComponent,
[Field.MULTI_TOGGLE]: ExtraButtonComponent,
[Field.DROPDOWN]: DropdownComponent,
[Field.COLOR]: ColorComponent,
[Field.TEXT]: TextComponent,
[Field.TEXT_AREA]: TextAreaComponent,
[Field.SLIDER]: SliderComponent
};
export type FieldValueRecord = {
[Field.TOGGLE]: boolean,
[Field.MULTI_TOGGLE]: OptionType,
[Field.DROPDOWN]: OptionType,
[Field.COLOR]: string,
[Field.TEXT]: string,
[Field.TEXT_AREA]: string,
[Field.SLIDER]: number
};
export type FieldSpecRecord<TValue> = {
[Field.TOGGLE]: null,
[Field.MULTI_TOGGLE]: { options: Options<TValue, { icon: IconName, tooltip?: string }> },
[Field.DROPDOWN]: { options: Options<TValue> },
[Field.COLOR]: null,
[Field.TEXT]: { placeholder?: string, filter?: RegExp },
[Field.TEXT_AREA]: { placeholder?: string, filter?: RegExp, resizable?: boolean }
[Field.SLIDER]: { min: number, max: number, step: number }
};
export type FieldValue<TField extends Field> = FieldValueRecord[TField];
export type FieldComponent<TField extends Field> = FieldComponentRecord[TField];
export type FieldSpec<TField extends Field, TValue> = FieldSpecRecord<TValue>[TField];
export type AbstractFieldDesc<TField extends Field, TRecord, TKey extends keyof TRecord> = {
type: TField,
record: TRecord extends Record<string, unknown> ? TRecord : never,
key: TKey extends keyof TRecord ? TRecord[TKey] extends infer TValue ? TValue extends FieldValue<TField> ? TKey : never : never : never,
callback?: (component: FieldComponent<TField>, plugin: ExtendedMarkdownSyntax) => unknown,
spec: FieldSpec<TField, TRecord[TKey]>,
update?: {
deep?: boolean,
internal?: boolean
}
};
export type ToggleFieldDesc<TRecord> = AbstractFieldDesc<Field.TOGGLE, TRecord, keyof TRecord>;
export type MultiToggleFieldDesc<TRecord> = AbstractFieldDesc<Field.MULTI_TOGGLE, TRecord, keyof TRecord>;
export type DropdownFieldDesc<TRecord> = AbstractFieldDesc<Field.DROPDOWN, TRecord, keyof TRecord>;
export type ColorFieldDesc<TRecord> = AbstractFieldDesc<Field.COLOR, TRecord, keyof TRecord>;
export type TextFieldDesc<TRecord> = AbstractFieldDesc<Field.TEXT, TRecord, keyof TRecord>;
export type TextAreaFieldDesc<TRecord> = AbstractFieldDesc<Field.TEXT_AREA, TRecord, keyof TRecord>;
export type SliderFieldDesc<TRecord> = AbstractFieldDesc<Field.SLIDER, TRecord, keyof TRecord>;
export type FieldGroup<TRecord> = (
ToggleFieldDesc<TRecord> |
MultiToggleFieldDesc<TRecord> |
DropdownFieldDesc<TRecord> |
ColorFieldDesc<TRecord> |
TextFieldDesc<TRecord> |
TextAreaFieldDesc<TRecord> |
SliderFieldDesc<TRecord>
)[];
export type SettingItem<TRecord> = {
name: string,
desc?: string,
fields?: FieldGroup<TRecord>,
preservedForTagSettings?: Format.HIGHLIGHT | Format.CUSTOM_SPAN | Format.FENCED_DIV
}
export type SettingGroup<TRecord> = {
heading?: string,
desc?: string,
collapsible?: boolean,
id: string,
items: SettingItem<TRecord>[]
}
export type SettingRoot<TRecord> = SettingGroup<TRecord>[];
export type TagSettingsSpec = {
type: TagSupportFormat;
addBtnPlaceholder: string;
nameFieldPlaceholder: string;
tagFieldPlaceholder: string;
tagFilter: RegExp;
onAdd?: (settingTab: ExtendedSettingTab, tagSettingItem: Setting, newConfig: TagConfig) => unknown;
onMove?: (settingTab: ExtendedSettingTab, oldIndex: number, newIndex: number) => unknown;
onResetted?: (settingTab: ExtendedSettingTab) => unknown;
onDelete?: (settingTab: ExtendedSettingTab, deletedIndex: number) => unknown;
onTagChange?: (settingTab: ExtendedSettingTab, changedConfig: TagConfig, index: number) => unknown;
}
export type FormattingSpec = {
range: SelectionRange,
type: Format,
state: EditorState,
tokenIndexMap: number[],
tokens: TokenGroup,
remadeTokenIndexes: Partial<Record<number, boolean>>,
preventMenu?: boolean,
precise?: boolean
}
export interface CtxMenuCommand extends Command {
icon: string;
ctxMenuTitle: string;
editorCallback: (editor: Editor, ctx: MarkdownView | MarkdownFileInfo) => unknown;
[data: string]: unknown;
}
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface ITagMenu extends TagMenu {}