diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index c83654e177e..56a36d8505b 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -52,6 +52,7 @@ jobs: fi # - run: npm run lint - run: node_modules/.bin/tsc --noImplicitAny --strict --noUnusedLocals --noImplicitReturns --noUnusedParameters --noImplicitThis ace.d.ts + - run: npm run typecheck - uses: codecov/codecov-action@v3 with: token: d8edca4b-8e97-41e5-b54e-34c7cf3b2d47 diff --git a/ace-internal.d.ts b/ace-internal.d.ts new file mode 100644 index 00000000000..82ce9e9eb82 --- /dev/null +++ b/ace-internal.d.ts @@ -0,0 +1,1502 @@ +export namespace Ace { + type Anchor = import("./src/anchor").Anchor; + type Editor = import("./src/editor").Editor; + type EditSession = import("./src/edit_session").EditSession; + type Document = import("./src/document").Document; + type Fold = import("./src/edit_session/fold").Fold; + type FoldLine = import("./src/edit_session/fold_line").FoldLine; + type Range = import("./src/range").Range; + type VirtualRenderer = import("./src/virtual_renderer").VirtualRenderer; + type UndoManager = import("./src/undomanager").UndoManager; + type Tokenizer = import("./src/tokenizer").Tokenizer; + type TokenIterator = import("./src/token_iterator").TokenIterator; + type Selection = import("./src/selection").Selection; + type Autocomplete = import("./src/autocomplete").Autocomplete; + type InlineAutocomplete = import("./src/ext/inline_autocomplete").InlineAutocomplete; + type CompletionProvider = import("./src/autocomplete").CompletionProvider; + type AcePopup = import("./src/autocomplete/popup").AcePopup; + type AceInline = import("./src/autocomplete/inline").AceInline; + type MouseEvent = import("./src/mouse/mouse_event").MouseEvent; + type RangeList = import("./src/range_list").RangeList; + type FilteredList = import("./src/autocomplete").FilteredList; + type LineWidgets = import("./src/line_widgets").LineWidgets; + type SearchBox = import("./src/ext/searchbox").SearchBox; + type Occur = import("./src/occur").Occur; + type DefaultHandlers = import("./src/mouse/default_handlers").DefaultHandlers; + type GutterHandler = import("./src/mouse/default_gutter_handler").GutterHandler; + type DragdropHandler = import("./src/mouse/dragdrop_handler").DragdropHandler; + type AppConfig = import("./src/lib/app_config").AppConfig; + + + type AfterLoadCallback = (err: Error | null, module: unknown) => void; + type LoaderFunction = (moduleName: string, afterLoad: AfterLoadCallback) => void; + + export interface Config { + get(key: string): any; + + set(key: string, value: any): void; + + all(): { [key: string]: any }; + + moduleUrl(name: string, component?: string): string; + + setModuleUrl(name: string, subst: string): string; + + setLoader(cb: LoaderFunction): void; + + setModuleLoader(name: string, onLoad: Function): void; + + loadModule(moduleName: string | [string, string], + onLoad?: (module: any) => void): void; + + init(packaged: any): any; + + defineOptions(obj: any, path: string, options: { [key: string]: any }): Config; + + resetOptions(obj: any): void; + + setDefaultValue(path: string, name: string, value: any): void; + + setDefaultValues(path: string, optionHash: { [key: string]: any }): void; + } + + interface Theme { + cssClass?: string; + cssText?: string; + $id?: string; + padding?: number | string; + isDark?: boolean; + } + + interface ScrollBar { + setVisible(visible: boolean): void; + + [key: string]: any; + } + + interface HScrollbar extends ScrollBar { + setWidth(width: number): void; + } + + interface VScrollbar extends ScrollBar { + setHeight(width: number): void; + } + + interface LayerConfig { + width: number, + padding: number, + firstRow: number, + firstRowScreen: number, + lastRow: number, + lineHeight: number, + characterWidth: number, + minHeight: number, + maxHeight: number, + offset: number, + height: number, + gutterOffset: number + } + + interface HardWrapOptions { + startRow: number; + endRow: number; + allowMerge?: boolean; + column?: number; + } + + interface CommandBarOptions { + maxElementsOnTooltip: number; + alwaysShow: boolean; + showDelay: number; + hideDelay: number; + } + + interface ScreenCoordinates { + row: number, + column: number, + side?: 1 | -1, + offsetX?: number + } + + interface Folding { + $foldData: FoldLine[]; + + /** + * Looks up a fold at a given row/column. Possible values for side: + * -1: ignore a fold if fold.start = row/column + * +1: ignore a fold if fold.end = row/column + **/ + getFoldAt(row: number, column: number, side?: number): Ace.Fold; + + /** + * Returns all folds in the given range. Note, that this will return folds + **/ + getFoldsInRange(range: Ace.Range | Ace.Delta): Ace.Fold[]; + + getFoldsInRangeList(ranges: Ace.Range[] | Ace.Range): Ace.Fold[]; + + /** + * Returns all folds in the document + */ + getAllFolds(): Ace.Fold[]; + + /** + * Returns the string between folds at the given position. + * E.g. + * foob|arwolrd -> "bar" + * foobarwol|rd -> "world" + * foobarwolrd -> + * + * where | means the position of row/column + * + * The trim option determs if the return string should be trimed according + * to the "side" passed with the trim value: + * + * E.g. + * foob|arwolrd -trim=-1> "b" + * foobarwol|rd -trim=+1> "rld" + * fo|obarwolrd -trim=00> "foo" + */ + getFoldStringAt(row: number, column: number, trim?: number, foldLine?: Ace.FoldLine): string | null; + + getFoldLine(docRow: number, startFoldLine?: Ace.FoldLine): null | Ace.FoldLine; + + /** + * Returns the fold which starts after or contains docRow + */ + getNextFoldLine(docRow: number, startFoldLine?: Ace.FoldLine): null | Ace.FoldLine; + + getFoldedRowCount(first: number, last: number): number; + + $addFoldLine(foldLine: FoldLine): Ace.FoldLine; + + /** + * Adds a new fold. + * @returns {Ace.Fold} + * The new created Fold object or an existing fold object in case the + * passed in range fits an existing fold exactly. + */ + addFold(placeholder: Ace.Fold | string, range?: Ace.Range): Ace.Fold; + + $modified: boolean; + + addFolds(folds: Ace.Fold[]): void; + + removeFold(fold: Ace.Fold): void; + + removeFolds(folds: Ace.Fold[]): void; + + expandFold(fold: Ace.Fold): void; + + expandFolds(folds: Ace.Fold[]): void; + + unfold(location?: number | null | Ace.Point | Ace.Range | Ace.Range[], expandInner?: boolean): Ace.Fold[] | undefined; + + /** + * Checks if a given documentRow is folded. This is true if there are some + * folded parts such that some parts of the line is still visible. + **/ + isRowFolded(docRow: number, startFoldRow?: Ace.FoldLine): boolean; + + getRowFoldEnd(docRow: number, startFoldRow?: Ace.FoldLine): number; + + getRowFoldStart(docRow: number, startFoldRow?: Ace.FoldLine): number; + + getFoldDisplayLine(foldLine: Ace.FoldLine, endRow?: number | null, endColumn?: number | null, startRow?: number | null, startColumn?: number | null): string; + + getDisplayLine(row: number, endColumn: number | null, startRow: number | null, startColumn: number | null): string; + + $cloneFoldData(): Ace.FoldLine[]; + + toggleFold(tryToUnfold?: boolean): void; + + getCommentFoldRange(row: number, column: number, dir?: number): Ace.Range | undefined; + + foldAll(startRow?: number | null, endRow?: number | null, depth?: number | null, test?: Function): void; + + foldToLevel(level: number): void; + + foldAllComments(): void; + + $foldStyles: { + manual: number; + markbegin: number; + markbeginend: number; + }; + $foldStyle: string; + + setFoldStyle(style: string): void; + + $setFolding(foldMode: Ace.FoldMode): void; + + $foldMode: any; + foldWidgets: any[]; + getFoldWidget: any; + getFoldWidgetRange: any; + $updateFoldWidgets: any; + $tokenizerUpdateFoldWidgets: any; + + getParentFoldRangeData(row: number, ignoreCurrent?: boolean): { + range?: Ace.Range; + firstRange?: Ace.Range; + }; + + onFoldWidgetClick(row: number, e: any): void; + + $toggleFoldWidget(row: number, options: any): Fold | any; + + /** + * + * @param {boolean} [toggleParent] + */ + toggleFoldWidget(toggleParent?: boolean): void; + + updateFoldWidgets(delta: Ace.Delta): void; + + tokenizerUpdateFoldWidgets(e: any): void; + } + + interface BracketMatch { + findMatchingBracket: (position: Point, chr?: string) => Point; + + getBracketRange: (pos: Point) => null | Range; + /** + * Returns: + * * null if there is no any bracket at `pos`; + * * two Ranges if there is opening and closing brackets; + * * one Range if there is only one bracket + */ + getMatchingBracketRanges: (pos: Point, isBackwards?: boolean) => null | Range[]; + $brackets: { + ")": string; + "(": string; + "]": string; + "[": string; + "{": string; + "}": string; + "<": string; + ">": string; + }; + $findOpeningBracket: (bracket: string, position: Point, typeRe?: RegExp) => Point | null; + $findClosingBracket: (bracket: string, position: Point, typeRe?: RegExp) => Point | null; + /** + * Returns [[Range]]'s for matching tags and tag names, if there are any + */ + getMatchingTags: (pos: Point) => { + closeTag: Range; + closeTagName: Range; + openTag: Range; + openTagName: Range; + }; + $findTagName: (iterator: any) => any; + $findClosingTag: (iterator: any, token: any) => { + openTag: Range; + closeTag: Range; + openTagName: Range; + closeTagName: Range; + }; + $findOpeningTag: (iterator: any, token: any) => { + openTag: Range; + closeTag: Range; + openTagName: Range; + closeTagName: Range; + }; + } + + interface IRange { + start: Point; + end: Point; + } + + interface LineWidget { + el: HTMLElement; + rowCount: number; + hidden: boolean; + _inDocument: boolean; + column?: number; + row?: number; + $oldWidget?: LineWidget, + session: EditSession, + html?: string, + text?: string, + className?: string, + coverGutter?: boolean, + pixelHeight?: number, + $fold?: Fold, + editor: Editor, + coverLine?: boolean, + fixedWidth?: boolean, + fullWidth?: boolean, + screenWidth?: number, + rowsAbove?: number, + lenses?: any[], + } + + type NewLineMode = 'auto' | 'unix' | 'windows'; + + interface EditSessionOptions { + wrap: "off" | "free" | "printmargin" | boolean | number; + wrapMethod: 'code' | 'text' | 'auto'; + indentedSoftWrap: boolean; + firstLineNumber: number; + useWorker: boolean; + useSoftTabs: boolean; + tabSize: number; + navigateWithinSoftTabs: boolean; + foldStyle: 'markbegin' | 'markbeginend' | 'manual'; + overwrite: boolean; + newLineMode: NewLineMode; + mode: string; + } + + interface VirtualRendererOptions { + animatedScroll: boolean; + showInvisibles: boolean; + showPrintMargin: boolean; + printMarginColumn: number; + printMargin: boolean | number; + showGutter: boolean; + fadeFoldWidgets: boolean; + showFoldWidgets: boolean; + showLineNumbers: boolean; + displayIndentGuides: boolean; + highlightIndentGuides: boolean; + highlightGutterLine: boolean; + hScrollBarAlwaysVisible: boolean; + vScrollBarAlwaysVisible: boolean; + fontSize: string; + fontFamily: string; + maxLines: number; + minLines: number; + scrollPastEnd: boolean; + fixedWidthGutter: boolean; + customScrollbar: boolean; + theme: string; + hasCssTransforms: boolean; + maxPixelHeight: number; + useSvgGutterIcons: boolean; + showFoldedAnnotations: boolean; + useResizeObserver: boolean; + } + + interface MouseHandlerOptions { + scrollSpeed: number; + dragDelay: number; + dragEnabled: boolean; + focusTimeout: number; + tooltipFollowsMouse: boolean; + } + + interface EditorOptions extends EditSessionOptions, + MouseHandlerOptions, + VirtualRendererOptions { + selectionStyle: "fullLine" | "screenLine" | "text" | "line"; + highlightActiveLine: boolean; + highlightSelectedWord: boolean; + readOnly: boolean; + copyWithEmptySelection: boolean; + cursorStyle: 'ace' | 'slim' | 'smooth' | 'wide'; + mergeUndoDeltas: true | false | 'always'; + behavioursEnabled: boolean; + wrapBehavioursEnabled: boolean; + enableAutoIndent: boolean; + enableBasicAutocompletion: boolean | Completer[]; + enableLiveAutocompletion: boolean | Completer[]; + liveAutocompletionDelay: number; + liveAutocompletionThreshold: number; + enableSnippets: boolean; + autoScrollEditorIntoView: boolean; + keyboardHandler: string | null; + placeholder: string; + value: string; + session: EditSession; + relativeLineNumbers: boolean; + enableMultiselect: boolean; + enableKeyboardAccessibility: boolean; + enableCodeLens: boolean; + } + + interface EventsBase { + [key: string]: any; + } + + interface EditSessionEvents { + /** + * Emitted when the document changes. + * @param delta + */ + "change": (delta: Delta) => void; + /** + * Emitted when the tab size changes, via [[EditSession.setTabSize]]. + * @param tabSize + */ + "changeTabSize": (tabSize: number) => void; + /** + * Emitted when the ability to overwrite text changes, via [[EditSession.setOverwrite]]. + * @param overwrite + */ + "changeOverwrite": (overwrite: boolean) => void; + /** + * Emitted when the gutter changes, either by setting or removing breakpoints, or when the gutter decorations change. + * @param e + */ + "changeBreakpoint": (e: { row: number, breakpoint: boolean }) => void; + /** + * Emitted when a front marker changes. + * @param e + */ + "changeFrontMarker": (e: { row: number, marker: boolean }) => void; + /** + * Emitted when a back marker changes. + * @param e + */ + "changeBackMarker": (e: { row: number, marker: boolean }) => void; + /** + * Emitted when an annotation changes, like through [[EditSession.setAnnotations]]. + * @param e + */ + "changeAnnotation": (e: { row: number, lines: string[] }) => void; + /** + * Emitted when a background tokenizer asynchronously processes new rows. + */ + "tokenizerUpdate": (e: { data: { first: string, last: string } }) => void; + /** + * Emitted when the current mode changes. + * @param e + */ + "changeMode": (e) => void; + /** + * Emitted when the wrap mode changes. + * @param e + */ + "changeWrapMode": (e) => void; + /** + * Emitted when the wrapping limit changes. + * @param e + */ + "changeWrapLimit": (e) => void; + /** + * Emitted when a code fold is added or removed. + * @param e + */ + "changeFold": (e, session: EditSession) => void; + /** + * Emitted when the scroll top changes. + * @param scrollTop The new scroll top value + **/ + "changeScrollTop": (scrollTop: number) => void; + /** + * Emitted when the scroll left changes. + * @param scrollLeft The new scroll left value + **/ + "changeScrollLeft": (scrollLeft: number) => void; + "changeEditor": (e: { editor: Editor }) => void; + } + + interface EditorEvents { + "change": (delta: Delta) => void; + "changeSelection": () => void; + "input": () => void; + /** + * Emitted whenever the [[EditSession]] changes. + * @param e An object with two properties, `oldSession` and `session`, that represent the old and new [[EditSession]]s. + **/ + "changeSession": (e: { oldSession: EditSession, session: EditSession }) => void; + "blur": (e) => void; + "mousedown": (e: MouseEvent) => void; + "mousemove": (e: MouseEvent & { scrollTop? }, editor?: Editor) => void; + "changeStatus": () => void; + "keyboardActivity": () => void; + "mousewheel": (e: MouseEvent) => void; + "mouseup": (e: MouseEvent) => void; + "beforeEndOperation": (e) => void; + "nativecontextmenu": (e) => void; + "destroy": () => void; + "focus": () => void; + /** + * Emitted when text is copied. + * @param text The copied text + **/ + "copy": (text: string) => void; + /** + * Emitted when text is pasted. + **/ + "paste": (text: string, event) => void; + /** + * Emitted when the selection style changes, via [[Editor.setSelectionStyle]]. + * @param data Contains one property, `data`, which indicates the new selection style + **/ + "changeSelectionStyle": (data: "fullLine" | "screenLine" | "text" | "line") => void; + } + + interface AcePopupEvents { + "click": (e: MouseEvent) => void; + "dblclick": (e: MouseEvent) => void; + "tripleclick": (e: MouseEvent) => void; + "quadclick": (e: MouseEvent) => void; + "show": () => void; + "hide": () => void; + "select": (hide: boolean) => void; + "changeHoverMarker": (e) => void; + } + + interface DocumentEvents { + /** + * Fires whenever the document changes. + * Several methods trigger different `"change"` events. Below is a list of each action type, followed by each property that's also available: + * * `"insert"` + * * `range`: the [[Range]] of the change within the document + * * `lines`: the lines being added + * * `"remove"` + * * `range`: the [[Range]] of the change within the document + * * `lines`: the lines being removed + * + **/ + "change": (e: Delta) => void; + "changeNewLineMode": () => void; + } + + interface AnchorEvents { + /** + * Fires whenever the anchor position changes. + * Both of these objects have a `row` and `column` property corresponding to the position. + * Events that can trigger this function include [[Anchor.setPosition `setPosition()`]]. + * @param {Object} e An object containing information about the anchor position. It has two properties: + * - `old`: An object describing the old Anchor position + * - `value`: An object describing the new Anchor position + **/ + "change": (e: { old: Point, value: Point }) => void; + } + + interface BackgroundTokenizerEvents { + /** + * Fires whenever the background tokeniziers between a range of rows are going to be updated. + * @param {Object} e An object containing two properties, `first` and `last`, which indicate the rows of the region being updated. + **/ + "update": (e: { first: number, last: number }) => void; + } + + interface SelectionEvents { + /** + * Emitted when the cursor position changes. + **/ + "changeCursor": () => void; + /** + * Emitted when the cursor selection changes. + **/ + "changeSelection": () => void; + } + + interface PlaceHolderEvents { + + } + + interface GutterEvents { + "changeGutterWidth": (width: number) => void; + } + + interface TextEvents { + "changeCharacterSize": (e) => void; + } + + interface VirtualRendererEvents { + "afterRender": (e, renderer: VirtualRenderer) => void; + "beforeRender": (e, renderer: VirtualRenderer) => void; + } + + class EventEmitter { + once(name: K, callback: T[K]): void; + + setDefaultHandler(name: string, callback: Function): void; + + removeDefaultHandler(name: string, callback: Function): void; + + on(name: K, callback: T[K], capturing?: boolean): T[K]; + + addEventListener(name: K, callback: T[K], capturing?: boolean): T[K]; + + off(name: K, callback: T[K]): void; + + removeListener(name: K, callback: T[K]): void; + + removeEventListener(name: K, callback: T[K]): void; + + removeAllListeners(name?: string): void; + + _signal(eventName: string, e: any): void; + + _emit(eventName: string, e: any): void; + + _dispatchEvent(eventName: string, e: any): void; + } + + interface SearchOptions { + needle: string | RegExp; + preventScroll: boolean; + backwards: boolean; + start: Range; + skipCurrent: boolean; + range: Range; + preserveCase: boolean; + regExp: boolean; + wholeWord: boolean; + caseSensitive: boolean; + wrap: boolean; + re: RegExp; + } + + interface Point { + row: number; + column: number; + } + + type Position = Point; + + interface Delta { + action: 'insert' | 'remove'; + start: Point; + end: Point; + lines: string[]; + id?: number, + folds?: Fold[] + } + + interface Annotation { + row: number; + column: number; + text: string; + type: string; + } + + export interface MarkerGroupItem { + range: Range; + className: string; + } + + type MarkerGroup = import("./src/marker_group").MarkerGroup; + + + export interface Command { + name?: string; + bindKey?: string | { mac?: string, win?: string }; + readOnly?: boolean; + exec?: (editor?: Editor | any, args?: any) => void; + isAvailable?: (editor: Editor) => boolean; + description?: string, + multiSelectAction?: "forEach" | "forEachLine" | Function, + scrollIntoView?: true | "cursor" | "center" | "selectionPart" | "animate" | "selection" | "none", + aceCommandGroup?: string, + passEvent?: boolean, + level?: number, + action?: string, + } + + type CommandLike = Command | ((editor: Editor) => void) | ((sb: SearchBox) => void); + + type KeyboardHandler = Partial & { + attach?: (editor: Editor) => void; + detach?: (editor: Editor) => void; + getStatusText?: (editor?: any, data?) => string; + } + + export interface MarkerLike { + range?: Range; + type: string; + renderer?: MarkerRenderer; + clazz: string; + inFront?: boolean; + id?: number; + update?: (html: string[], + // TODO maybe define Marker class + marker: any, + session: EditSession, + config: any) => void; + + [key: string]: any; + } + + type MarkerRenderer = (html: string[], + range: Range, + left: number, + top: number, + config: any) => void; + + interface Token { + type: string; + value: string; + index?: number; + start?: number; + } + + type BaseCompletion = import("./src/autocomplete").BaseCompletion; + type SnippetCompletion = import("./src/autocomplete").SnippetCompletion; + type ValueCompletion = import("./src/autocomplete").ValueCompletion; + type Completion = import("./src/autocomplete").Completion; + + type HighlightRule = ({ defaultToken: string } | { include: string } | { todo: string } | { + token: string | string[] | ((value: string) => string); + regex: string | RegExp; + next?: string | (() => void); + push?: string; + comment?: string; + caseInsensitive?: boolean; + nextState?: string; + }) & { [key: string]: any }; + + type HighlightRulesMap = Record; + + type KeywordMapper = (keyword: string) => string; + + interface HighlightRules { + $rules: HighlightRulesMap; + $embeds: string[]; + $keywords: any[]; + $keywordList: string[]; + + addRules(rules: HighlightRulesMap, prefix?: string): void; + + getRules(): HighlightRulesMap; + + embedRules(rules: (new () => HighlightRules) | HighlightRulesMap, prefix: string, escapeRules?: boolean, append?: boolean): void; + + getEmbeds(): string[]; + + normalizeRules(): void; + + createKeywordMapper(map: Record, defaultToken?: string, ignoreCase?: boolean, splitChar?: string): KeywordMapper; + } + + type FoldWidget = "start" | "end" | "" + + interface FoldMode { + foldingStartMarker: RegExp; + foldingStopMarker?: RegExp; + + getFoldWidget(session: EditSession, foldStyle: string, row: number): FoldWidget; + + getFoldWidgetRange(session: EditSession, foldStyle: string, row: number): Range | undefined; + + indentationBlock(session: EditSession, row: number, column?: number): Range | undefined; + + openingBracketBlock(session: EditSession, bracket: string, row: number, column: number, typeRe?: RegExp): Range | undefined; + + closingBracketBlock(session: EditSession, bracket: string, row: number, column: number, typeRe?: RegExp): Range | undefined; + } + + type BehaviorAction = (state: string, action: string, editor: Editor, session: EditSession, text: string | Range) => ({ text: string, selection: number[] } | Range) & { [key: string]: any } | undefined; + type BehaviorMap = Record>; + + interface Behaviour { + add(name: string, action: string, callback: BehaviorAction): void; + + addBehaviours(behaviours: BehaviorMap): void; + + remove(name: string): void; + + inherit(mode: SyntaxMode | (new () => SyntaxMode), filter: string[]): void; + + getBehaviours(filter?: string[]): BehaviorMap; + } + + interface Outdent { + checkOutdent(line: string, input: string): boolean; + + autoOutdent(doc: Document, row: number): number | undefined; + } + + interface SyntaxMode { + /** + * quotes used by language mode + */ + $quotes: { [quote: string]: string }; + HighlightRules: { + new(config: any): HighlightRules + }; //TODO: fix this + foldingRules?: FoldMode; + $behaviour?: Behaviour; + $defaultBehaviour?: Behaviour; + /** + * characters that indicate the start of a line comment + */ + lineCommentStart?: string; + /** + * characters that indicate the start and end of a block comment + */ + blockComment?: { start: string, end: string } + tokenRe?: RegExp; + nonTokenRe?: RegExp; + /** + * An object containing conditions to determine whether to apply matching quote or not. + */ + $pairQuotesAfter: { [quote: string]: RegExp } + $tokenizer: Tokenizer; + $highlightRules: HighlightRules; + $embeds?: string[]; + $modes?: SyntaxMode[]; + $keywordList?: string[]; + $highlightRuleConfig?: any; + completionKeywords: string[]; + transformAction: BehaviorAction; + path?: string; + + getTokenizer(): Tokenizer; + + toggleCommentLines(state: any, + session: EditSession, + startRow: number, + endRow: number): void; + + toggleBlockComment(state: any, + session: EditSession, + range: Range, + cursor: Point): void; + + getNextLineIndent(state: any, line: string, tab: string): string; + + checkOutdent(state: any, line: string, input: string): boolean; + + autoOutdent(state: any, doc: EditSession, row: number): void; + + // TODO implement WorkerClient types + createWorker(session: EditSession): any; + + createModeDelegates(mapping: { [key: string]: string }): void; + + getKeywords(append?: boolean): Array; + + getCompletions(state: string, + session: EditSession, + pos: Point, + prefix: string): Completion[]; + + $getIndent(line: string): string; + + $createKeywordList(): string[]; + + $delegator(method: string, args: IArguments, defaultHandler): any; + + } + + interface OptionsBase { + [key: string]: any; + } + + class OptionsProvider { + setOptions(optList: Partial): void; + + getOptions(optionNames?: Array | Partial): Partial; + + setOption(name: K, value: T[K]): void; + + getOption(name: K): T[K]; + } + + type KeyBinding = import("./src/keyboard/keybinding").KeyBinding; + + interface CommandMap { + [name: string]: Command; + } + + type execEventHandler = (obj: { + editor: Editor, + command: Command, + args: any[] + }) => void; + + interface CommandManagerEvents { + on(name: 'exec', callback: execEventHandler): Function; + + on(name: 'afterExec', callback: execEventHandler): Function; + } + + type CommandManager = import("./src/commands/command_manager").CommandManager; + + + interface SavedSelection { + start: Point; + end: Point; + isBackwards: boolean; + } + + var Selection: { + new(session: EditSession): Selection; + } + + interface TextInput { + resetSelection(): void; + + setAriaOption(options?: { activeDescendant: string, role: string, setLabel }): void; + } + + type CompleterCallback = (error: any, completions: Completion[]) => void; + + interface Completer { + identifierRegexps?: Array, + + getCompletions(editor: Editor, + session: EditSession, + position: Point, + prefix: string, + callback: CompleterCallback): void; + + getDocTooltip?(item: Completion): void | string | Completion; + onSeen?: (editor: Ace.Editor, completion: Completion) => void; + onInsert?: (editor: Ace.Editor, completion: Completion) => void; + cancel?(): void; + + id?: string; + triggerCharacters?: string[]; + hideInlinePreview?: boolean; + } + + interface CompletionOptions { + matches?: Completion[]; + } + + type CompletionProviderOptions = { + exactMatch?: boolean; + ignoreCaption?: boolean; + } + + type GatherCompletionRecord = { + prefix: string; + matches: Completion[]; + finished: boolean; + } + + type CompletionCallbackFunction = (err: Error | undefined, data: GatherCompletionRecord) => void; + type CompletionProviderCallback = (this: import("./src/autocomplete").Autocomplete, err: Error | undefined, completions: import("./src/autocomplete").FilteredList, finished: boolean) => void; + + type AcePopupNavigation = "up" | "down" | "start" | "end"; + + interface EditorMultiSelectProperties { + inMultiSelectMode?: boolean, + /** + * Updates the cursor and marker layers. + **/ + updateSelectionMarkers: () => void, + /** + * Adds the selection and cursor. + * @param orientedRange A range containing a cursor + **/ + addSelectionMarker: (orientedRange: Ace.Range & { marker? }) => Ace.Range & { marker? }, + /** + * Removes the selection marker. + * @param range The selection range added with [[Editor.addSelectionMarker `addSelectionMarker()`]]. + **/ + removeSelectionMarker: (range: Ace.Range & { marker? }) => void, + removeSelectionMarkers: (ranges: (Ace.Range & { marker? })[]) => void, + $onAddRange: (e) => void, + $onRemoveRange: (e) => void, + $onMultiSelect: (e) => void, + $onSingleSelect: (e) => void, + $onMultiSelectExec: (e) => void, + /** + * Executes a command for each selection range. + * @param cmd The command to execute + * @param [args] Any arguments for the command + **/ + forEachSelection: (cmd: Object, args?: string, options?: Object) => void, + /** + * Removes all the selections except the last added one. + **/ + exitMultiSelectMode: () => void, + getSelectedText: () => string, + $checkMultiselectChange: (e, anchor: Ace.Anchor) => void, + /** + * Finds and selects all the occurrences of `needle`. + * @param needle The text to find + * @param options The search options + * @param additive keeps + * @returns {Number} The cumulative count of all found matches + **/ + findAll: (needle?: string, options?: Partial, additive?: boolean) => number, + /** + * Adds a cursor above or below the active cursor. + * @param dir The direction of lines to select: -1 for up, 1 for down + * @param [skip] If `true`, removes the active selection range + */ + selectMoreLines: (dir: number, skip?: boolean) => void, + /** + * Transposes the selected ranges. + * @param {Number} dir The direction to rotate selections + **/ + transposeSelections: (dir: number) => void, + /** + * Finds the next occurrence of text in an active selection and adds it to the selections. + * @param {Number} dir The direction of lines to select: -1 for up, 1 for down + * @param {Boolean} [skip] If `true`, removes the active selection range + * @param {Boolean} [stopAtFirst] + **/ + selectMore: (dir: number, skip?: boolean, stopAtFirst?: boolean) => void, + /** + * Aligns the cursors or selected text. + **/ + alignCursors: () => void, + $reAlignText: (lines: string[], forceLeft: boolean) => string[], + multiSelect?: any, + $multiselectOnSessionChange?: any, + $blockSelectEnabled?: boolean, + } + + interface CodeLenseEditorExtension { + codeLensProviders?: any[]; + $codeLensClickHandler?: any; + $updateLenses?: () => void; + $updateLensesOnInput?: () => void; + } + + interface ElasticTabstopsEditorExtension { + elasticTabstops?: import("./src/ext/elastic_tabstops_lite").ElasticTabstopsLite; + } + + interface TextareaEditorExtension { + setDisplaySettings?: (settings: any) => void; + } + + interface PromptEditorExtension { + cmdLine?: Editor; + } + + interface OptionsEditorExtension { + $options?: any; + } + + interface MultiSelectProperties { + ranges: Ace.Range[] | null; + rangeList: Ace.RangeList | null; + + /** + * Adds a range to a selection by entering multiselect mode, if necessary. + * @param {Ace.Range} range The new range to add + * @param {Boolean} [$blockChangeEvents] Whether or not to block changing events + **/ + addRange(range: Ace.Range, $blockChangeEvents?: boolean): any; + + inMultiSelectMode: boolean; + + /** + * @param {Ace.Range} [range] + **/ + toSingleRange(range?: Ace.Range): void; + + /** + * Removes a Range containing pos (if it exists). + * @param {Ace.Point} pos The position to remove, as a `{row, column}` object + **/ + substractPoint(pos: Ace.Point): any; + + /** + * Merges overlapping ranges ensuring consistency after changes + **/ + mergeOverlappingRanges(): void; + + /** + * @param {Ace.Range} range + */ + $onAddRange(range: Ace.Range): void; + + rangeCount: number; + + /** + * + * @param {Ace.Range[]} removed + */ + $onRemoveRange(removed: Ace.Range[]): void; + + /** + * adds multicursor support to selection + */ + $initRangeList(): void; + + /** + * Returns a concatenation of all the ranges. + * @returns {Ace.Range[]} + **/ + getAllRanges(): Ace.Range[]; + + /** + * Splits all the ranges into lines. + **/ + splitIntoLines(): void; + + /** + */ + joinSelections(): void; + + /** + **/ + toggleBlockSelection(): void; + + /** + * + * Gets list of ranges composing rectangular block on the screen + * + * @param {Ace.ScreenCoordinates} screenCursor The cursor to use + * @param {Ace.ScreenCoordinates} screenAnchor The anchor to use + * @param {Boolean} [includeEmptyLines] If true, this includes ranges inside the block which are empty due to clipping + * @returns {Ace.Range[]} + **/ + rectangularRangeBlock(screenCursor: Ace.ScreenCoordinates, screenAnchor: Ace.ScreenCoordinates, includeEmptyLines?: boolean): Ace.Range[]; + + _eventRegistry?: any; + index?: number; + } + + type AcePopupEventsCombined = Ace.EditorEvents & Ace.AcePopupEvents; + type AcePopupWithEditor = Ace.EventEmitter & Ace.Editor; + type InlineAutocompleteAction = "prev" | "next" | "first" | "last"; + + type TooltipCommandFunction = (editor: Ace.Editor) => T; + + export interface TooltipCommand extends Ace.Command { + enabled: TooltipCommandFunction | boolean, + getValue?: TooltipCommandFunction, + type: "button" | "text" | "checkbox" + iconCssClass?: string, + cssClass?: string + } + + export type CommandBarTooltip = import("./src/ext/command_bar").CommandBarTooltip; + + export type TokenizeResult = Array> +} + + +export const version: string; +export const config: Ace.Config; + +export function require(name: string): any; + +export function edit(el: string | (Element & { + env?; + value?; +}), options?: Partial): Ace.Editor; + +export function createEditSession(text: Ace.Document | string, mode: Ace.SyntaxMode): Ace.EditSession; + +export const VirtualRenderer: { + new(container: HTMLElement, theme?: string): Ace.VirtualRenderer; +}; +export const EditSession: { + new(text: string | Ace.Document, mode?: Ace.SyntaxMode): Ace.EditSession; +}; +export const UndoManager: { + new(): Ace.UndoManager; +}; +export const Editor: { + new(renderer: Ace.VirtualRenderer, session?: Ace.EditSession, options?: Partial): Ace.Editor; +}; +export const Range: { + new(startRow: number, startColumn: number, endRow: number, endColumn: number): Ace.Range; + fromPoints(start: Ace.Point, end: Ace.Point): Ace.Range; + comparePoints(p1: Ace.Point, p2: Ace.Point): number; +}; + +export type InlineAutocomplete = Ace.InlineAutocomplete; +export type CommandBarTooltip = Ace.CommandBarTooltip; + + +declare module "./src/anchor" { + export interface Anchor extends Ace.EventEmitter { + markerId?: number; + document: Ace.Document; + } + + +} + +declare module "./src/autocomplete" { + export interface Autocomplete { + popup: Ace.AcePopup; + emptyMessage?: Function, + } + + export interface CompletionProvider { + completions: Ace.FilteredList; + } +} + +declare module "./src/background_tokenizer" { + export interface BackgroundTokenizer extends Ace.EventEmitter { + + } +} + +declare module "./src/document" { + export interface Document extends Ace.EventEmitter { + + } + +} + +declare module "./src/editor" { + export interface Editor extends Ace.EditorMultiSelectProperties, Ace.OptionsProvider, + Ace.EventEmitter, Ace.CodeLenseEditorExtension, Ace.ElasticTabstopsEditorExtension, + Ace.TextareaEditorExtension, Ace.PromptEditorExtension, Ace.OptionsEditorExtension { + session: Ace.EditSession; + $mergeUndoDeltas?: any, + $highlightSelectedWord?: boolean, + $updatePlaceholder?: Function, + $cursorStyle?: string, + $readOnly?: any, + $highlightActiveLine?: any, + $enableAutoIndent?: any, + $copyWithEmptySelection?: any + $selectionStyle?: string, + env?: any; + widgetManager?: Ace.LineWidgets, + completer?: Ace.Autocomplete | Ace.InlineAutocomplete, + completers: Ace.Completer[], + $highlightTagPending?: boolean, + showKeyboardShortcuts?: () => void, + showSettingsMenu?: () => void, + searchBox?: Ace.SearchBox, + _eventRegistry?: any, + } +} + +declare module "./src/edit_session" { + export interface EditSession extends Ace.EventEmitter, + Ace.OptionsProvider, + Ace.Folding, Ace.BracketMatch { + doc: Ace.Document, + $highlightLineMarker?: { + start: Ace.Point, + end: Ace.Point, + id?: number + } + $useSoftTabs?: boolean, + $tabSize?: number, + $useWorker?: boolean, + $wrapAsCode?: boolean, + $indentedSoftWrap?: boolean, + widgetManager?: any, + $bracketHighlight?: any, + $selectionMarker?: number, + curOp?: { + command: {}, + args: string, + scrollTop: number, + [key: string]: any; + }, + lineWidgetsWidth?: number, + $getWidgetScreenLength?: () => number, + _changedWidgets?: any, + $options: any, + $wrapMethod?: any, + $enableVarChar?: any, + $wrap?: any, + $navigateWithinSoftTabs?: boolean, + + getSelectionMarkers(): any[], + + $selectionMarkers?: any[], + gutterRenderer?: any, + $firstLineNumber?: number, + $emacsMark?: any, + selectionMarkerCount?: number, + multiSelect?: any, + $occurHighlight?: any, + $occur?: Ace.Occur, + $occurMatchingLines?: any, + $useEmacsStyleLineStart?: boolean, + $selectLongWords?: boolean, + } + +} + +declare module "./src/edit_session/fold" { + export interface Fold { + collapseChildren?: number; + } +} + +// @ts-expect-error +declare module "./src/placeholder" { + export interface PlaceHolder extends Ace.EventEmitter { + } +} + +declare module "./src/scrollbar" { + export interface VScrollBar extends Ace.EventEmitter { + } + + export interface HScrollBar extends Ace.EventEmitter { + } +} + +declare module "./src/scrollbar_custom" { + export interface VScrollBar extends Ace.EventEmitter { + } + + export interface HScrollBar extends Ace.EventEmitter { + } +} + +declare module "./src/line_widgets" { + export interface LineWidgets { + lineWidgets: Ace.LineWidget[]; + editor: Ace.Editor; + } +} + +declare module "./src/selection" { + export interface Selection extends Ace.EventEmitter, Ace.MultiSelectProperties { + } + +} + +declare module "./src/range" { + export interface Range { + id?: number; + cursor?: Ace.Point; + isBackwards?: boolean; + } +} + +declare module "./src/virtual_renderer" { + export interface VirtualRenderer extends Ace.EventEmitter, Ace.OptionsProvider { + $customScrollbar?: boolean, + $extraHeight?: number, + $showGutter?: boolean, + $showPrintMargin?: boolean, + $printMarginColumn?: number, + $animatedScroll?: boolean, + $isMousePressed?: boolean, + textarea?: HTMLTextAreaElement, + $hScrollBarAlwaysVisible?: boolean, + $vScrollBarAlwaysVisible?: boolean + $maxLines?: number, + $scrollPastEnd?: number, + enableKeyboardAccessibility?: boolean, + keyboardFocusClassName?: string, + $highlightGutterLine?: boolean, + $minLines?: number, + $maxPixelHeight?: number, + $gutterWidth?: number, + showInvisibles?: boolean, + $hasCssTransforms?: boolean, + $blockCursor?: boolean, + $useTextareaForIME?: boolean, + theme?: any, + $theme?: any, + destroyed?: boolean, + session: Ace.EditSession, + } + +} + +declare module "./src/snippets" { + export interface SnippetManager extends Ace.EventEmitter { + } +} + +declare module "./src/ext/command_bar" { + export interface CommandBarTooltip extends Ace.EventEmitter { + $shouldHideMoreOptions?: boolean, + } +} + +declare module "./src/commands/command_manager" { + export interface CommandManager extends Ace.EventEmitter { + $checkCommandState?: boolean + } +} + +declare module "./src/autocomplete/popup" { + + export interface AcePopup extends Ace.AcePopupWithEditor { + setSelectOnHover: (val: boolean) => void, + setRow: (line: number) => void, + getRow: () => number, + getHoveredRow: () => number, + filterText: string, + isOpen: boolean, + isTopdown: boolean, + autoSelect: boolean, + data: Ace.Completion[], + setData: (data: Ace.Completion[], filterText: string) => void, + getData: (row: number) => Ace.Completion, + hide: () => void, + anchor: "top" | "bottom", + anchorPosition: Ace.Point, + tryShow: (pos: any, lineHeight: number, anchor: "top" | "bottom", forceShow?: boolean) => boolean, + $borderSize: number, + show: (pos: any, lineHeight: number, topdownOnly?: boolean) => void, + goTo: (where: Ace.AcePopupNavigation) => void, + getTextLeftOffset: () => number, + $imageSize: number, + anchorPos: any, + isMouseOver?: boolean + } +} + +declare module "./src/layer/cursor" { + export interface Cursor { + timeoutId?: number; + } +} + +declare module "./src/layer/gutter" { + export interface Gutter extends Ace.EventEmitter { + $useSvgGutterIcons?: boolean, + $showFoldedAnnotations?: boolean, + } +} + +declare module "./src/layer/text" { + export interface Text extends Ace.EventEmitter { + } +} + +declare module "./src/lib/app_config" { + export interface AppConfig extends Ace.EventEmitter { + } +} + +declare module "./src/mouse/mouse_event" { + export interface MouseEvent { + time?: number; + } +} + +declare module "./src/mouse/mouse_handler" { + + export interface MouseHandler { + $tooltipFollowsMouse?: boolean, + cancelDrag?: boolean + //from DefaultHandlers + $clickSelection?: Ace.Range, + mousedownEvent?: Ace.MouseEvent, + startSelect?: (pos?: Ace.Point, waitForClickSelection?: boolean) => void, + select?: () => void + $lastScroll?: { t: number, vx: number, vy: number, allowed: number } + selectEnd?: () => void + } +} + +// @ts-expect-error +declare module "./src/ext/options" { + export interface OptionPanel extends Ace.EventEmitter { + } +} + +declare module "./src/layer/font_metrics" { + export interface FontMetrics extends Ace.EventEmitter { + } +} + +declare module "./src/tooltip" { + export interface HoverTooltip { + row: number; + } +} + +declare module "./src/mouse/default_gutter_handler" { + export interface GutterHandler { + } +} diff --git a/package.json b/package.json index bd3b22299da..d5e9b724bc7 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "eslint": "^8.20.0", "istanbul": "^0.4.5", "standard-version": "^9.3.2", - "typescript": "^4.7.4" + "typescript": "^5.2.2" }, "mappings": { "ace": "." @@ -44,6 +44,7 @@ "cover": "istanbul cover src/test/all.js", "lint": "eslint \"src/**/*.js\"", "fix": "eslint --fix \"src/**/*.js\"", + "typecheck": "tsc -p tsconfig.json", "changelog": "standard-version", "prepack": "node tool/esm_resolver_generator.js && node Makefile.dryice.js css --target build-styles && rm -rf styles && mv build-styles/css styles" }, diff --git a/src/ace.js b/src/ace.js index 5198e4cf6cb..86ea484a156 100644 --- a/src/ace.js +++ b/src/ace.js @@ -1,9 +1,8 @@ /** * The main class required to set up an Ace instance in the browser. * - * @class Ace + * @namespace Ace **/ - "use strict"; "include loader_build"; @@ -29,9 +28,9 @@ exports.config = require("./config"); /** * Embeds the Ace editor into the DOM, at the element provided by `el`. - * @param {String | Element} el Either the id of an element, or the element itself - * @param {Object } options Options for the editor - * + * @param {String | HTMLElement & {env?, value?}} el Either the id of an element, or the element itself + * @param {Object } [options] Options for the editor + * @returns {Editor} **/ exports.edit = function(el, options) { if (typeof el == "string") { @@ -56,7 +55,6 @@ exports.edit = function(el, options) { } var doc = exports.createEditSession(value); - var editor = new Editor(new Renderer(el), doc, options); var env = { @@ -74,9 +72,9 @@ exports.edit = function(el, options) { /** * Creates a new [[EditSession]], and returns the associated [[Document]]. - * @param {Document | String} text {:textParam} - * @param {TextMode} mode {:modeParam} - * + * @param {import('./document').Document | String} text {:textParam} + * @param {import("../ace-internal").Ace.SyntaxMode} [mode] {:modeParam} + * @returns {EditSession} **/ exports.createEditSession = function(text, mode) { var doc = new EditSession(text, mode); diff --git a/src/anchor.js b/src/anchor.js index 06884e56bd6..b5d112c9a6d 100644 --- a/src/anchor.js +++ b/src/anchor.js @@ -1,5 +1,7 @@ "use strict"; - +/** + * @typedef {import("./document").Document} Document + */ var oop = require("./lib/oop"); var EventEmitter = require("./lib/event_emitter").EventEmitter; @@ -11,14 +13,14 @@ class Anchor { * Creates a new `Anchor` and associates it with a document. * * @param {Document} doc The document to associate with the anchor - * @param {Number} row The starting row position - * @param {Number} column The starting column position + * @param {Number|import("../ace-internal").Ace.Point} row The starting row position + * @param {Number} [column] The starting column position **/ constructor(doc, row, column) { this.$onChange = this.onChange.bind(this); this.attach(doc); - if (typeof column == "undefined") + if (typeof row != "number") this.setPosition(row.row, row.column); else this.setPosition(row, column); @@ -26,7 +28,7 @@ class Anchor { /** * Returns an object identifying the `row` and `column` position of the current anchor. - * @returns {Ace.Point} + * @returns {import("../ace-internal").Ace.Point} **/ getPosition() { return this.$clipPositionToDocument(this.row, this.column); @@ -41,22 +43,9 @@ class Anchor { return this.document; } - /** - * Fires whenever the anchor position changes. - * - * Both of these objects have a `row` and `column` property corresponding to the position. - * - * Events that can trigger this function include [[Anchor.setPosition `setPosition()`]]. - * - * @event change - * @param {Object} e An object containing information about the anchor position. It has two properties: - * - `old`: An object describing the old Anchor position - * - `value`: An object describing the new Anchor position - * - **/ /** * Internal function called when `"change"` event fired. - * @param {Ace.Delta} delta + * @param {import("../ace-internal").Ace.Delta} delta */ onChange(delta) { if (delta.start.row == delta.end.row && delta.start.row != this.row) @@ -73,8 +62,7 @@ class Anchor { * Sets the anchor position to the specified row and column. If `noClip` is `true`, the position is not clipped. * @param {Number} row The row index to move the anchor to * @param {Number} column The column index to move the anchor to - * @param {Boolean} noClip Identifies if you want the position to be clipped - * + * @param {Boolean} [noClip] Identifies if you want the position to be clipped **/ setPosition(row, column, noClip) { var pos; @@ -117,6 +105,7 @@ class Anchor { * **/ attach(doc) { + /**@type{Document}*/ this.document = doc || this.document; this.document.on("change", this.$onChange); } @@ -125,7 +114,7 @@ class Anchor { * Clips the anchor position to the specified row and column. * @param {Number} row The row index to clip the anchor to * @param {Number} column The column index to clip the anchor to - * @returns {Ace.Point} + * @returns {import("../ace-internal").Ace.Point} * **/ $clipPositionToDocument(row, column) { diff --git a/src/apply_delta.js b/src/apply_delta.js index de43678f009..5d2fffb1925 100644 --- a/src/apply_delta.js +++ b/src/apply_delta.js @@ -40,6 +40,12 @@ function validateDelta(docLines, delta) { throwDeltaError(delta, "delta.range must match delta lines"); } +/** + * Applies a delta to a document. + * @param {string[]} docLines + * @param {import("../ace-internal").Ace.Delta} delta + * @param [doNotValidate] + */ exports.applyDelta = function(docLines, delta, doNotValidate) { // disabled validation since it breaks autocompletion popup // if (!doNotValidate) @@ -54,6 +60,7 @@ exports.applyDelta = function(docLines, delta, doNotValidate) { if (lines.length === 1) { docLines[row] = line.substring(0, startColumn) + delta.lines[0] + line.substring(startColumn); } else { + // @ts-ignore var args = [row, 1].concat(delta.lines); docLines.splice.apply(docLines, args); docLines[row] = line.substring(0, startColumn) + docLines[row]; diff --git a/src/autocomplete.js b/src/autocomplete.js index 638e0553ad1..01d2762c8b7 100644 --- a/src/autocomplete.js +++ b/src/autocomplete.js @@ -1,5 +1,9 @@ "use strict"; - +/** + * @typedef {import("./editor").Editor} Editor + * @typedef {import("../ace-internal").Ace.CompletionProviderOptions} CompletionProviderOptions + * @typedef {import("../ace-internal").Ace.CompletionOptions} CompletionOptions + */ var HashHandler = require("./keyboard/hash_handler").HashHandler; var AcePopup = require("./autocomplete/popup").AcePopup; var AceInline = require("./autocomplete/inline").AceInline; @@ -22,26 +26,34 @@ var event = require("./lib/event"); * @property {string} [docText] - a plain text that would be displayed as an additional popup. If `docHTML` exists, * it would be used instead of `docText`. * @property {string} [completerId] - the identifier of the completer - * @property {Ace.Range} [range] - An object specifying the range of text to be replaced with the new completion value (experimental) + * @property {import("../ace-internal").Ace.IRange} [range] - An object specifying the range of text to be replaced with the new completion value (experimental) * @property {string} [command] - A command to be executed after the completion is inserted (experimental) + * @property {string} [snippet] - a text snippet that would be inserted when the completion is selected + * @property {string} [value] - The text that would be inserted when selecting this completion. + * @property {import("../ace-internal").Ace.Completer & {insertMatch:(editor: Editor, data: Completion) => void}} [completer] + * @property {boolean} [hideInlinePreview] + * @export */ /** - * @typedef SnippetCompletion - * @extends BaseCompletion - * @property {string} snippet - a text snippet that would be inserted when the completion is selected + * @typedef {BaseCompletion & {snippet: string}} SnippetCompletion + * @property {string} snippet + * @property {string} [value] + * @export */ /** - * @typedef ValueCompletion - * @extends BaseCompletion - * @property {string} value - The text that would be inserted when selecting this completion. + * @typedef {BaseCompletion & {value: string}} ValueCompletion + * @property {string} value + * @property {string} [snippet] + * @export */ /** * Represents a suggested text snippet intended to complete a user's input * @typedef Completion * @type {SnippetCompletion|ValueCompletion} + * @export */ var destroyCompleter = function(e, editor) { @@ -97,7 +109,7 @@ class Autocomplete { this.stickySelection = true; }.bind(this), this.stickySelectionDelay); - this.$firstOpenTimer = lang.delayedCall(function() { + this.$firstOpenTimer = lang.delayedCall(/**@this{Autocomplete}*/function() { var initialPosition = this.completionProvider && this.completionProvider.initialPosition; if (this.autoShown || (this.popup && this.popup.isOpen) || !initialPosition) return; @@ -114,6 +126,7 @@ class Autocomplete { } $init() { + /**@type {AcePopup}**/ this.popup = new AcePopup(this.parentNode || document.body || document.documentElement); this.popup.on("click", function(e) { this.insertMatch(); @@ -137,6 +150,9 @@ class Autocomplete { return this.inlineRenderer; } + /** + * @return {AcePopup} + */ getPopup() { return this.popup || this.$init(); } @@ -168,6 +184,7 @@ class Autocomplete { // If the mouse is over the tooltip, and we're changing selection on hover don't // move the tooltip while hovering over the popup. if (this.popup.isMouseOver && this.setSelectOnHover) { + // @ts-expect-error TODO: potential wrong arguments this.tooltipTimer.call(null, null); return; } @@ -176,7 +193,9 @@ class Autocomplete { this.popupTimer.schedule(); this.tooltipTimer.schedule(); } else { + // @ts-expect-error TODO: potential wrong arguments this.popupTimer.call(null, null); + // @ts-expect-error TODO: potential wrong arguments this.tooltipTimer.call(null, null); } } @@ -215,9 +234,11 @@ class Autocomplete { this.$elements = elements; } unObserveLayoutChanges() { + // @ts-expect-error This is expected for some browsers window.removeEventListener("resize", this.onLayoutChange, {passive: true}); window.removeEventListener("wheel", this.mousewheelListener); this.$elements && this.$elements.forEach((el) => { + // @ts-expect-error This is expected for some browsers el.removeEventListener("scroll", this.onLayoutChange, {passive: true}); }); this.$elements = null; @@ -271,6 +292,11 @@ class Autocomplete { this.popup.show(pos, lineHeight); } + /** + * @param {Editor} editor + * @param {string} prefix + * @param {boolean} [keepPopupPosition] + */ openPopup(editor, prefix, keepPopupPosition) { this.$firstOpenTimer.cancel(); @@ -402,6 +428,11 @@ class Autocomplete { this.popup.goTo(where); } + /** + * @param {Completion} data + * @param {undefined} [options] + * @return {boolean | void} + */ insertMatch(data, options) { if (!data) data = this.popup.getData(this.popup.getRow()); @@ -410,6 +441,7 @@ class Autocomplete { if (data.value === "") // Explicitly given nothing to insert, e.g. "No suggestion state" return this.detach(); var completions = this.completions; + // @ts-expect-error TODO: potential wrong arguments var result = this.getCompletionProvider().insertMatch(this.editor, data, completions.filterText, options); // detach only if new popup was not opened while inserting match if (this.completions == completions) @@ -458,6 +490,10 @@ class Autocomplete { return this.getCompletionProvider().gatherCompletions(editor, callback); } + /** + * @param {boolean} keepPopupPosition + * @param {CompletionOptions} options + */ updateCompletions(keepPopupPosition, options) { if (keepPopupPosition && this.base && this.completions) { var pos = this.editor.getCursorPosition(); @@ -490,43 +526,49 @@ class Autocomplete { this.base.$insertRight = true; var completionOptions = { exactMatch: this.exactMatch, + // @ts-expect-error TODO: couldn't find initializer ignoreCaption: this.ignoreCaption }; this.getCompletionProvider({ prefix, pos - }).provideCompletions(this.editor, completionOptions, function(err, completions, finished) { - var filtered = completions.filtered; - var prefix = util.getCompletionPrefix(this.editor); + }).provideCompletions(this.editor, completionOptions, + /** + * @type {(err: any, completions: FilteredList, finished: boolean) => void | boolean} + * @this {Autocomplete} + */ + function (err, completions, finished) { + var filtered = completions.filtered; + var prefix = util.getCompletionPrefix(this.editor); this.$firstOpenTimer.cancel(); - if (finished) { - // No results - if (!filtered.length) { - var emptyMessage = !this.autoShown && this.emptyMessage; - if (typeof emptyMessage == "function") - emptyMessage = this.emptyMessage(prefix); - if (emptyMessage) { + if (finished) { + // No results + if (!filtered.length) { + var emptyMessage = !this.autoShown && this.emptyMessage; + if (typeof emptyMessage == "function") emptyMessage = this.emptyMessage(prefix); + if (emptyMessage) { var completionsForEmpty = [{ caption: emptyMessage, - value: "" - }]; - this.completions = new FilteredList(completionsForEmpty); - this.openPopup(this.editor, prefix, keepPopupPosition); + value: "" + } + ]; + this.completions = new FilteredList(completionsForEmpty); + this.openPopup(this.editor, prefix, keepPopupPosition); this.popup.renderer.setStyle("ace_loading", false); - return; + return; + } + return this.detach(); } - return this.detach(); - } - // One result equals to the prefix - if (filtered.length == 1 && filtered[0].value == prefix && !filtered[0].snippet) - return this.detach(); + // One result equals to the prefix + if (filtered.length == 1 && filtered[0].value == prefix + && !filtered[0].snippet) return this.detach(); - // Autoinsert if one result - if (this.autoInsert && !this.autoShown && filtered.length == 1) - return this.insertMatch(filtered[0]); - } + // Autoinsert if one result + if (this.autoInsert && !this.autoShown && filtered.length == 1) return this.insertMatch( + filtered[0]); + } // If showLoadingState is true and there is still a completer loading, show 'Loading...' // in the top row of the completer popup. this.completions = !finished && this.showLoadingState ? @@ -535,7 +577,7 @@ class Autocomplete { ) : completions; - this.openPopup(this.editor, prefix, keepPopupPosition); + this.openPopup(this.editor, prefix, keepPopupPosition); this.popup.renderer.setStyle("ace_loading", !finished); }.bind(this)); @@ -578,7 +620,7 @@ class Autocomplete { showDocTooltip(item) { if (!this.tooltipNode) { this.tooltipNode = dom.createElement("div"); - this.tooltipNode.style.margin = 0; + this.tooltipNode.style.margin = "0"; this.tooltipNode.style.pointerEvents = "auto"; this.tooltipNode.tabIndex = -1; this.tooltipNode.onblur = this.blurListener.bind(this); @@ -702,9 +744,9 @@ Autocomplete.for = function(editor) { editor.completer = null; } if (config.get("sharedPopups")) { - if (!Autocomplete.$sharedInstance) - Autocomplete.$sharedInstance = new Autocomplete(); - editor.completer = Autocomplete.$sharedInstance; + if (!Autocomplete["$sharedInstance"]) + Autocomplete["$sharedInstance"] = new Autocomplete(); + editor.completer = Autocomplete["$sharedInstance"]; } else { editor.completer = new Autocomplete(); editor.once("destroy", destroyCompleter); @@ -730,15 +772,22 @@ Autocomplete.startCommand = { * This class is responsible for providing completions and inserting them to the editor */ class CompletionProvider { + /** - * @param {{pos: Position, prefix: string}} initialPosition + * @param {{pos: import("../ace-internal").Ace.Position, prefix: string}} initialPosition */ constructor(initialPosition) { this.initialPosition = initialPosition; this.active = true; } - + + /** + * @param {Editor} editor + * @param {number} index + * @param {CompletionProviderOptions} [options] + * @returns {boolean} + */ insertByIndex(editor, index, options) { if (!this.completions || !this.completions.filtered) { return false; @@ -746,6 +795,12 @@ class CompletionProvider { return this.insertMatch(editor, this.completions.filtered[index], options); } + /** + * @param {Editor} editor + * @param {Completion} data + * @param {CompletionProviderOptions} [options] + * @returns {boolean} + */ insertMatch(editor, data, options) { if (!data) return false; @@ -799,11 +854,19 @@ class CompletionProvider { return true; } + /** + * @param {Editor} editor + * @param {Completion} data + */ $insertString(editor, data) { var text = data.value || data; editor.execCommand("insertstring", text); } + /** + * @param {Editor} editor + * @param {import("../ace-internal").Ace.CompletionCallbackFunction} callback + */ gatherCompletions(editor, callback) { var session = editor.getSession(); var pos = editor.getCursorPosition(); @@ -838,7 +901,7 @@ class CompletionProvider { * The callback function may be called multiple times, the last invokation is marked with a `finished` flag * @param {Editor} editor * @param {CompletionProviderOptions} options - * @param {CompletionProviderCallback} callback + * @param {(err: Error | undefined, completions: FilteredList | [], finished: boolean) => void} callback */ provideCompletions(editor, options, callback) { var processResults = function(results) { diff --git a/src/autocomplete/inline.js b/src/autocomplete/inline.js index 68b20b9a20f..67b02d0f838 100644 --- a/src/autocomplete/inline.js +++ b/src/autocomplete/inline.js @@ -1,5 +1,7 @@ "use strict"; - +/** + * @typedef {import("../editor").Editor} Editor + */ var snippetManager = require("../snippets").snippetManager; var AceInlineScreenReader = require("./inline_screenreader").AceInlineScreenReader; @@ -17,7 +19,7 @@ class AceInline { /** * Renders the completion as ghost text to the current cursor position * @param {Editor} editor - * @param {Completion} completion + * @param {import("../../ace-internal").Ace.Completion} completion * @param {string} prefix * @returns {boolean} True if the completion could be rendered to the editor, false otherwise */ diff --git a/src/autocomplete/inline_screenreader.js b/src/autocomplete/inline_screenreader.js index 9df04c2916a..93b7ed0e036 100644 --- a/src/autocomplete/inline_screenreader.js +++ b/src/autocomplete/inline_screenreader.js @@ -6,7 +6,7 @@ class AceInlineScreenReader { /** * Creates the off-screen div in which the ghost text content in redered and which the screen reader reads. - * @param {Editor} editor + * @param {import("../editor").Editor} editor */ constructor(editor) { this.editor = editor; @@ -22,8 +22,8 @@ class AceInlineScreenReader { */ setScreenReaderContent(content) { // Path for when inline preview is used with 'normal' completion popup. - if (!this.popup && this.editor.completer && this.editor.completer.popup) { - this.popup = this.editor.completer.popup; + if (!this.popup && this.editor.completer && /**@type{import("../autocomplete").Autocomplete}*/(this.editor.completer).popup) { + this.popup = /**@type{import("../autocomplete").Autocomplete}*/(this.editor.completer).popup; this.popup.renderer.on("afterRender", function() { let row = this.popup.getRow(); @@ -75,4 +75,4 @@ class AceInlineScreenReader { } } -exports.AceInlineScreenReader = AceInlineScreenReader; \ No newline at end of file +exports.AceInlineScreenReader = AceInlineScreenReader; diff --git a/src/autocomplete/popup.js b/src/autocomplete/popup.js index 196191eea17..fb4abbe8f31 100644 --- a/src/autocomplete/popup.js +++ b/src/autocomplete/popup.js @@ -1,5 +1,4 @@ "use strict"; - var Renderer = require("../virtual_renderer").VirtualRenderer; var Editor = require("../editor").Editor; var Range = require("../range").Range; @@ -9,15 +8,19 @@ var dom = require("../lib/dom"); var nls = require("../config").nls; var userAgent = require("./../lib/useragent"); -var getAriaId = function(index) { +var getAriaId = function (index) { return `suggest-aria-id:${index}`; }; +/** + * + * @param {HTMLElement} [el] + * @return {Editor} + */ var $singleLineEditor = function(el) { var renderer = new Renderer(el); renderer.$maxLines = 4; - var editor = new Editor(renderer); editor.setHighlightActiveLine(false); @@ -37,11 +40,13 @@ var $singleLineEditor = function(el) { class AcePopup { /** * Creates and renders single line editor in popup window. If `parentNode` param is isset, then attaching it to this element. - * @param {Element} parentNode + * @param {Element} [parentNode] */ constructor(parentNode) { var el = dom.createElement("div"); - var popup = new $singleLineEditor(el); + /**@type {AcePopup}*/ + // @ts-ignore + var popup = $singleLineEditor(el); if (parentNode) { parentNode.appendChild(el); @@ -65,13 +70,14 @@ class AcePopup { popup.$isFocused = true; popup.renderer.$cursorLayer.restartTimer = noop; - popup.renderer.$cursorLayer.element.style.opacity = 0; + popup.renderer.$cursorLayer.element.style.opacity = "0"; popup.renderer.$maxLines = 8; popup.renderer.$keepTextAreaAtCursor = false; popup.setHighlightActiveLine(false); // set default highlight color + // @ts-ignore popup.session.highlight(""); popup.session.$searchHighlight.clazz = "ace_highlight-marker"; @@ -83,10 +89,10 @@ class AcePopup { }); var lastMouseEvent; - var hoverMarker = new Range(-1,0,-1,Infinity); - var selectionMarker = new Range(-1,0,-1,Infinity); + var hoverMarker = new Range(-1, 0, -1, Infinity); + var selectionMarker = new Range(-1, 0, -1, Infinity); selectionMarker.id = popup.session.addMarker(selectionMarker, "ace_active-line", "fullLine"); - popup.setSelectOnHover = function(val) { + popup.setSelectOnHover = function (val) { if (!val) { hoverMarker.id = popup.session.addMarker(hoverMarker, "ace_line-hover", "fullLine"); } else if (hoverMarker.id) { @@ -122,8 +128,9 @@ class AcePopup { setHoverMarker(row, true); } }); - popup.renderer.on("afterRender", function() { + popup.renderer.on("afterRender", function () { var row = popup.getRow(); + /**@type {any}*/ var t = popup.renderer.$textLayer; var selected = t.element.childNodes[row - t.config.firstRow]; var el = document.activeElement; // Active element is textarea of main editor @@ -144,7 +151,7 @@ class AcePopup { selected.setAttribute("aria-roledescription", nls("item")); selected.setAttribute("aria-label", popup.getData(row).value); selected.setAttribute("aria-setsize", popup.data.length); - selected.setAttribute("aria-posinset", row+1); + selected.setAttribute("aria-posinset", row + 1); selected.setAttribute("aria-describedby", "doc-tooltip"); selected.setAttribute("aria-selected", "true"); } @@ -181,6 +188,7 @@ class AcePopup { var bgTokenizer = popup.session.bgTokenizer; bgTokenizer.$tokenizeRow = function(row) { + /**@type {import("../../ace-internal").Ace.Completion &{name?, className?, matchMask?, message?}}*/ var data = popup.data[row]; var tokens = []; if (!data) @@ -282,7 +290,7 @@ class AcePopup { * If the anchor is not specified it tries to align to bottom and right as much as possible. * If the popup does not have enough space to be rendered with the given anchors, it returns false without rendering the popup. * The forceShow flag can be used to render the popup in these cases, which slides the popup so it entirely fits on the screen. - * @param {Point} pos + * @param {{top: number, left: number}} pos * @param {number} lineHeight * @param {"top" | "bottom" | undefined} anchor * @param {boolean} forceShow @@ -398,7 +406,7 @@ class AcePopup { return popup; } -} +} dom.importCssString(` .ace_editor.ace_autocomplete .ace_marker-layer .ace_active-line { diff --git a/src/autocomplete/util.js b/src/autocomplete/util.js index 97228e94376..51619407a87 100644 --- a/src/autocomplete/util.js +++ b/src/autocomplete/util.js @@ -40,6 +40,10 @@ exports.retrieveFollowingIdentifier = function(text, pos, regex) { return buf; }; +/** + * @param editor + * @return {string} + */ exports.getCompletionPrefix = function (editor) { var pos = editor.getCursorPosition(); var line = editor.session.getLine(pos.row); diff --git a/src/background_tokenizer.js b/src/background_tokenizer.js index 2729e38d93e..f52649caea8 100644 --- a/src/background_tokenizer.js +++ b/src/background_tokenizer.js @@ -1,9 +1,12 @@ "use strict"; - +/** + * @typedef {import("./document").Document} Document + * @typedef {import("./edit_session").EditSession} EditSession + * @typedef {import("./tokenizer").Tokenizer} Tokenizer + */ var oop = require("./lib/oop"); var EventEmitter = require("./lib/event_emitter").EventEmitter; - /** * Tokenizes the current [[Document `Document`]] in the background, and caches the tokenized rows for future use. * @@ -14,9 +17,10 @@ class BackgroundTokenizer { /** * Creates a new `BackgroundTokenizer` object. * @param {Tokenizer} tokenizer The tokenizer to use - * @param {Editor} editor The editor to associate with + * @param {EditSession} [session] The editor session to associate with **/ - constructor(tokenizer, editor) { + constructor(tokenizer, session) { + /**@type {false|number}*/ this.running = false; this.lines = []; this.states = []; @@ -49,6 +53,7 @@ class BackgroundTokenizer { // only check every 5 lines processedLines ++; + // @ts-ignore if ((processedLines % 5 === 0) && (new Date() - workerStart) > 20) { self.running = setTimeout(self.$worker, 20); break; @@ -88,13 +93,7 @@ class BackgroundTokenizer { this.stop(); } - /** - * Fires whenever the background tokeniziers between a range of rows are going to be updated. - * - * @event update - * @param {Object} e An object containing two properties, `first` and `last`, which indicate the rows of the region being updated. - * - **/ + /** * Emits the `'update'` event. `firstRow` and `lastRow` are used to define the boundaries of the region to be updated. * @param {Number} firstRow The starting row region @@ -132,6 +131,9 @@ class BackgroundTokenizer { this.running = setTimeout(this.$worker, 700); } + /** + * @param {import("../ace-internal").Ace.Delta} delta + */ $updateOnChange(delta) { var startRow = delta.start.row; var len = delta.end.row - startRow; @@ -165,7 +167,7 @@ class BackgroundTokenizer { /** * Gives list of [[Token]]'s of the row. (tokens are cached) * @param {Number} row The row to get tokens at - * @returns {Token[]} + * @returns {import("../ace-internal").Ace.Token[]} **/ getTokens(row) { return this.lines[row] || this.$tokenizeRow(row); @@ -182,10 +184,13 @@ class BackgroundTokenizer { return this.states[row] || "start"; } + /** + * @param {number} row + */ $tokenizeRow(row) { var line = this.doc.getLine(row); var state = this.states[row - 1]; - + // @ts-expect-error TODO: potential wrong argument var data = this.tokenizer.getLineTokens(line, state, row); if (this.states[row] + "" !== data.state + "") { diff --git a/src/bidihandler.js b/src/bidihandler.js index 8b065f83da6..003822fcafd 100644 --- a/src/bidihandler.js +++ b/src/bidihandler.js @@ -1,4 +1,7 @@ "use strict"; +/** + * @typedef {import("./edit_session").EditSession} EditSession + */ var bidiUtil = require("./lib/bidiutil"); var lang = require("./lib/lang"); @@ -42,8 +45,8 @@ class BidiHandler { * creates Bidi map to be used in operations related to selection * (keyboard arrays, mouse click, select) * @param {Number} screenRow the screen row to be checked - * @param {Number} docRow the document row to be checked [optional] - * @param {Number} splitIndex the wrapped screen line index [ optional] + * @param {Number} [docRow] the document row to be checked [optional] + * @param {Number} [splitIndex] the wrapped screen line index [ optional] **/ isBidiRow(screenRow, docRow, splitIndex) { if (!this.seenBidi) diff --git a/src/clipboard.js b/src/clipboard.js index 57c6466c1a8..73bb845a680 100644 --- a/src/clipboard.js +++ b/src/clipboard.js @@ -2,6 +2,7 @@ var $cancelT; module.exports = { + /** @type {string|false} */ lineMode: false, pasteCancelled: function() { if ($cancelT && $cancelT > Date.now() - 50) diff --git a/src/commands/command_manager.js b/src/commands/command_manager.js index 76d1ba9bee1..add1f086220 100644 --- a/src/commands/command_manager.js +++ b/src/commands/command_manager.js @@ -1,5 +1,8 @@ "use strict"; - +/** + * + * @typedef {import("../editor").Editor} Editor + */ var oop = require("../lib/oop"); var MultiHashHandler = require("../keyboard/hash_handler").MultiHashHandler; var EventEmitter = require("../lib/event_emitter").EventEmitter; @@ -8,7 +11,7 @@ class CommandManager extends MultiHashHandler{ /** * new CommandManager(platform, commands) * @param {String} platform Identifier for the platform; must be either `"mac"` or `"win"` - * @param {Array} commands A list of commands + * @param {any[]} commands A list of commands **/ constructor(platform, commands) { super(commands, platform); @@ -20,7 +23,14 @@ class CommandManager extends MultiHashHandler{ return e.command.exec(e.editor, e.args, e.event, false); }); } - + + /** + * + * @param {string | string[] | import("../../ace-internal").Ace.Command} command + * @param {Editor} editor + * @param {any} args + * @returns {boolean} + */ exec(command, editor, args) { if (Array.isArray(command)) { for (var i = command.length; i--; ) { @@ -48,6 +58,10 @@ class CommandManager extends MultiHashHandler{ return e.returnValue === false ? false : true; } + /** + * @param {Editor} editor + * @returns {boolean} + */ toggleRecording(editor) { if (this.$inReplay) return; @@ -74,6 +88,9 @@ class CommandManager extends MultiHashHandler{ return this.recording = true; } + /** + * @param {Editor} editor + */ replay(editor) { if (this.$inReplay || !this.macro) return; diff --git a/src/commands/default_commands.js b/src/commands/default_commands.js index 038b8c14be4..1d1b7346b11 100644 --- a/src/commands/default_commands.js +++ b/src/commands/default_commands.js @@ -12,6 +12,7 @@ function bindKey(win, mac) { multiSelectAction: "forEach"|"forEachLine"|function|undefined, scrollIntoView: true|"cursor"|"center"|"selectionPart" */ +/**@type {import("../../ace-internal").Ace.Command[]} */ exports.commands = [{ name: "showSettingsMenu", description: "Show settings menu", diff --git a/src/commands/incremental_search_commands.js b/src/commands/incremental_search_commands.js index c228df9bb49..6c992f98fac 100644 --- a/src/commands/incremental_search_commands.js +++ b/src/commands/incremental_search_commands.js @@ -138,7 +138,10 @@ function IncrementalSearchKeyboardHandler(iSearch) { oop.inherits(IncrementalSearchKeyboardHandler, HashHandler); (function() { - + /** + * @param editor + * @this {IncrementalSearchKeyboardHandler & this & {$commandExecHandler}} + */ this.attach = function(editor) { var iSearch = this.$iSearch; HashHandler.call(this, exports.iSearchCommands, editor.commands.platform); @@ -155,6 +158,10 @@ oop.inherits(IncrementalSearchKeyboardHandler, HashHandler); }); }; + /** + * @this {IncrementalSearchKeyboardHandler & this & {$commandExecHandler}} + * @param editor + */ this.detach = function(editor) { if (!this.$commandExecHandler) return; editor.commands.off('exec', this.$commandExecHandler); @@ -162,9 +169,17 @@ oop.inherits(IncrementalSearchKeyboardHandler, HashHandler); }; var handleKeyboard$super = this.handleKeyboard; + /** + * @param data + * @param hashId + * @param key + * @param keyCode + * @this {IncrementalSearchKeyboardHandler & import("../keyboard/hash_handler").HashHandler} + */ this.handleKeyboard = function(data, hashId, key, keyCode) { if (((hashId === 1/*ctrl*/ || hashId === 8/*command*/) && key === 'v') || (hashId === 1/*ctrl*/ && key === 'y')) return null; + // @ts-ignore var cmd = handleKeyboard$super.call(this, data, hashId, key, keyCode); if (cmd && cmd.command) { return cmd; } if (hashId == -1) { diff --git a/src/commands/multi_select_commands.js b/src/commands/multi_select_commands.js index 2cba5d95b56..3635c8bc119 100644 --- a/src/commands/multi_select_commands.js +++ b/src/commands/multi_select_commands.js @@ -1,4 +1,8 @@ -// commands to enter multiselect mode + +/** + * commands to enter multiselect mode + * @type {import("../../ace-internal").Ace.Command[]} + */ exports.defaultCommands = [{ name: "addCursorAbove", description: "Add cursor above", @@ -86,7 +90,10 @@ exports.defaultCommands = [{ readOnly: true }]; -// commands active only in multiselect mode +/** + * commands active only in multiselect mode + * @type {import("../../ace-internal").Ace.Command[]} + */ exports.multiSelectCommands = [{ name: "singleSelection", description: "Single selection", diff --git a/src/commands/occur_commands.js b/src/commands/occur_commands.js index dfe7268074f..572bb6bd01d 100644 --- a/src/commands/occur_commands.js +++ b/src/commands/occur_commands.js @@ -54,6 +54,7 @@ oop.inherits(OccurKeyboardHandler, HashHandler); var handleKeyboard$super = this.handleKeyboard; this.handleKeyboard = function(data, hashId, key, keyCode) { + // @ts-ignore var cmd = handleKeyboard$super.call(this, data, hashId, key, keyCode); return (cmd && cmd.command) ? cmd : undefined; }; diff --git a/src/config.js b/src/config.js index 419072350eb..56b22687fe9 100644 --- a/src/config.js +++ b/src/config.js @@ -1,5 +1,4 @@ "no use strict"; - var lang = require("./lib/lang"); var net = require("./lib/net"); var dom = require("./lib/dom"); @@ -20,12 +19,20 @@ var options = { useStrictCSP: null }; +/** + * @param {string} key + * @return {*} + */ exports.get = function(key) { if (!options.hasOwnProperty(key)) throw new Error("Unknown config key: " + key); return options[key]; }; +/** + * @param {string} key + * @param value + */ exports.set = function(key, value) { if (options.hasOwnProperty(key)) options[key] = value; @@ -34,14 +41,21 @@ exports.set = function(key, value) { if (key == "useStrictCSP") dom.useStrictCSP(value); }; - +/** + * @return {{[key: string]: any}} + */ exports.all = function() { return lang.copyObject(options); }; exports.$modes = {}; -// module loading +/** + * module loading + * @param {string} name + * @param {string} [component] + * @returns {string} + */ exports.moduleUrl = function(name, component) { if (options.$moduleUrls[name]) return options.$moduleUrls[name]; @@ -69,7 +83,11 @@ exports.moduleUrl = function(name, component) { path += "/"; return path + component + sep + base + this.get("suffix"); }; - +/** + * @param {string} name + * @param {string} subst + * @returns {string} + */ exports.setModuleUrl = function(name, subst) { return options.$moduleUrls[name] = subst; }; @@ -82,6 +100,9 @@ var loader = function(moduleName, cb) { console.error("loader is not configured"); }; var customLoader; +/** + * @param {(moduleName: string, afterLoad: (err: Error | null, module: unknown) => void) => void}cb + */ exports.setLoader = function(cb) { customLoader = cb; }; @@ -89,13 +110,18 @@ exports.setLoader = function(cb) { exports.dynamicModules = Object.create(null); exports.$loading = {}; exports.$loaded = {}; -exports.loadModule = function(moduleName, onLoad) { - var loadedModule, moduleType; - if (Array.isArray(moduleName)) { - moduleType = moduleName[0]; - moduleName = moduleName[1]; +/** + * @param {string | [string, string]} moduleId + * @param {(module: any) => void} onLoad + */ +exports.loadModule = function(moduleId, onLoad) { + var loadedModule; + if (Array.isArray(moduleId)) { + var moduleType = moduleId[0]; + var moduleName = moduleId[1]; + } else if (typeof moduleId == "string") { + var moduleName = moduleId; } - var load = function (module) { // require(moduleName) can return empty object if called after require([moduleName], callback) if (module && !exports.$loading[moduleName]) return onLoad && onLoad(module); @@ -143,7 +169,7 @@ exports.loadModule = function(moduleName, onLoad) { }; exports.$require = function(moduleName) { - if (typeof module.require == "function") { + if (typeof module["require"] == "function") { var req = "require"; return module[req](moduleName); } diff --git a/src/document.js b/src/document.js index 10ddbc5ca0d..0d8d243f107 100644 --- a/src/document.js +++ b/src/document.js @@ -1,5 +1,11 @@ "use strict"; +/** + * @typedef {import("../ace-internal").Ace.Delta} Delta + * @typedef {import("../ace-internal").Ace.Point} Point + * @typedef {import("../ace-internal").Ace.IRange} IRange + * @typedef {import("../ace-internal").Ace.NewLineMode} NewLineMode + */ var oop = require("./lib/oop"); var applyDelta = require("./apply_delta").applyDelta; var EventEmitter = require("./lib/event_emitter").EventEmitter; @@ -17,6 +23,7 @@ class Document { * @param {String | String[]} textOrLines text The starting text **/ constructor(textOrLines) { + /**@type {string[]}*/ this.$lines = [""]; // There has to be one line at least in the document. If you pass an empty @@ -43,6 +50,7 @@ class Document { /** * Returns all the lines in the document as a single string, joined by the new line character. + * @returns {String} **/ getValue() { return this.getAllLines().join(this.getNewLineCharacter()); @@ -52,12 +60,15 @@ class Document { * Creates a new `Anchor` to define a floating point in the document. * @param {Number} row The row number to use * @param {Number} column The column number to use - * + * @returns {Anchor} **/ createAnchor(row, column) { return new Anchor(this, row, column); } + /** + * @param {string} text + */ $detectNewLine(text) { var match = text.match(/^.*?(\r\n|\r|\n)/m); this.$autoNewLine = match ? match[1] : "\n"; @@ -84,8 +95,8 @@ class Document { /** * [Sets the new line mode.]{: #Document.setNewLineMode.desc} - * @param {String} newLineMode [The newline mode to use; can be either `windows`, `unix`, or `auto`]{: #Document.setNewLineMode.param} - * + * @param {NewLineMode} newLineMode [The newline mode to use; can be either `windows`, `unix`, or `auto`]{: #Document.setNewLineMode.param} + **/ setNewLineMode(newLineMode) { if (this.$newLineMode === newLineMode) @@ -97,7 +108,7 @@ class Document { /** * [Returns the type of newlines being used; either `windows`, `unix`, or `auto`]{: #Document.getNewLineMode} - * @returns {String} + * @returns {NewLineMode} **/ getNewLineMode() { return this.$newLineMode; @@ -106,7 +117,7 @@ class Document { /** * Returns `true` if `text` is a newline character (either `\r\n`, `\r`, or `\n`). * @param {String} text The text to check - * + * @returns {boolean} **/ isNewLine(text) { return (text == "\r\n" || text == "\r" || text == "\n"); @@ -115,7 +126,7 @@ class Document { /** * Returns a verbatim copy of the given line as it is in the document * @param {Number} row The row index to retrieve - * + * @returns {string} **/ getLine(row) { return this.$lines[row] || ""; @@ -125,7 +136,7 @@ class Document { * Returns an array of strings of the rows between `firstRow` and `lastRow`. This function is inclusive of `lastRow`. * @param {Number} firstRow The first row index to retrieve * @param {Number} lastRow The final row index to retrieve - * + * @returns {string[]} **/ getLines(firstRow, lastRow) { return this.$lines.slice(firstRow, lastRow + 1); @@ -133,6 +144,7 @@ class Document { /** * Returns all lines in the document as string array. + * @returns {string[]} **/ getAllLines() { return this.getLines(0, this.getLength()); @@ -140,6 +152,7 @@ class Document { /** * Returns the number of rows in the document. + * @returns {Number} **/ getLength() { return this.$lines.length; @@ -147,7 +160,7 @@ class Document { /** * Returns all the text within `range` as a single string. - * @param {Range} range The range to work with. + * @param {IRange} range The range to work with. * * @returns {String} **/ @@ -157,7 +170,7 @@ class Document { /** * Returns all the text within `range` as an array of lines. - * @param {Range} range The range to work with. + * @param {IRange} range The range to work with. * * @returns {string[]} **/ @@ -178,14 +191,35 @@ class Document { } // Deprecated methods retained for backwards compatibility. + /** + * @param row + * @param lines + + * @deprecated + */ insertLines(row, lines) { console.warn("Use of document.insertLines is deprecated. Use the insertFullLines method instead."); return this.insertFullLines(row, lines); } + + /** + * @param firstRow + * @param lastRow + * @returns {String[]} + + * @deprecated + */ removeLines(firstRow, lastRow) { console.warn("Use of document.removeLines is deprecated. Use the removeFullLines method instead."); return this.removeFullLines(firstRow, lastRow); } + + /** + * @param position + * @returns {Point} + + * @deprecated + */ insertNewLine(position) { console.warn("Use of document.insertNewLine is deprecated. Use insertMergedLines(position, ['', '']) instead."); return this.insertMergedLines(position, ["", ""]); @@ -193,10 +227,10 @@ class Document { /** * Inserts a block of `text` at the indicated `position`. - * @param {Position} position The position to start inserting at; it's an object that looks like `{ row: row, column: column}` + * @param {Point} position The position to start inserting at; it's an object that looks like `{ row: row, column: column}` * @param {String} text A chunk of text to insert - * @returns {Object} The position ({row, column}) of the last line of `text`. If the length of `text` is 0, this function simply returns `position`. - * + * @returns {Point} The position ({row, column}) of the last line of `text`. If the length of `text` is 0, this function simply returns `position`. + **/ insert(position, text) { // Only detect new lines if the document has no line break yet. @@ -213,12 +247,9 @@ class Document { * 1. This does NOT handle newline characters (single-line text only). * 2. This is faster than the `insert` method for single-line text insertions. * - * @param {Object} position The position to insert at; it's an object that looks like `{ row: row, column: column}` - * @param {String} text A chunk of text - * @returns {Object} Returns an object containing the final row and column, like this: - * ``` - * {row: endRow, column: 0} - * ``` + * @param {Point} position The position to insert at; it's an object that looks like `{ row: row, column: column}` + * @param {String} text A chunk of text without new lines + * @returns {Point} Returns the position of the end of the inserted text **/ insertInLine(position, text) { var start = this.clippedPos(position.row, position.column); @@ -233,7 +264,13 @@ class Document { return this.clonePos(end); } - + + /** + * + * @param {number} row + * @param {number} column + * @return {Point} + */ clippedPos(row, column) { var length = this.getLength(); if (row === undefined) { @@ -250,15 +287,29 @@ class Document { column = Math.min(Math.max(column, 0), line.length); return {row: row, column: column}; } - + + /** + * @param {Point} pos + * @return {Point} + */ clonePos(pos) { return {row: pos.row, column: pos.column}; } - + + /** + * @param {number} row + * @param {number} column + * @return {Point} + */ pos(row, column) { return {row: row, column: column}; } - + + /** + * @param {Point} position + * @return {Point} + * @private + */ $clipPosition(position) { var length = this.getLength(); if (position.row >= length) { @@ -270,37 +321,12 @@ class Document { } return position; } - - /** - * Fires whenever the document changes. - * - * Several methods trigger different `"change"` events. Below is a list of each action type, followed by each property that's also available: - * - * * `"insert"` - * * `range`: the [[Range]] of the change within the document - * * `lines`: the lines being added - * * `"remove"` - * * `range`: the [[Range]] of the change within the document - * * `lines`: the lines being removed - * - * @event change - * @param {Object} e Contains at least one property called `"action"`. `"action"` indicates the action that triggered the change. Each action also has a set of additional properties. - * - **/ /** * Inserts the elements in `lines` into the document as full lines (does not merge with existing line), starting at the row index given by `row`. This method also triggers the `"change"` event. * @param {Number} row The index of the row to insert at * @param {string[]} lines An array of strings - * @returns {Object} Contains the final row and column, like this: - * ``` - * {row: endRow, column: 0} - * ``` - * If `lines` is empty, this function returns an object containing the current row, and column, like this: - * ``` - * {row: row, column: 0} - * ``` - * + **/ insertFullLines(row, lines) { // Clip to document. @@ -326,9 +352,9 @@ class Document { /** * Inserts the elements in `lines` into the document, starting at the position index given by `row`. This method also triggers the `"change"` event. - * @param {Position} position + * @param {Point} position * @param {string[]} lines An array of strings - * @returns {Object} Contains the final row and column, like this: + * @returns {Point} Contains the final row and column, like this: * ``` * {row: endRow, column: 0} * ``` @@ -336,7 +362,6 @@ class Document { * ``` * {row: row, column: 0} * ``` - * **/ insertMergedLines(position, lines) { var start = this.clippedPos(position.row, position.column); @@ -357,9 +382,9 @@ class Document { /** * Removes the `range` from the document. - * @param {Range} range A specified Range to remove - * @returns {Object} Returns the new `start` property of the range, which contains `startRow` and `startColumn`. If `range` is empty, this function returns the unmodified value of `range.start`. - * + * @param {IRange} range A specified Range to remove + * @returns {Point} Returns the new `start` property of the range, which contains `startRow` and `startColumn`. If `range` is empty, this function returns the unmodified value of `range.start`. + **/ remove(range) { var start = this.clippedPos(range.start.row, range.start.column); @@ -378,8 +403,8 @@ class Document { * @param {Number} row The row to remove from * @param {Number} startColumn The column to start removing at * @param {Number} endColumn The column to stop removing at - * @returns {Object} Returns an object containing `startRow` and `startColumn`, indicating the new row and column values.
If `startColumn` is equal to `endColumn`, this function returns nothing. - * + * @returns {Point} Returns an object containing `startRow` and `startColumn`, indicating the new row and column values.
If `startColumn` is equal to `endColumn`, this function returns nothing. + **/ removeInLine(row, startColumn, endColumn) { var start = this.clippedPos(row, startColumn); @@ -399,8 +424,8 @@ class Document { * Removes a range of full lines. This method also triggers the `"change"` event. * @param {Number} firstRow The first row to be removed * @param {Number} lastRow The last row to be removed - * @returns {[String]} Returns all the removed lines. - * + * @returns {String[]} Returns all the removed lines. + **/ removeFullLines(firstRow, lastRow) { // Clip to document. @@ -450,9 +475,9 @@ class Document { /** * Replaces a range in the document with the new `text`. - * @param {Range} range A specified Range to replace + * @param {Range | IRange} range A specified Range to replace * @param {String} text The new text to use as a replacement - * @returns {Object} Returns an object containing the final row and column, like this: + * @returns {Point} Returns an object containing the final row and column, like this: * {row: endRow, column: 0} * If the text and range are empty, this function returns an object containing the current `range.start` value. * If the text is the exact same as what currently exists, this function returns an object containing the current `range.end` value. @@ -461,6 +486,7 @@ class Document { replace(range, text) { if (!(range instanceof Range)) range = Range.fromPoints(range.start, range.end); + // @ts-expect-error if (text.length === 0 && range.isEmpty()) return range.start; @@ -503,8 +529,8 @@ class Document { /** * Applies `delta` to the document. - * @param {Object} delta A delta object (can include "insert" and "remove" actions) - * @param [doNotValidate] + * @param {Delta} delta A delta object (can include "insert" and "remove" actions) + * @param {boolean} [doNotValidate] **/ applyDelta(delta, doNotValidate) { var isInsert = delta.action == "insert"; @@ -522,7 +548,10 @@ class Document { this._signal("change", delta); } } - + + /** + * @param {Delta} delta + */ $safeApplyDelta(delta) { var docLength = this.$lines.length; // verify that delta is in the document to prevent applyDelta from corrupting lines array @@ -533,7 +562,12 @@ class Document { this.applyDelta(delta); } } - + + /** + * + * @param {Delta} delta + * @param {number} MAX + */ $splitAndapplyLargeDelta(delta, MAX) { // Split large insert deltas. This is necessary because: // 1. We need to support splicing delta lines into the document via $lines.splice.apply(...) @@ -568,7 +602,7 @@ class Document { /** * Reverts `delta` from the document. - * @param {Object} delta A delta object (can include "insert" and "remove" actions) + * @param {Delta} delta A delta object (can include "insert" and "remove" actions) **/ revertDelta(delta) { this.$safeApplyDelta({ @@ -592,8 +626,8 @@ class Document { * Here, `y` is an index 15: 11 characters for the first row, and 5 characters until `y` in the second. * * @param {Number} index An index to convert - * @param {Number} startRow=0 The row from which to start the conversion - * @returns {Object} A `{row, column}` object of the `index` position + * @param {Number} [startRow=0] The row from which to start the conversion + * @returns {Point} A `{row, column}` object of the `index` position */ indexToPosition(index, startRow) { var lines = this.$lines || this.getAllLines(); @@ -618,8 +652,8 @@ class Document { * * Here, `y` is an index 15: 11 characters for the first row, and 5 characters until `y` in the second. * - * @param {Object} pos The `{row, column}` to convert - * @param {Number} startRow=0 The row from which to start the conversion + * @param {Point} pos The `{row, column}` to convert + * @param {Number} [startRow=0] The row from which to start the conversion * @returns {Number} The index position in the document */ positionToIndex(pos, startRow) { @@ -638,7 +672,7 @@ class Document { * * @method $split * @param {String} text The text to work with - * @returns {String} A String array, with each index containing a piece of the original `text` string. + * @returns {String[]} A String array, with each index containing a piece of the original `text` string. * **/ $split(text) { diff --git a/src/edit_session.js b/src/edit_session.js index 877e9978e79..3a14f596977 100644 --- a/src/edit_session.js +++ b/src/edit_session.js @@ -1,4 +1,12 @@ "use strict"; +/** + * @typedef {import("./layer/font_metrics").FontMetrics} FontMetrics + * @typedef {import("./edit_session/fold_line").FoldLine} FoldLine + * @typedef {import("../ace-internal").Ace.Point} Point + * @typedef {import("../ace-internal").Ace.Delta} Delta + * @typedef {import("../ace-internal").Ace.IRange} IRange + * @typedef {import("../ace-internal").Ace.SyntaxMode} SyntaxMode + */ var oop = require("./lib/oop"); var lang = require("./lib/lang"); @@ -13,101 +21,24 @@ var BackgroundTokenizer = require("./background_tokenizer").BackgroundTokenizer; var SearchHighlight = require("./search_highlight").SearchHighlight; var UndoManager = require("./undomanager").UndoManager; -//{ events -/** - * - * Emitted when the document changes. - * @event change - * @param {Object} e An object containing a `delta` of information about the change. - **/ -/** - * Emitted when the tab size changes, via [[EditSession.setTabSize]]. - * - * @event changeTabSize - **/ -/** - * Emitted when the ability to overwrite text changes, via [[EditSession.setOverwrite]]. - * - * @event changeOverwrite - **/ -/** - * Emitted when the gutter changes, either by setting or removing breakpoints, or when the gutter decorations change. - * - * @event changeBreakpoint - **/ -/** - * Emitted when a front marker changes. - * - * @event changeFrontMarker - **/ -/** - * Emitted when a back marker changes. - * - * @event changeBackMarker - **/ -/** - * Emitted when an annotation changes, like through [[EditSession.setAnnotations]]. - * - * @event changeAnnotation - **/ -/** - * Emitted when a background tokenizer asynchronously processes new rows. - * @event tokenizerUpdate - * - * @param {Object} e An object containing one property, `"data"`, that contains information about the changing rows - * - **/ /** - * Emitted when the current mode changes. - * - * @event changeMode - * - **/ -/** - * Emitted when the wrap mode changes. - * - * @event changeWrapMode - * - **/ -/** - * Emitted when the wrapping limit changes. - * - * @event changeWrapLimit - * - **/ -/** - * Emitted when a code fold is added or removed. - * - * @event changeFold - * - **/ - /** - * Emitted when the scroll top changes. - * @event changeScrollTop - * - * @param {Number} scrollTop The new scroll top value - **/ -/** - * Emitted when the scroll left changes. - * @event changeScrollLeft - * - * @param {Number} scrollLeft The new scroll left value - **/ -//} + * @typedef TextMode + * @type {SyntaxMode} + */ /** * Stores all the data about [[Editor `Editor`]] state providing easy way to change editors state. * * `EditSession` can be attached to only one [[Document `Document`]]. Same `Document` can be attached to several `EditSession`s. **/ - class EditSession { /** * Sets up a new `EditSession` and associates it with the given `Document` and `Mode`. - * @param {Document | String} text [If `text` is a `Document`, it associates the `EditSession` with it. Otherwise, a new `Document` is created, with the initial text]{: #textParam} - * @param {Mode} mode [The initial language mode to use for the document]{: #modeParam} + * @param {Document | String} [text] [If `text` is a `Document`, it associates the `EditSession` with it. Otherwise, a new `Document` is created, with the initial text]{: #textParam} + * @param {SyntaxMode} [mode] [The initial language mode to use for the document]{: #modeParam} **/ constructor(text, mode) { + /**@type {Document}*/this.doc; this.$breakpoints = []; this.$decorations = []; this.$frontMarkers = {}; @@ -115,6 +46,7 @@ class EditSession { this.$markerId = 1; this.$undoSelect = true; + /** @type {FoldLine[]} */ this.$foldData = []; this.id = "session" + (++EditSession.$uid); this.$foldData.toString = function() { @@ -124,6 +56,7 @@ class EditSession { // Set default background tokenizer with Text mode until editor session mode is set this.bgTokenizer = new BackgroundTokenizer((new TextMode()).getTokenizer(), this); + var _self = this; this.bgTokenizer.on("update", function(e) { _self._signal("tokenizerUpdate", e); @@ -133,7 +66,7 @@ class EditSession { this.$onChange = this.onChange.bind(this); if (typeof text != "object" || !text.getLine) - text = new Document(text); + text = new Document(/**@type{string}*/(text)); this.setDocument(text); this.selection = new Selection(this); @@ -155,7 +88,6 @@ class EditSession { setDocument(doc) { if (this.doc) this.doc.off("change", this.$onChange); - this.doc = doc; doc.on("change", this.$onChange, true); @@ -178,7 +110,9 @@ class EditSession { **/ $resetRowCache(docRow) { if (!docRow) { + /** @type {number[]} */ this.$docRowCache = []; + /** @type {number[]} */ this.$screenRowCache = []; return; } @@ -223,6 +157,10 @@ class EditSession { this.$resetRowCache(fold.start.row); } + /** + * + * @param {Delta} delta + */ onChange(delta) { this.$modified = true; this.$bidiHandler.onChange(delta); @@ -232,6 +170,7 @@ class EditSession { if (!this.$fromUndo && this.$undoManager) { if (removedFolds && removedFolds.length) { this.$undoManager.add({ + // @ts-expect-error TODO: this action type is missing in the types action: "removeFolds", folds: removedFolds }, this.mergeUndoDeltas); @@ -250,7 +189,6 @@ class EditSession { /** * Sets the session text. * @param {String} text The new text to place - * **/ setValue(text) { this.doc.setValue(text); @@ -264,11 +202,12 @@ class EditSession { /** * Returns a new instance of EditSession with state from JSON. * @method fromJSON - * @param {String} session The EditSession state. + * @param {string|object} session The EditSession state. * @returns {EditSession} */ - static fromJSON(session) { - session = JSON.parse(session); + static fromJSON(session) { + if (typeof session == "string") + session = JSON.parse(session); const undoManager = new UndoManager(); undoManager.$undoStack = session.history.undo; undoManager.$redoStack = session.history.redo; @@ -324,6 +263,7 @@ class EditSession { /** * Returns selection object. + * @returns {Selection} **/ getSelection() { return this.selection; @@ -332,7 +272,7 @@ class EditSession { /** * {:BackgroundTokenizer.getState} * @param {Number} row The row to start at - * + * @returns {string} * @related BackgroundTokenizer.getState **/ getState(row) { @@ -342,7 +282,7 @@ class EditSession { /** * Starts tokenizing at the row indicated. Returns a list of objects of the tokenized rows. * @param {Number} row The row to start at - * @returns {Token[]} + * @returns {import("../ace-internal").Ace.Token[]} **/ getTokens(row) { return this.bgTokenizer.getTokens(row); @@ -352,7 +292,7 @@ class EditSession { * Returns an object indicating the token at the current row. The object has two properties: `index` and `start`. * @param {Number} row The row number to retrieve from * @param {Number} column The column number to retrieve from - * @returns {Token} + * @returns {import("../ace-internal").Ace.Token} * **/ getTokenAt(row, column) { @@ -379,8 +319,6 @@ class EditSession { /** * Sets the undo manager. * @param {UndoManager} undoManager The new undo manager - * - * **/ setUndoManager(undoManager) { this.$undoManager = undoManager; @@ -411,13 +349,16 @@ class EditSession { /** * Returns the current undo manager. + * @returns {UndoManager} **/ getUndoManager() { + // @ts-ignore return this.$undoManager || this.$defaultUndoManager; } /** * Returns the current value for tabs. If the user is using soft tabs, this will be a series of spaces (defined by [[EditSession.getTabSize `getTabSize()`]]); otherwise it's simply `'\t'`. + * @returns {String} **/ getTabString() { if (this.getUseSoftTabs()) { @@ -452,6 +393,7 @@ class EditSession { } /** * Returns the current tab size. + * @return {number} **/ getTabSize() { return this.$tabSize; @@ -459,8 +401,7 @@ class EditSession { /** * Returns `true` if the character at the position is a soft tab. - * @param {Object} position The position to check - * + * @param {Point} position The position to check **/ isTabStop(position) { return this.$useSoftTabs && (position.column % this.$tabSize === 0); @@ -488,7 +429,6 @@ class EditSession { * * @param {Boolean} overwrite Defines whether or not to set overwrites * - * **/ setOverwrite(overwrite) { this.setOption("overwrite", overwrite); @@ -512,7 +452,6 @@ class EditSession { * Adds `className` to the `row`, to be used for CSS stylings and whatnot. * @param {Number} row The row number * @param {String} className The class to add - * **/ addGutterDecoration(row, className) { if (!this.$decorations[row]) @@ -525,7 +464,6 @@ class EditSession { * Removes `className` from the `row`. * @param {Number} row The row number * @param {String} className The class to add - * **/ removeGutterDecoration(row, className) { this.$decorations[row] = (this.$decorations[row] || "").replace(" " + className, ""); @@ -534,7 +472,7 @@ class EditSession { /** * Returns an array of strings, indicating the breakpoint class (if any) applied to each row. - * @returns {[String]} + * @returns {String[]} **/ getBreakpoints() { return this.$breakpoints; @@ -542,8 +480,7 @@ class EditSession { /** * Sets a breakpoint on every row number given by `rows`. This function also emites the `'changeBreakpoint'` event. - * @param {Array} rows An array of row indices - * + * @param {number[]} rows An array of row indices **/ setBreakpoints(rows) { this.$breakpoints = []; @@ -565,7 +502,6 @@ class EditSession { * Sets a breakpoint on the row number given by `row`. This function also emits the `'changeBreakpoint'` event. * @param {Number} row A row index * @param {String} className Class of the breakpoint - * **/ setBreakpoint(row, className) { if (className === undefined) @@ -580,7 +516,6 @@ class EditSession { /** * Removes a breakpoint on the row number given by `row`. This function also emits the `'changeBreakpoint'` event. * @param {Number} row A row index - * **/ clearBreakpoint(row) { delete this.$breakpoints[row]; @@ -591,8 +526,8 @@ class EditSession { * Adds a new marker to the given `Range`. If `inFront` is `true`, a front marker is defined, and the `'changeFrontMarker'` event fires; otherwise, the `'changeBackMarker'` event fires. * @param {Range} range Define the range of the marker * @param {String} clazz Set the CSS class for the marker - * @param {Function | String} type Identify the renderer type of the marker. If string provided, corresponding built-in renderer is used. Supported string types are "fullLine", "screenLine", "text" or "line". If a Function is provided, that Function is used as renderer. - * @param {Boolean} inFront Set to `true` to establish a front marker + * @param {import("../ace-internal").Ace.MarkerRenderer | "fullLine" | "screenLine" | "text" | "line"} [type] Identify the renderer type of the marker. If string provided, corresponding built-in renderer is used. Supported string types are "fullLine", "screenLine", "text" or "line". If a Function is provided, that Function is used as renderer. + * @param {Boolean} [inFront] Set to `true` to establish a front marker * * @return {Number} The new marker id **/ @@ -621,10 +556,10 @@ class EditSession { /** * Adds a dynamic marker to the session. - * @param {Object} marker object with update method - * @param {Boolean} inFront Set to `true` to establish a front marker + * @param {import("../ace-internal").Ace.MarkerLike} marker object with update method + * @param {Boolean} [inFront] Set to `true` to establish a front marker * - * @return {Object} The added marker + * @return {import("../ace-internal").Ace.MarkerLike} The added marker **/ addDynamicMarker(marker, inFront) { if (!marker.update) @@ -647,7 +582,6 @@ class EditSession { /** * Removes the marker with the specified ID. If this marker was in front, the `'changeFrontMarker'` event is emitted. If the marker was in the back, the `'changeBackMarker'` event is emitted. * @param {Number} markerId A number representing a marker - * **/ removeMarker(markerId) { var marker = this.$frontMarkers[markerId] || this.$backMarkers[markerId]; @@ -661,14 +595,17 @@ class EditSession { /** * Returns an object containing all of the markers, either front or back. - * @param {Boolean} inFront If `true`, indicates you only want front markers; `false` indicates only back markers + * @param {Boolean} [inFront] If `true`, indicates you only want front markers; `false` indicates only back markers * - * @returns {Object} + * @returns {{[id: number]: import("../ace-internal").Ace.MarkerLike}} **/ getMarkers(inFront) { return inFront ? this.$frontMarkers : this.$backMarkers; } + /** + * @param {RegExp} re + */ highlight(re) { if (!this.$searchHighlight) { var highlight = new SearchHighlight(null, "ace_selected-word", "text"); @@ -677,7 +614,14 @@ class EditSession { this.$searchHighlight.setRegexp(re); } - // experimental + /** + * experimental + * @param {number} startRow + * @param {number} endRow + * @param {string} clazz + * @param {boolean} [inFront] + * @return {Range} + */ highlightLines(startRow, endRow, clazz, inFront) { if (typeof endRow != "number") { clazz = endRow; @@ -702,8 +646,7 @@ class EditSession { */ /** * Sets annotations for the `EditSession`. This functions emits the `'changeAnnotation'` event. - * @param {Annotation[]} annotations A list of annotations - * + * @param {import("../ace-internal").Ace.Annotation[]} annotations A list of annotations **/ setAnnotations(annotations) { this.$annotations = annotations; @@ -712,7 +655,7 @@ class EditSession { /** * Returns the annotations for the `EditSession`. - * @returns {Annotation[]} + * @returns {import("../ace-internal").Ace.Annotation[]} **/ getAnnotations() { return this.$annotations || []; @@ -799,7 +742,7 @@ class EditSession { /** * {:Document.setNewLineMode.desc} - * @param {String} newLineMode {:Document.setNewLineMode.param} + * @param {import("../ace-internal").Ace.NewLineMode} newLineMode {:Document.setNewLineMode.param} * * * @related Document.setNewLineMode @@ -811,7 +754,7 @@ class EditSession { /** * * Returns the current new line mode. - * @returns {String} + * @returns {import("../ace-internal").Ace.NewLineMode} * @related Document.getNewLineMode **/ getNewLineMode() { @@ -821,7 +764,6 @@ class EditSession { /** * Identifies if you want to use a worker for the `EditSession`. * @param {Boolean} useWorker Set to `true` to use a worker - * **/ setUseWorker(useWorker) { this.setOption("useWorker", useWorker); } @@ -841,9 +783,8 @@ class EditSession { /** * Sets a new text mode for the `EditSession`. This method also emits the `'changeMode'` event. If a [[BackgroundTokenizer `BackgroundTokenizer`]] is set, the `'tokenizerUpdate'` event is also emitted. - * @param {TextMode} mode Set a new text mode - * @param {Function} cb optional callback - * + * @param {SyntaxMode | string} mode Set a new text mode + * @param {() => void} [cb] optional callback **/ setMode(mode, cb) { if (mode && typeof mode === "object") { @@ -852,7 +793,7 @@ class EditSession { var options = mode; var path = options.path; } else { - path = mode || "ace/mode/text"; + path = /**@type{string}*/(mode) || "ace/mode/text"; } // this is needed if ace isn't on require path (e.g tests in node) @@ -887,6 +828,10 @@ class EditSession { this.$onChangeMode(this.$modes["ace/mode/text"], true); } + /** + * @param mode + * @param [$isPlaceholder] + */ $onChangeMode(mode, $isPlaceholder) { if (!$isPlaceholder) this.$modeId = mode.$id; @@ -911,7 +856,9 @@ class EditSession { this.bgTokenizer.setTokenizer(tokenizer); this.bgTokenizer.setDocument(this.getDocument()); + /**@type {RegExp}*/ this.tokenRe = mode.tokenRe; + /**@type {RegExp}*/ this.nonTokenRe = mode.nonTokenRe; @@ -953,7 +900,6 @@ class EditSession { /** * This function sets the scroll top value. It also emits the `'changeScrollTop'` event. * @param {Number} scrollTop The new scroll top value - * **/ setScrollTop(scrollTop) { // TODO: should we force integer lineheight instead? scrollTop = Math.round(scrollTop); @@ -974,7 +920,8 @@ class EditSession { /** * [Sets the value of the distance between the left of the editor and the leftmost part of the visible content.]{: #EditSession.setScrollLeft} - **/ + * @param {number} scrollLeft + */ setScrollLeft(scrollLeft) { // scrollLeft = Math.round(scrollLeft); if (this.$scrollLeft === scrollLeft || isNaN(scrollLeft)) @@ -1002,7 +949,10 @@ class EditSession { return Math.max(this.getLineWidgetMaxWidth(), this.screenWidth); return this.screenWidth; } - + + /** + * @return {number} + */ getLineWidgetMaxWidth() { if (this.lineWidgetsWidth != null) return this.lineWidgetsWidth; var width = 0; @@ -1013,6 +963,9 @@ class EditSession { return this.lineWidgetWidth = width; } + /** + * @param {boolean} [force] + */ $computeWidth(force) { if (this.$modified || force) { this.$modified = false; @@ -1050,7 +1003,6 @@ class EditSession { /** * Returns a verbatim copy of the given line as it is in the document * @param {Number} row The row to retrieve from - * * @returns {String} **/ getLine(row) { @@ -1062,7 +1014,7 @@ class EditSession { * @param {Number} firstRow The first row index to retrieve * @param {Number} lastRow The final row index to retrieve * - * @returns {[String]} + * @returns {String[]} * **/ getLines(firstRow, lastRow) { @@ -1079,7 +1031,7 @@ class EditSession { /** * {:Document.getTextRange.desc} - * @param {Range} range The range to work with + * @param {IRange} [range] The range to work with * * @returns {String} **/ @@ -1089,11 +1041,9 @@ class EditSession { /** * Inserts a block of `text` and the indicated `position`. - * @param {Object} position The position {row, column} to start inserting at + * @param {Point} position The position {row, column} to start inserting at * @param {String} text A chunk of text to insert - * @returns {Object} The position of the last line of `text`. If the length of `text` is 0, this function simply returns `position`. - * - * + * @returns {Point} The position of the last line of `text`. If the length of `text` is 0, this function simply returns `position`. **/ insert(position, text) { return this.doc.insert(position, text); @@ -1101,11 +1051,8 @@ class EditSession { /** * Removes the `range` from the document. - * @param {Range} range A specified Range to remove - * @returns {Object} The new `start` property of the range, which contains `startRow` and `startColumn`. If `range` is empty, this function returns the unmodified value of `range.start`. - * - * @related Document.remove - * + * @param {IRange} range A specified Range to remove + * @returns {Point} The new `start` property of the range, which contains `startRow` and `startColumn`. If `range` is empty, this function returns the unmodified value of `range.start`. **/ remove(range) { return this.doc.remove(range); @@ -1115,7 +1062,7 @@ class EditSession { * Removes a range of full lines. This method also triggers the `'change'` event. * @param {Number} firstRow The first row to be removed * @param {Number} lastRow The last row to be removed - * @returns {[String]} Returns all the removed lines. + * @returns {String[]} Returns all the removed lines. * * @related Document.removeFullLines * @@ -1127,7 +1074,7 @@ class EditSession { /** * Reverts previous changes to your document. * @param {Delta[]} deltas An array of previous changes - * @param {Boolean} dontSelect [If `true`, doesn't select the range of where the change occured]{: #dontSelect} + * @param {Boolean} [dontSelect] [If `true`, doesn't select the range of where the change occured]{: #dontSelect} **/ undoChanges(deltas, dontSelect) { if (!deltas.length) @@ -1143,7 +1090,9 @@ class EditSession { } } if (!dontSelect && this.$undoSelect) { + //@ts-expect-error TODO: potential wrong property if (deltas.selectionBefore) + //@ts-expect-error TODO: potential wrong property this.selection.fromJSON(deltas.selectionBefore); else this.selection.setRange(this.$getUndoSelection(deltas, true)); @@ -1154,7 +1103,7 @@ class EditSession { /** * Re-implements a previously undone change to your document. * @param {Delta[]} deltas An array of previous changes - * @param {Boolean} dontSelect {:dontSelect} + * @param {Boolean} [dontSelect] {:dontSelect} **/ redoChanges(deltas, dontSelect) { if (!deltas.length) @@ -1169,7 +1118,9 @@ class EditSession { } if (!dontSelect && this.$undoSelect) { + //@ts-expect-error TODO: potential wrong property if (deltas.selectionAfter) + //@ts-expect-error TODO: potential wrong property this.selection.fromJSON(deltas.selectionAfter); else this.selection.setRange(this.$getUndoSelection(deltas, false)); @@ -1186,6 +1137,12 @@ class EditSession { this.$undoSelect = enable; } + /** + * + * @param {Delta[]} deltas + * @param {boolean} [isUndo] + * @return {Range} + */ $getUndoSelection(deltas, isUndo) { function isInsert(delta) { return isUndo ? delta.action !== "insert" : delta.action === "insert"; @@ -1227,9 +1184,9 @@ class EditSession { /** * Replaces a range in the document with the new `text`. * - * @param {Range} range A specified Range to replace + * @param {IRange} range A specified Range to replace * @param {String} text The new text to use as a replacement - * @returns {Object} An object containing the final row and column, like this: + * @returns {Point} An object containing the final row and column, like this: * ``` * {row: endRow, column: 0} * ``` @@ -1248,8 +1205,8 @@ class EditSession { * { row: newRowLocation, column: newColumnLocation } * ``` * @param {Range} fromRange The range of text you want moved within the document - * @param {Object} toPosition The location (row and column) where you want to move the text to - * @param {boolean} copy + * @param {Point} toPosition The location (row and column) where you want to move the text to + * @param {boolean} [copy] * @returns {Range} The new range where the text was moved to. **/ moveText(fromRange, toPosition, copy) { @@ -1311,7 +1268,6 @@ class EditSession { /** * Outdents all the rows defined by the `start` and `end` properties of `range`. * @param {Range} range A range of rows - * **/ outdentRows(range) { var rowRange = range.collapseRows(); @@ -1337,6 +1293,13 @@ class EditSession { } } + /** + * + * @param {number} firstRow + * @param {number} lastRow + * @param [dir] + * @returns {number} + */ $moveLines(firstRow, lastRow, dir) { firstRow = this.getRowFoldStart(firstRow); lastRow = this.getRowFoldEnd(lastRow); @@ -1374,7 +1337,6 @@ class EditSession { * @param {Number} firstRow The starting row to move up * @param {Number} lastRow The final row to move up * @returns {Number} If `firstRow` is less-than or equal to 0, this function returns 0. Otherwise, on success, it returns -1. - * **/ moveLinesUp(firstRow, lastRow) { return this.$moveLines(firstRow, lastRow, -1); @@ -1400,16 +1362,29 @@ class EditSession { return this.$moveLines(firstRow, lastRow, 0); } - + /** + * @param {number} row + * @returns {number} + */ $clipRowToDocument(row) { return Math.max(0, Math.min(row, this.doc.getLength()-1)); } + /** + * @param {number} row + * @param {number} column + * @returns {number} + */ $clipColumnToRow(row, column) { if (column < 0) return 0; return Math.min(this.doc.getLine(row).length, column); } - + + /** + * @param {number} row + * @param {number} column + * @returns {Point} + */ $clipPositionToDocument(row, column) { column = Math.max(0, column); @@ -1432,6 +1407,10 @@ class EditSession { }; } + /** + * @param {Range} range + * @returns {Range} + */ $clipRangeToDocument(range) { if (range.start.row < 0) { range.start.row = 0; @@ -1459,7 +1438,6 @@ class EditSession { /** * Sets whether or not line wrapping is enabled. If `useWrapMode` is different than the current value, the `'changeWrapMode'` event is emitted. * @param {Boolean} useWrapMode Enable (or disable) wrap mode - * **/ setUseWrapMode(useWrapMode) { if (useWrapMode != this.$useWrapMode) { @@ -1494,7 +1472,6 @@ class EditSession { * Sets the boundaries of wrap. Either value can be `null` to have an unconstrained wrap, or, they can be the same number to pin the limit. If the wrap limits for `min` or `max` are different, this method also emits the `'changeWrapMode'` event. * @param {Number} min The minimum wrap value (the left side wrap) * @param {Number} max The maximum wrap value (the right side wrap) - * **/ setWrapLimitRange(min, max) { if (this.$wrapLimitRange.min !== min || this.$wrapLimitRange.max !== max) { @@ -1511,9 +1488,8 @@ class EditSession { /** * This should generally only be called by the renderer when a resize is detected. * @param {Number} desiredLimit The new wrap limit + * @param [$printMargin] * @returns {Boolean} - * - * @private **/ adjustWrapLimit(desiredLimit, $printMargin) { var limits = this.$wrapLimitRange; @@ -1533,6 +1509,13 @@ class EditSession { return false; } + /** + * + * @param {number} wrapLimit + * @param {number} [min] + * @param {number} [max] + * @returns {number} + */ $constrainWrapLimit(wrapLimit, min, max) { if (min) wrapLimit = Math.max(min, wrapLimit); @@ -1566,7 +1549,7 @@ class EditSession { * * { min: wrapLimitRange_min, max: wrapLimitRange_max } * - * @returns {Object} + * @returns {{ min: number, max: number }} **/ getWrapLimitRange() { // Avoid unexpected mutation by returning a copy @@ -1576,6 +1559,9 @@ class EditSession { }; } + /** + * @param {Delta} delta + */ $updateInternalDataOnChange(delta) { var useWrapMode = this.$useWrapMode; var action = delta.action; @@ -1684,11 +1670,19 @@ class EditSession { return removedFolds; } - $updateRowLengthCache(firstRow, lastRow, b) { + /** + * @param {number} firstRow + * @param {number} lastRow + */ + $updateRowLengthCache(firstRow, lastRow) { this.$rowLengthCache[firstRow] = null; this.$rowLengthCache[lastRow] = null; } + /** + * @param {number} firstRow + * @param {number} lastRow + */ $updateWrapData(firstRow, lastRow) { var lines = this.doc.getAllLines(); var tabSize = this.getTabSize(); @@ -1732,7 +1726,13 @@ class EditSession { } } } - + + /** + * @param {number[]}tokens + * @param {number} wrapLimit + * @param {number} tabSize + * @returns {*[]} + */ $computeWrapSplits(tokens, wrapLimit, tabSize) { if (tokens.length == 0) { return []; @@ -1780,6 +1780,7 @@ class EditSession { if (!splits.length) { indent = getWrapIndent(); + //@ts-expect-error TODO: potential wrong property splits.indent = indent; } lastDocSplit += len; @@ -1885,7 +1886,8 @@ class EditSession { /** * Given a string, returns an array of the display characters, including tabs and spaces. * @param {String} str The string to check - * @param {Number} offset The value to start at + * @param {Number} [offset] The value to start at + * @returns {number[]} **/ $getDisplayTokens(str, offset) { var arr = []; @@ -1921,9 +1923,9 @@ class EditSession { /** * Calculates the width of the string `str` on the screen while assuming that the string starts at the first column on the screen. * @param {String} str The string to calculate the screen width of - * @param {Number} maxScreenColumn - * @param {Number} screenColumn - * @returns {[Number]} Returns an `int[]` array with two elements:
+ * @param {Number} [maxScreenColumn] + * @param {Number} [screenColumn] + * @returns {Number[]} Returns an `int[]` array with two elements:
* The first position indicates the number of columns for `str` on screen.
* The second value contains the position of the document column that this function read until. **/ @@ -1958,7 +1960,6 @@ class EditSession { /** * Returns number of screenrows in a wrapped line. * @param {Number} row The row number to check - * * @returns {Number} **/ getRowLength(row) { @@ -1971,6 +1972,11 @@ class EditSession { else return this.$wrapData[row].length + h; } + + /** + * @param {Number} row + * @returns {Number} + **/ getRowLineCount(row) { if (!this.$useWrapMode || !this.$wrapData[row]) { return 1; @@ -1979,6 +1985,10 @@ class EditSession { } } + /** + * @param {Number} screenRow + * @returns {Number} + **/ getRowWrapIndent(screenRow) { if (this.$useWrapMode) { var pos = this.screenToDocumentPosition(screenRow, Number.MAX_VALUE); @@ -2004,8 +2014,8 @@ class EditSession { /** * For the given document row and column, this returns the column position of the last screen row. * @param {Number} docRow - * * @param {Number} docColumn + * @returns {number} **/ getDocumentLastRowColumn(docRow, docColumn) { var screenRow = this.documentToScreenRow(docRow, docColumn); @@ -2016,7 +2026,7 @@ class EditSession { * For the given document row and column, this returns the document position of the last row. * @param {Number} docRow * @param {Number} docColumn - * + * @returns {Point} **/ getDocumentLastRowColumnPosition(docRow, docColumn) { var screenRow = this.documentToScreenRow(docRow, docColumn); @@ -2025,8 +2035,9 @@ class EditSession { /** * For the given row, this returns the split data. - * @returns {String} - **/ + * @param {number} row + * @returns {String | undefined} + */ getRowSplitData(row) { if (!this.$useWrapMode) { return undefined; @@ -2045,12 +2056,20 @@ class EditSession { return this.$tabSize - (screenColumn % this.$tabSize | 0); } - + /** + * @param {number} screenRow + * @param {number} screenColumn + * @returns {number} + */ screenToDocumentRow(screenRow, screenColumn) { return this.screenToDocumentPosition(screenRow, screenColumn).row; } - + /** + * @param {number} screenRow + * @param {number} screenColumn + * @returns {number} + */ screenToDocumentColumn(screenRow, screenColumn) { return this.screenToDocumentPosition(screenRow, screenColumn).column; } @@ -2059,9 +2078,9 @@ class EditSession { * Converts characters coordinates on the screen to characters coordinates within the document. [This takes into account code folding, word wrap, tab size, and any other visual modifications.]{: #conversionConsiderations} * @param {Number} screenRow The screen row to check * @param {Number} screenColumn The screen column to check - * @param {Number} offsetX screen character x-offset [optional] + * @param {Number} [offsetX] screen character x-offset [optional] * - * @returns {Object} The object returned has two properties: `row` and `column`. + * @returns {Point} The object returned has two properties: `row` and `column`. * * @related EditSession.documentToScreenPosition **/ @@ -2155,18 +2174,18 @@ class EditSession { /** * Converts document coordinates to screen coordinates. {:conversionConsiderations} - * @param {Number|Position} docRow The document row to check + * @param {Number|Point} docRow The document row to check * @param {Number|undefined} [docColumn] The document column to check - * @returns {Position} The object returned by this method has two properties: `row` and `column`. + * @returns {Point} The object returned by this method has two properties: `row` and `column`. * * @related EditSession.screenToDocumentPosition **/ documentToScreenPosition(docRow, docColumn) { // Normalize the passed in arguments. if (typeof docColumn === "undefined") - var pos = this.$clipPositionToDocument(docRow.row, docRow.column); + var pos = this.$clipPositionToDocument(/**@type{Point}*/(docRow).row, /**@type{Point}*/(docRow).column); else - pos = this.$clipPositionToDocument(docRow, docColumn); + pos = this.$clipPositionToDocument(/**@type{number}*/(docRow), docColumn); docRow = pos.row; docColumn = pos.column; @@ -2184,7 +2203,6 @@ class EditSession { var rowEnd, row = 0; - var rowCache = this.$docRowCache; var i = this.$getRowCacheIndex(rowCache, docRow); var l = rowCache.length; @@ -2258,10 +2276,9 @@ class EditSession { /** * For the given document row and column, returns the screen column. - * @param {Number} row - * @param {Number} docColumn + * @param {Number|Point} row + * @param {Number} [docColumn] * @returns {Number} - * **/ documentToScreenColumn(row, docColumn) { return this.documentToScreenPosition(row, docColumn).column; @@ -2269,9 +2286,9 @@ class EditSession { /** * For the given document row and column, returns the screen row. - * @param {Number} docRow - * @param {Number} docColumn - * + * @param {Number|Point} docRow + * @param {Number} [docColumn] + * @returns {number} **/ documentToScreenRow(docRow, docColumn) { return this.documentToScreenPosition(docRow, docColumn).row; @@ -2283,6 +2300,7 @@ class EditSession { **/ getScreenLength() { var screenRows = 0; + /**@type {FoldLine}*/ var fold = null; if (!this.$useWrapMode) { screenRows = this.getLength(); @@ -2317,10 +2335,9 @@ class EditSession { return screenRows; } - + /** - * @private - * + * @param {FontMetrics} fm */ $setFontMetrics(fm) { if (!this.$enableVarChar) return; @@ -2348,7 +2365,7 @@ class EditSession { return [screenColumn, column]; }; } - + destroy() { if (!this.destroyed) { this.bgTokenizer.setDocument(null); @@ -2386,10 +2403,7 @@ EditSession.prototype.$defaultUndoManager = { addSession: function() {} }; EditSession.prototype.$overwrite = false; -/** - * - * @type {TextMode|null} - */ + EditSession.prototype.$mode = null; EditSession.prototype.$modeId = null; EditSession.prototype.$scrollTop = 0; @@ -2401,6 +2415,10 @@ EditSession.prototype.$wrapLimitRange = { min : null, max : null }; +/** + * + * @type {null | import("../ace-internal").Ace.LineWidget[]} + */ EditSession.prototype.lineWidgets = null; EditSession.prototype.isFullWidth = isFullWidth; @@ -2459,6 +2477,10 @@ require("./edit_session/bracket_match").BracketMatch.call(EditSession.prototype) config.defineOptions(EditSession.prototype, "session", { wrap: { + /** + * @param {string | boolean | number} value + * @this {EditSession} + */ set: function(value) { if (!value || value == "off") value = false; @@ -2493,7 +2515,10 @@ config.defineOptions(EditSession.prototype, "session", { handlesSet: true }, wrapMethod: { - // code|text|auto + /** + * @param {"code"|"text"|"auto"|boolean} val + * @this{EditSession} + */ set: function(val) { val = val == "auto" ? this.$mode.type != "text" @@ -2509,6 +2534,9 @@ config.defineOptions(EditSession.prototype, "session", { initialValue: "auto" }, indentedSoftWrap: { + /** + * @this{EditSession} + */ set: function() { if (this.$useWrapMode) { this.$useWrapMode = false; @@ -2522,6 +2550,10 @@ config.defineOptions(EditSession.prototype, "session", { initialValue: 1 }, useWorker: { + /** + * @param {boolean} useWorker + * @this{EditSession} + */ set: function(useWorker) { this.$useWorker = useWorker; @@ -2533,6 +2565,10 @@ config.defineOptions(EditSession.prototype, "session", { }, useSoftTabs: {initialValue: true}, tabSize: { + /** + * @param tabSize + * @this{EditSession} + */ set: function(tabSize) { tabSize = parseInt(tabSize); if (tabSize > 0 && this.$tabSize !== tabSize) { @@ -2567,3 +2603,4 @@ config.defineOptions(EditSession.prototype, "session", { }); exports.EditSession = EditSession; + diff --git a/src/edit_session/bracket_match.js b/src/edit_session/bracket_match.js index 17d39b4390c..53c182c417e 100644 --- a/src/edit_session/bracket_match.js +++ b/src/edit_session/bracket_match.js @@ -1,11 +1,19 @@ "use strict"; - +/** + * @typedef {import("../edit_session").EditSession} EditSession + * @typedef {import("../edit_session").Point} Point + */ var TokenIterator = require("../token_iterator").TokenIterator; var Range = require("../range").Range; - function BracketMatch() { + /** + * + * @param {Point} position + * @param {string} [chr] + * @this {EditSession} + */ this.findMatchingBracket = function(position, chr) { if (position.column == 0) return null; @@ -21,7 +29,12 @@ function BracketMatch() { else return this.$findOpeningBracket(match[2], position); }; - + + /** + * @param {Point} pos + * @return {null|Range} + * @this {EditSession} + */ this.getBracketRange = function(pos) { var line = this.getLine(pos.row); var before = true, range; @@ -71,6 +84,7 @@ function BracketMatch() { * @param {Point} pos * @param {boolean} [isBackwards] * @returns {null|Range[]} + * @this {EditSession} */ this.getMatchingBracketRanges = function(pos, isBackwards) { var line = this.getLine(pos.row); @@ -110,6 +124,14 @@ function BracketMatch() { ">": "<" }; + /** + * + * @param {string} bracket + * @param {Point} position + * @param {RegExp} [typeRe] + * @return {Point|null} + * @this {EditSession} + */ this.$findOpeningBracket = function(bracket, position, typeRe) { var openBracket = this.$brackets[bracket]; var depth = 1; @@ -168,6 +190,14 @@ function BracketMatch() { return null; }; + /** + * + * @param {string} bracket + * @param {Point} position + * @param {RegExp} [typeRe] + * @return {Point|null} + * @this {EditSession} + */ this.$findClosingBracket = function(bracket, position, typeRe) { var closingBracket = this.$brackets[bracket]; var depth = 1; @@ -228,8 +258,9 @@ function BracketMatch() { /** * Returns [[Range]]'s for matching tags and tag names, if there are any - * @param {Position} pos + * @param {Point} pos * @returns {{closeTag: Range, closeTagName: Range, openTag: Range, openTagName: Range} | undefined} + * @this {EditSession} */ this.getMatchingTags = function (pos) { var iterator = new TokenIterator(this, pos.row, pos.column); diff --git a/src/edit_session/fold.js b/src/edit_session/fold.js index a3c752fd270..fe2b9d5dc6c 100644 --- a/src/edit_session/fold.js +++ b/src/edit_session/fold.js @@ -1,11 +1,21 @@ "use strict"; - +/** + * @typedef {import("./fold_line").FoldLine} FoldLine + * @typedef {import("../range").Range} Range + * @typedef {import("../../ace-internal").Ace.Point} Point + * @typedef {import("../../ace-internal").Ace.IRange} IRange + */ var RangeList = require("../range_list").RangeList; /* * Simple fold-data struct. **/ class Fold extends RangeList { + + /** + * @param {Range} range + * @param {any} placeholder + */ constructor(range, placeholder) { super(); this.foldLine = null; @@ -15,6 +25,7 @@ class Fold extends RangeList { this.end = range.end; this.sameRow = range.start.row == range.end.row; + /**@type {Fold[]}*/ this.subFolds = this.ranges = []; } @@ -22,6 +33,9 @@ class Fold extends RangeList { return '"' + this.placeholder + '" ' + this.range.toString(); } + /** + * @param {FoldLine} foldLine + */ setFoldLine(foldLine) { this.foldLine = foldLine; this.subFolds.forEach(function(fold) { @@ -39,6 +53,9 @@ class Fold extends RangeList { return fold; } + /** + * @param {Fold} fold + */ addSubFold(fold) { if (this.range.isEqual(fold)) return; @@ -79,27 +96,46 @@ class Fold extends RangeList { return fold; } - + + /** + * @param {IRange} range + */ restoreRange(range) { return restoreRange(range, this.start); } } +/** + * @param {Point} point + * @param {Point} anchor + */ function consumePoint(point, anchor) { point.row -= anchor.row; if (point.row == 0) point.column -= anchor.column; } +/** + * @param {IRange} range + * @param {Point} anchor + */ function consumeRange(range, anchor) { consumePoint(range.start, anchor); consumePoint(range.end, anchor); } +/** + * @param {Point} point + * @param {Point} anchor + */ function restorePoint(point, anchor) { if (point.row == 0) point.column += anchor.column; point.row += anchor.row; } +/** + * @param {IRange} range + * @param {Point} anchor + */ function restoreRange(range, anchor) { restorePoint(range.start, anchor); restorePoint(range.end, anchor); diff --git a/src/edit_session/fold_line.js b/src/edit_session/fold_line.js index d8d7ce8e433..892fd128e73 100644 --- a/src/edit_session/fold_line.js +++ b/src/edit_session/fold_line.js @@ -1,14 +1,19 @@ "use strict"; - +/** + * @typedef {import("./fold").Fold} Fold + */ var Range = require("../range").Range; class FoldLine { /** * If an array is passed in, the folds are expected to be sorted already. + * @param {FoldLine[]} foldData + * @param {Fold[]|Fold} folds */ constructor(foldData, folds) { this.foldData = foldData; if (Array.isArray(folds)) { + /**@type {Fold[]} */ this.folds = folds; } else { folds = this.folds = [ folds ]; @@ -24,8 +29,10 @@ class FoldLine { fold.setFoldLine(this); }, this); } - /* + + /** * Note: This doesn't update wrapData! + * @param {number} shift */ shiftRow(shift) { this.start.row += shift; @@ -36,8 +43,12 @@ class FoldLine { }); } + /** + * @param {Fold} fold + */ addFold(fold) { if (fold.sameRow) { + // @ts-expect-error TODO: startRow, endRow are missing in Fold and FoldLine if (fold.start.row < this.startRow || fold.endRow > this.endRow) { throw new Error("Can't add a fold to this FoldLine as it has no connection"); } @@ -66,10 +77,18 @@ class FoldLine { fold.foldLine = this; } + /** + * @param {number} row + */ containsRow(row) { return row >= this.start.row && row <= this.end.row; } + /** + * @param {Function} callback + * @param {number} endRow + * @param {number} endColumn + */ walk(callback, endRow, endColumn) { var lastEnd = 0, folds = this.folds, @@ -108,6 +127,11 @@ class FoldLine { callback(null, endRow, endColumn, lastEnd, isNewRow); } + /** + * @param {number} row + * @param {number} column + * @return {{ fold: Fold, kind: string } | null} + */ getNextFoldTo(row, column) { var fold, cmp; for (var i = 0; i < this.folds.length; i++) { @@ -128,6 +152,11 @@ class FoldLine { return null; } + /** + * @param {number} row + * @param {number} column + * @param {number} len + */ addRemoveChars(row, column, len) { var ret = this.getNextFoldTo(row, column), fold, folds; @@ -159,6 +188,11 @@ class FoldLine { } } + /** + * @param {number} row + * @param {number} column + * @return {FoldLine | null} + */ split(row, column) { var pos = this.getNextFoldTo(row, column); @@ -183,6 +217,9 @@ class FoldLine { return newFoldLine; } + /** + * @param {FoldLine} foldLineNext + */ merge(foldLineNext) { var folds = foldLineNext.folds; for (var i = 0; i < folds.length; i++) { @@ -204,6 +241,10 @@ class FoldLine { return ret.join("\n"); } + /** + * @param {number} idx + * @return {import("../../ace-internal").Ace.Point} + */ idxToPosition(idx) { var lastFoldEndColumn = 0; diff --git a/src/edit_session/folding.js b/src/edit_session/folding.js index 50ce67d0443..a4c6b3959ce 100644 --- a/src/edit_session/folding.js +++ b/src/edit_session/folding.js @@ -1,3 +1,4 @@ +// @ts-nocheck "use strict"; var Range = require("../range").Range; @@ -6,12 +7,25 @@ var Fold = require("./fold").Fold; var TokenIterator = require("../token_iterator").TokenIterator; var MouseEvent = require("../mouse/mouse_event").MouseEvent; +/** + * @typedef {import("../edit_session").EditSession & import("../../ace-internal").Ace.Folding} IFolding + * @typedef {import("../ace-internal").Ace.Delta } Delta + */ + +/** + * @this {IFolding} + * @type {IFolding} + */ function Folding() { - /* + /** * Looks up a fold at a given row/column. Possible values for side: * -1: ignore a fold if fold.start = row/column * +1: ignore a fold if fold.end = row/column - */ + * @param {number} row + * @param {number} column + * @param {number} [side] + * @return {Fold} + **/ this.getFoldAt = function(row, column, side) { var foldLine = this.getFoldLine(row); if (!foldLine) @@ -31,10 +45,11 @@ function Folding() { } }; - /* + /** * Returns all folds in the given range. Note, that this will return folds - * - */ + * @param {Range| Delta} range + * @returns {Fold[]} + **/ this.getFoldsInRange = function(range) { var start = range.start; var end = range.end; @@ -79,8 +94,14 @@ function Folding() { return foundFolds; }; + /** + * + * @param {Range[]|Range}ranges + * @returns {Fold[]} + */ this.getFoldsInRangeList = function(ranges) { if (Array.isArray(ranges)) { + /**@type {Fold[]} */ var folds = []; ranges.forEach(function(range) { folds = folds.concat(this.getFoldsInRange(range)); @@ -91,8 +112,9 @@ function Folding() { return folds; }; - /* + /** * Returns all folds in the document + * @returns {Fold[]} */ this.getAllFolds = function() { var folds = []; @@ -105,7 +127,7 @@ function Folding() { return folds; }; - /* + /** * Returns the string between folds at the given position. * E.g. * foob|arwolrd -> "bar" @@ -121,6 +143,11 @@ function Folding() { * foob|arwolrd -trim=-1> "b" * foobarwol|rd -trim=+1> "rld" * fo|obarwolrd -trim=00> "foo" + * @param {number} row + * @param {number} column + * @param {number} [trim] + * @param {FoldLine} [foldLine] + * @returns {string | null} */ this.getFoldStringAt = function(row, column, trim, foldLine) { foldLine = foldLine || this.getFoldLine(row); @@ -157,6 +184,12 @@ function Folding() { return str; }; + /** + * + * @param {number} docRow + * @param {FoldLine} [startFoldLine] + * @returns {null|FoldLine} + */ this.getFoldLine = function(docRow, startFoldLine) { var foldData = this.$foldData; var i = 0; @@ -175,7 +208,12 @@ function Folding() { return null; }; - // returns the fold which starts after or contains docRow + /** + * Returns the fold which starts after or contains docRow + * @param {number} docRow + * @param {FoldLine} [startFoldLine] + * @returns {null|FoldLine} + */ this.getNextFoldLine = function(docRow, startFoldLine) { var foldData = this.$foldData; var i = 0; @@ -192,6 +230,12 @@ function Folding() { return null; }; + /** + * + * @param {number} first + * @param {number} last + * @return {number} + */ this.getFoldedRowCount = function(first, last) { var foldData = this.$foldData, rowCount = last-first+1; for (var i = 0; i < foldData.length; i++) { @@ -216,6 +260,11 @@ function Folding() { return rowCount; }; + /** + * + * @param {FoldLine}foldLine + * @return {FoldLine} + */ this.$addFoldLine = function(foldLine) { this.$foldData.push(foldLine); this.$foldData.sort(function(a, b) { @@ -227,19 +276,24 @@ function Folding() { /** * Adds a new fold. * - * @returns + * @param {Fold|string} placeholder + * @param {Range} [range] + * @returns {Fold} * The new created Fold object or an existing fold object in case the * passed in range fits an existing fold exactly. + * @this {IFolding} */ this.addFold = function(placeholder, range) { var foldData = this.$foldData; var added = false; + /**@type {Fold}*/ var fold; if (placeholder instanceof Fold) fold = placeholder; else { fold = new Fold(range, placeholder); + // @ts-ignore fold.collapseChildren = range.collapseChildren; } this.$clipRangeToDocument(fold.range); @@ -312,12 +366,19 @@ function Folding() { return fold; }; + /** + * @param {Fold[]} folds + */ this.addFolds = function(folds) { folds.forEach(function(fold) { this.addFold(fold); }, this); }; + /** + * + * @param {Fold} fold + */ this.removeFold = function(fold) { var foldLine = fold.foldLine; var startRow = foldLine.start.row; @@ -371,6 +432,10 @@ function Folding() { this._signal("changeFold", { data: fold, action: "remove" }); }; + /** + * + * @param {Fold[]} folds + */ this.removeFolds = function(folds) { // We need to clone the folds array passed in as it might be the folds // array of a fold line and as we call this.removeFold(fold), folds @@ -386,6 +451,9 @@ function Folding() { this.$modified = true; }; + /** + * @param {Fold} fold + */ this.expandFold = function(fold) { this.removeFold(fold); fold.subFolds.forEach(function(subFold) { @@ -398,12 +466,21 @@ function Folding() { fold.subFolds = []; }; + /** + * @param {Fold[]}folds + */ this.expandFolds = function(folds) { folds.forEach(function(fold) { this.expandFold(fold); }, this); }; + /** + * + * @param {number|null|import("../../ace-internal").Ace.Point|Range|Range[]} [location] + * @param {boolean} [expandInner] + * @return {Fold[]| undefined} + */ this.unfold = function(location, expandInner) { var range, folds; if (location == null) { @@ -445,24 +522,48 @@ function Folding() { return outermostFolds; }; - /* + /** * Checks if a given documentRow is folded. This is true if there are some * folded parts such that some parts of the line is still visible. + * @param {number} docRow + * @param {FoldLine} [startFoldRow] + * @returns {boolean} **/ this.isRowFolded = function(docRow, startFoldRow) { return !!this.getFoldLine(docRow, startFoldRow); }; + /** + * + * @param {number} docRow + * @param {FoldLine} [startFoldRow] + * @return {number} + */ this.getRowFoldEnd = function(docRow, startFoldRow) { var foldLine = this.getFoldLine(docRow, startFoldRow); return foldLine ? foldLine.end.row : docRow; }; + /** + * + * @param {number} docRow + * @param {FoldLine} [startFoldRow] + * @returns {number} + */ this.getRowFoldStart = function(docRow, startFoldRow) { var foldLine = this.getFoldLine(docRow, startFoldRow); return foldLine ? foldLine.start.row : docRow; }; + /** + * + * @param {FoldLine} foldLine + * @param {number | null} [endRow] + * @param {number | null} [endColumn] + * @param {number | null} [startRow] + * @param {number | null} [startColumn] + * @return {string} + */ this.getFoldDisplayLine = function(foldLine, endRow, endColumn, startRow, startColumn) { if (startRow == null) startRow = foldLine.start.row; @@ -496,6 +597,14 @@ function Folding() { return textLine; }; + /** + * + * @param {number} row + * @param {number | null} endColumn + * @param {number | null} startRow + * @param {number | null} startColumn + * @return {string} + */ this.getDisplayLine = function(row, endColumn, startRow, startColumn) { var foldLine = this.getFoldLine(row); @@ -509,6 +618,9 @@ function Folding() { } }; + /** + * @return {FoldLine[]} + */ this.$cloneFoldData = function() { var fd = []; fd = this.$foldData.map(function(foldLine) { @@ -521,6 +633,9 @@ function Folding() { return fd; }; + /** + * @param {boolean} [tryToUnfold] + */ this.toggleFold = function(tryToUnfold) { var selection = this.selection; var range = selection.getRange(); @@ -581,6 +696,13 @@ function Folding() { this.addFold(placeholder, range); }; + /** + * + * @param {number} row + * @param {number} column + * @param {number} [dir] + * @return {Range | undefined} + */ this.getCommentFoldRange = function(row, column, dir) { var iterator = new TokenIterator(this, row, column); var token = iterator.getCurrentToken(); @@ -628,6 +750,13 @@ function Folding() { } }; + /** + * + * @param {number | null} [startRow] + * @param {number | null} [endRow] + * @param {number | null} [depth] + * @param {Function} [test] + */ this.foldAll = function(startRow, endRow, depth, test) { if (depth == undefined) depth = 100000; // JSON.stringify doesn't hanle Infinity @@ -656,13 +785,20 @@ function Folding() { } } }; - + + /** + * + * @param {number} level + */ this.foldToLevel = function(level) { this.foldAll(); while (level-- > 0) this.unfold(null, false); }; - + + /** + * + */ this.foldAllComments = function() { var session = this; this.foldAll(null, null, null, function(row) { @@ -685,6 +821,10 @@ function Folding() { "markbeginend": 1 }; this.$foldStyle = "markbegin"; + + /** + * @param {string} style + */ this.setFoldStyle = function(style) { if (!this.$foldStyles[style]) throw new Error("invalid fold style: " + style + "[" + Object.keys(this.$foldStyles).join(", ") + "]"); @@ -703,10 +843,14 @@ function Folding() { this.$setFolding(mode); }; + /** + * @param {import("../../ace-internal").Ace.FoldMode} foldMode + */ this.$setFolding = function(foldMode) { if (this.$foldMode == foldMode) return; - + + this.$foldMode = foldMode; this.off('change', this.$updateFoldWidgets); @@ -727,7 +871,11 @@ function Folding() { this.on('change', this.$updateFoldWidgets); this.on('tokenizerUpdate', this.$tokenizerUpdateFoldWidgets); }; - + /** + * @param {number} row + * @param {boolean} [ignoreCurrent] + * @return {{range?: Range, firstRange?: Range}} + */ this.getParentFoldRangeData = function (row, ignoreCurrent) { var fw = this.foldWidgets; if (!fw || (ignoreCurrent && fw[row])) @@ -755,6 +903,11 @@ function Folding() { }; }; + /** + * + * @param {number} row + * @param {any} e + */ this.onFoldWidgetClick = function(row, e) { if (e instanceof MouseEvent) e = e.domEvent; @@ -772,7 +925,13 @@ function Folding() { el.className += " ace_invalid"; } }; - + + /** + * + * @param {number} row + * @param options + * @return {Fold|*} + */ this.$toggleFoldWidget = function(row, options) { if (!this.getFoldWidget) return; @@ -819,8 +978,10 @@ function Folding() { return range; }; - - + /** + * + * @param {boolean} [toggleParent] + */ this.toggleFoldWidget = function(toggleParent) { var row = this.selection.getCursor().row; row = this.getRowFoldStart(row); @@ -844,6 +1005,9 @@ function Folding() { } }; + /** + * @param {Delta} delta + */ this.updateFoldWidgets = function(delta) { var firstRow = delta.start.row; var len = delta.end.row - firstRow; @@ -858,6 +1022,9 @@ function Folding() { this.foldWidgets.splice.apply(this.foldWidgets, args); } }; + /** + * @param e + */ this.tokenizerUpdateFoldWidgets = function(e) { var rows = e.data; if (rows.first != rows.last) { diff --git a/src/editor.js b/src/editor.js index fbed78eb579..b088521e0cb 100644 --- a/src/editor.js +++ b/src/editor.js @@ -1,5 +1,12 @@ "use strict"; +/** + * @typedef {import("./virtual_renderer").VirtualRenderer} VirtualRenderer + * @typedef {import("./selection").Selection} Selection + * @typedef {import("../ace-internal").Ace.Point} Point + * @typedef {import("../ace-internal").Ace.SearchOptions} SearchOptions + */ + var oop = require("./lib/oop"); var dom = require("./lib/dom"); var lang = require("./lib/lang"); @@ -23,6 +30,7 @@ var nls = require("./config").nls; var clipboard = require("./clipboard"); var keys = require('./lib/keys'); + /** * The main entry point into the Ace functionality. * @@ -35,26 +43,33 @@ class Editor { * Creates a new `Editor` object. * * @param {VirtualRenderer} renderer Associated `VirtualRenderer` that draws everything - * @param {EditSession} session The `EditSession` to refer to + * @param {EditSession} [session] The `EditSession` to refer to + * @param {Partial} [options] The default options **/ constructor(renderer, session, options) { + /**@type{EditSession}*/this.session; + this.$toDestroy = []; + var container = renderer.getContainerElement(); + /**@type {HTMLElement & {env?, value?}}*/ this.container = container; + /**@type {VirtualRenderer}*/ this.renderer = renderer; + /**@type {string}*/ this.id = "editor" + (++Editor.$uid); - this.commands = new CommandManager(useragent.isMac ? "mac" : "win", defaultCommands); if (typeof document == "object") { this.textInput = new TextInput(renderer.getTextAreaContainer(), this); this.renderer.textarea = this.textInput.getElement(); // TODO detect touch event support + /**@type {MouseHandler}*/ this.$mouseHandler = new MouseHandler(this); new FoldHandler(this); } - + /**@type {KeyBinding}*/ this.keyBinding = new KeyBinding(this); - + /**@type {Search}*/ this.$search = new Search().set({ wrap: true }); @@ -117,6 +132,9 @@ class Editor { } this.$opResetTimer.schedule(); + /** + * @type {{[key: string]: any;}} + */ this.curOp = this.session.curOp = { command: commandEvent.command || {}, args: commandEvent.args, @@ -125,6 +143,9 @@ class Editor { this.curOp.selectionBefore = this.selection.toJSON(); } + /** + * @arg e + */ endOperation(e) { if (this.curOp && this.session) { if (e && e.returnValue === false || !this.session) @@ -170,7 +191,10 @@ class Editor { this.curOp = null; } } - + + /** + * @param e + */ $historyTracker(e) { if (!this.$mergeUndoDeltas) return; @@ -209,8 +233,8 @@ class Editor { /** * Sets a new key handler, such as "vim" or "windows". - * @param {String} keyboardHandler The new key handler - * + * @param {String | import("../ace-internal").Ace.KeyboardHandler | null} keyboardHandler The new key handler + * @param {() => void} [cb] **/ setKeyboardHandler(keyboardHandler, cb) { if (keyboardHandler && typeof keyboardHandler === "string" && keyboardHandler != "ace") { @@ -223,6 +247,7 @@ class Editor { }); } else { this.$keybindingId = null; + // @ts-ignore this.keyBinding.setKeyboardHandler(keyboardHandler); cb && cb(); } @@ -230,25 +255,17 @@ class Editor { /** * Returns the keyboard handler, such as "vim" or "windows". - * - * @returns {String} - * + * @returns {Object} **/ getKeyboardHandler() { return this.keyBinding.getKeyboardHandler(); } - /** - * Emitted whenever the [[EditSession]] changes. - * @event changeSession - * @param {Object} e An object with two properties, `oldSession` and `session`, that represent the old and new [[EditSession]]s. - * - **/ + /** * Sets a new editsession to use. This method also emits the `'changeSession'` event. - * @param {EditSession} session The new session to use - * + * @param {EditSession} [session] The new session to use **/ setSession(session) { if (this.session == session) @@ -374,7 +391,7 @@ class Editor { /** * Sets the current document to `val`. * @param {String} val The new value to set for the document - * @param {Number} cursorPos Where to set the new value. `undefined` or 0 is selectAll, -1 is at the document start, and 1 is at the end + * @param {Number} [cursorPos] Where to set the new value. `undefined` or 0 is selectAll, -1 is at the document start, and 1 is at the end * * @returns {String} The current document value * @related Document.setValue @@ -413,9 +430,7 @@ class Editor { /** * {:VirtualRenderer.onResize} - * @param {Boolean} force If `true`, recomputes the size, even if the height and width haven't changed - * - * + * @param {Boolean} [force] If `true`, recomputes the size, even if the height and width haven't changed * @related VirtualRenderer.onResize **/ resize(force) { @@ -424,8 +439,8 @@ class Editor { /** * {:VirtualRenderer.setTheme} - * @param {String} theme The path to a theme - * @param {Function} cb optional callback called when theme is loaded + * @param {string | import("../ace-internal").Ace.Theme} theme The path to a theme + * @param {() => void} [cb] optional callback called when theme is loaded **/ setTheme(theme, cb) { this.renderer.setTheme(theme, cb); @@ -444,8 +459,6 @@ class Editor { /** * {:VirtualRenderer.setStyle} * @param {String} style A class name - * - * * @related VirtualRenderer.setStyle **/ setStyle(style) { @@ -455,13 +468,15 @@ class Editor { /** * {:VirtualRenderer.unsetStyle} * @related VirtualRenderer.unsetStyle - **/ + * @param {string} style + */ unsetStyle(style) { this.renderer.unsetStyle(style); } /** * Gets the current font size of the editor text. + * @return {string} */ getFontSize() { return this.getOption("fontSize") || @@ -471,8 +486,6 @@ class Editor { /** * Set a new font size (in pixels) for the editor text. * @param {String} size A font size ( _e.g._ "12px") - * - * **/ setFontSize(size) { this.setOption("fontSize", size); @@ -568,9 +581,6 @@ class Editor { /** * Emitted once the editor comes into focus. - * @event focus - * - * **/ onFocus(e) { if (this.$isFocused) @@ -583,9 +593,6 @@ class Editor { /** * Emitted once the editor has been blurred. - * @event blur - * - * **/ onBlur(e) { if (!this.$isFocused) @@ -596,6 +603,8 @@ class Editor { this._emit("blur", e); } + /** + */ $cursorChange() { this.renderer.updateCursor(); this.$highlightBrackets(); @@ -604,11 +613,7 @@ class Editor { /** * Emitted whenever the document is changed. - * @event change - * @param {Object} delta Contains a single property, `data`, which has the delta of changes - * - * - * + * @param {import("../ace-internal").Ace.Delta} delta Contains a single property, `data`, which has the delta of changes **/ onDocumentChange(delta) { // Rerender and emit "change" event. @@ -638,16 +643,17 @@ class Editor { /** * Emitted when the selection changes. - * **/ onCursorChange() { this.$cursorChange(); this._signal("changeSelection"); } + /** + */ $updateHighlightActiveLine() { var session = this.getSession(); - + /**@type {Point|false}*/ var highlight; if (this.$highlightActiveLine) { if (this.$selectionStyle != "line" || !this.selection.isMultiLine()) @@ -673,6 +679,10 @@ class Editor { } } + /** + * + * @param e + */ onSelectionChange(e) { var session = this.session; @@ -743,7 +753,9 @@ class Editor { this.renderer.setAnnotations(this.session.getAnnotations()); } - + /** + * @param e + */ onChangeMode (e) { this.renderer.updateText(); this._emit("changeMode", e); @@ -759,6 +771,8 @@ class Editor { } + /** + */ onChangeFold() { // Update the active line marker as due to folding changes the current // line range on the screen might have changed. @@ -776,12 +790,7 @@ class Editor { return this.session.getTextRange(this.getSelectionRange()); } - /** - * Emitted when text is copied. - * @event copy - * @param {String} text The copied text - * - **/ + /** * Returns the string of text currently highlighted. * @returns {String} @@ -820,24 +829,22 @@ class Editor { this.commands.exec("cut", this); } - /** - * Emitted when text is pasted. - * @event paste - * @param {Object} an object which contains one property, `text`, that represents the text to be pasted. Editing this property will alter the text that is pasted. - * - * - **/ + /** * Called whenever a text "paste" happens. * @param {String} text The pasted text - * - * + * @param {any} event **/ onPaste(text, event) { var e = {text: text, event: event}; this.commands.exec("paste", this, e); } - + + /** + * + * @param e + * @returns {boolean} + */ $handlePaste(e) { if (typeof e == "string") e = {text: e}; @@ -873,6 +880,12 @@ class Editor { } } + /** + * + * @param {string | string[]} command + * @param [args] + * @return {boolean} + */ execCommand(command, args) { return this.commands.exec(command, this, args); } @@ -880,7 +893,7 @@ class Editor { /** * Inserts `text` into wherever the cursor is pointing. * @param {String} text The new text to add - * + * @param {boolean} [pasted] **/ insert(text, pasted) { var session = this.session; @@ -913,7 +926,7 @@ class Editor { this.clearSelection(); } else if (this.session.getOverwrite() && text.indexOf("\n") == -1) { - var range = new Range.fromPoints(cursor, cursor); + var range = Range.fromPoints(cursor, cursor); range.end.column += text.length; this.session.remove(range); } @@ -1001,7 +1014,12 @@ class Editor { } } - + /** + * + * @param text + * @param composition + * @returns {*} + */ onTextInput(text, composition) { if (!composition) return this.keyBinding.onTextInput(text); @@ -1014,7 +1032,11 @@ class Editor { applyComposition(); this.endOperation(); } - + + /** + * @param {string} [text] + * @param {any} [composition] + */ applyComposition(text, composition) { if (composition.extendLeft || composition.extendRight) { var r = this.selection.getRange(); @@ -1045,8 +1067,6 @@ class Editor { /** * Pass in `true` to enable overwrites in your session, or `false` to disable. If overwrites is enabled, any text you enter will type over any text after it. If the value of `overwrite` changes, this function also emits the `changeOverwrite` event. * @param {Boolean} overwrite Defines whether or not to set overwrites - * - * * @related EditSession.setOverwrite **/ setOverwrite(overwrite) { @@ -1102,15 +1122,10 @@ class Editor { return this.getOption("dragDelay"); } - /** - * Emitted when the selection style changes, via [[Editor.setSelectionStyle]]. - * @event changeSelectionStyle - * @param {Object} data Contains one property, `data`, which indicates the new selection style - **/ + /** * Draw selection markers spanning whole line, or only over selected text. Default value is "line" - * @param {String} val The new selection style "line"|"text" - * + * @param {"fullLine" | "screenLine" | "text" | "line"} val The new selection style "line"|"text" **/ setSelectionStyle(val) { this.setOption("selectionStyle", val); @@ -1118,7 +1133,7 @@ class Editor { /** * Returns the current selection style. - * @returns {String} + * @returns {import("../ace-internal").Ace.EditorOptions["selectionStyle"]} **/ getSelectionStyle() { return this.getOption("selectionStyle"); @@ -1138,10 +1153,17 @@ class Editor { getHighlightActiveLine() { return this.getOption("highlightActiveLine"); } + + /** + * @param {boolean} shouldHighlight + */ setHighlightGutterLine(shouldHighlight) { this.setOption("highlightGutterLine", shouldHighlight); } + /** + * @returns {Boolean} + */ getHighlightGutterLine() { return this.getOption("highlightGutterLine"); } @@ -1149,7 +1171,6 @@ class Editor { /** * Determines if the currently selected word should be highlighted. * @param {Boolean} shouldHighlight Set to `true` to highlight the currently selected word - * **/ setHighlightSelectedWord(shouldHighlight) { this.setOption("highlightSelectedWord", shouldHighlight); @@ -1163,10 +1184,16 @@ class Editor { return this.$highlightSelectedWord; } + /** + * @param {boolean} shouldAnimate + */ setAnimatedScroll(shouldAnimate){ this.renderer.setAnimatedScroll(shouldAnimate); } + /** + * @return {boolean} + */ getAnimatedScroll(){ return this.renderer.getAnimatedScroll(); } @@ -1174,7 +1201,6 @@ class Editor { /** * If `showInvisibles` is set to `true`, invisible characters—like spaces or new lines—are show in the editor. * @param {Boolean} showInvisibles Specifies whether or not to show invisible characters - * **/ setShowInvisibles(showInvisibles) { this.renderer.setShowInvisibles(showInvisibles); @@ -1188,18 +1214,30 @@ class Editor { return this.renderer.getShowInvisibles(); } + /** + * @param {boolean} display + */ setDisplayIndentGuides(display) { this.renderer.setDisplayIndentGuides(display); } + /** + * @return {boolean} + */ getDisplayIndentGuides() { return this.renderer.getDisplayIndentGuides(); } + /** + * @param {boolean} highlight + */ setHighlightIndentGuides(highlight) { this.renderer.setHighlightIndentGuides(highlight); } + /** + * @return {boolean} + */ getHighlightIndentGuides() { return this.renderer.getHighlightIndentGuides(); } @@ -1241,7 +1279,6 @@ class Editor { /** * If `readOnly` is true, then the editor is set to read-only mode, and none of the content can change. * @param {Boolean} readOnly Specifies whether the editor can be modified or not - * **/ setReadOnly(readOnly) { this.setOption("readOnly", readOnly); @@ -1258,7 +1295,6 @@ class Editor { /** * Specifies whether to use behaviors or not. ["Behaviors" in this case is the auto-pairing of special characters, like quotation marks, parenthesis, or brackets.]{: #BehaviorsDef} * @param {Boolean} enabled Enables or disables behaviors - * **/ setBehavioursEnabled(enabled) { this.setOption("behavioursEnabled", enabled); @@ -1266,7 +1302,6 @@ class Editor { /** * Returns `true` if the behaviors are currently enabled. {:BehaviorsDef} - * * @returns {Boolean} **/ getBehavioursEnabled() { @@ -1277,7 +1312,6 @@ class Editor { * Specifies whether to use wrapping behaviors or not, i.e. automatically wrapping the selection with characters such as brackets * when such a character is typed in. * @param {Boolean} enabled Enables or disables wrapping behaviors - * **/ setWrapBehavioursEnabled(enabled) { this.setOption("wrapBehavioursEnabled", enabled); @@ -1285,6 +1319,7 @@ class Editor { /** * Returns `true` if the wrapping behaviors are currently enabled. + * @returns {boolean} **/ getWrapBehavioursEnabled() { return this.getOption("wrapBehavioursEnabled"); @@ -1306,18 +1341,23 @@ class Editor { return this.getOption("showFoldWidgets"); } + /** + * @param {boolean} fade + */ setFadeFoldWidgets(fade) { this.setOption("fadeFoldWidgets", fade); } + /** + * @returns {boolean} + */ getFadeFoldWidgets() { return this.getOption("fadeFoldWidgets"); } /** * Removes the current selection or one character. - * @param {String} dir The direction of the deletion to occur, either "left" or "right" - * + * @param {'left' | 'right'} [dir] The direction of the deletion to occur, either "left" or "right" **/ remove(dir) { if (this.selection.isEmpty()){ @@ -1326,7 +1366,7 @@ class Editor { else this.selection.selectRight(); } - + var range = this.getSelectionRange(); if (this.getBehavioursEnabled()) { var session = this.session; @@ -1343,6 +1383,7 @@ class Editor { } } if (new_range) + // @ts-expect-error TODO: possible bug, new_range could be not a Range range = new_range; } @@ -1421,7 +1462,7 @@ class Editor { * inline in the editor such as, for example, code completions. * * @param {String} text Text to be inserted as "ghost" text - * @param {object} position Position to insert text to + * @param {Point} [position] Position to insert text to */ setGhostText(text, position) { if (!this.session.widgetManager) { @@ -1600,7 +1641,7 @@ class Editor { /** * Works like [[EditSession.getTokenAt]], except it returns a number. - * @returns {Number} + * @returns {any} **/ getNumberAt(row, column) { var _numberRx = /[\-]?[0-9]+(?:\.[0-9]+)?/g; @@ -1624,7 +1665,6 @@ class Editor { /** * If the character before the cursor is a number, this functions changes its value by `amount`. * @param {Number} amount The value to change the numeral by (can be negative to decrease value) - * **/ modifyNumber(amount) { var row = this.selection.getCursor().row; @@ -1635,6 +1675,7 @@ class Editor { var c = this.session.getTextRange(charRange); // if the char is a digit + // @ts-ignore if (!isNaN(parseFloat(c)) && isFinite(c)) { // get the whole number the digit is part of var nr = this.getNumberAt(row, column); @@ -1669,7 +1710,9 @@ class Editor { this.toggleWord(); } } - + + /** + */ toggleWord() { var row = this.selection.getCursor().row; var column = this.selection.getCursor().column; @@ -1776,7 +1819,7 @@ class Editor { doc.duplicateLines(row, row); } else { var point = reverse ? range.start : range.end; - var endPoint = doc.insert(point, doc.getTextRange(range), false); + var endPoint = doc.insert(point, doc.getTextRange(range)); range.start = point; range.end = endPoint; @@ -1787,7 +1830,6 @@ class Editor { /** * Shifts all the selected lines down one row. * - * @returns {Number} On success, it returns -1. * @related EditSession.moveLinesUp **/ moveLinesDown() { @@ -1796,7 +1838,6 @@ class Editor { /** * Shifts all the selected lines up one row. - * @returns {Number} On success, it returns -1. * @related EditSession.moveLinesDown **/ moveLinesUp() { @@ -1809,8 +1850,9 @@ class Editor { * { row: newRowLocation, column: newColumnLocation } * ``` * @param {Range} range The range of text you want moved within the document - * @param {Object} toPosition The location (row and column) where you want to move the text to - * + * @param {Point} toPosition The location (row and column) where you want to move the text to + * @param {boolean} [copy] + * * @returns {Range} The new range where the text was moved to. * @related EditSession.moveText **/ @@ -1820,7 +1862,6 @@ class Editor { /** * Copies all the selected lines up one row. - * @returns {Number} On success, returns 0. * **/ copyLinesUp() { @@ -1829,7 +1870,6 @@ class Editor { /** * Copies all the selected lines down one row. - * @returns {Number} On success, returns the number of new rows added; in other words, `lastRow - firstRow + 1`. * @related EditSession.duplicateLines * **/ @@ -1854,6 +1894,7 @@ class Editor { selection.fromOrientedRange(range); } else { var ranges = selection.rangeList.ranges; + // @ts-expect-error TODO: possible bug, no args in parameters selection.rangeList.detach(this.session); this.inVirtualSelectionMode = true; @@ -2039,7 +2080,8 @@ class Editor { /** * Moves the editor to the specified row. * @related VirtualRenderer.scrollToRow - **/ + * @param {number} row + */ scrollToRow(row) { this.renderer.scrollToRow(row); } @@ -2049,8 +2091,7 @@ class Editor { * @param {Number} line The line to scroll to * @param {Boolean} center If `true` * @param {Boolean} animate If `true` animates scrolling - * @param {Function} callback Function to be called when the animation has finished - * + * @param {() => void} [callback] Function to be called when the animation has finished * * @related VirtualRenderer.scrollToLine **/ @@ -2072,7 +2113,7 @@ class Editor { /** * Gets the current position of the cursor. - * @returns {Object} An object that looks something like this: + * @returns {Point} An object that looks something like this: * * ```json * { row: currRow, column: currCol } @@ -2086,7 +2127,7 @@ class Editor { /** * Returns the screen position of the cursor. - * @returns {Position} + * @returns {Point} * @related EditSession.documentToScreenPosition **/ getCursorPositionScreen() { @@ -2130,7 +2171,7 @@ class Editor { /** * Moves the cursor to the position indicated by `pos.row` and `pos.column`. - * @param {Position} pos An object with two properties, row and column + * @param {Point} pos An object with two properties, row and column * @related Selection.moveCursorToPosition **/ moveCursorToPosition(pos) { @@ -2139,8 +2180,9 @@ class Editor { /** * Moves the cursor's row and column to the next matching bracket or HTML tag. - * - **/ + * @param {boolean} [select] + * @param {boolean} [expand] + */ jumpToMatching(select, expand) { var cursor = this.getCursorPosition(); var iterator = new TokenIterator(this.session, cursor.row, cursor.column); @@ -2289,9 +2331,8 @@ class Editor { /** * Moves the cursor to the specified line number, and also into the indicated column. * @param {Number} lineNumber The line number to go to - * @param {Number} column A column number to go to - * @param {Boolean} animate If `true` animates scolling - * + * @param {Number} [column] A column number to go to + * @param {Boolean} [animate] If `true` animates scolling **/ gotoLine(lineNumber, column, animate) { this.selection.clearSelection(); @@ -2310,7 +2351,6 @@ class Editor { * @param {Number} row The new row number * @param {Number} column The new column number * - * * @related Editor.moveCursorTo **/ navigateTo(row, column) { @@ -2319,8 +2359,7 @@ class Editor { /** * Moves the cursor up in the document the specified number of times. Note that this does de-select the current selection. - * @param {Number} times The number of times to change navigation - * + * @param {Number} [times] The number of times to change navigation * **/ navigateUp(times) { @@ -2334,8 +2373,7 @@ class Editor { /** * Moves the cursor down in the document the specified number of times. Note that this does de-select the current selection. - * @param {Number} times The number of times to change navigation - * + * @param {Number} [times] The number of times to change navigation * **/ navigateDown(times) { @@ -2349,8 +2387,7 @@ class Editor { /** * Moves the cursor left in the document the specified number of times. Note that this does de-select the current selection. - * @param {Number} times The number of times to change navigation - * + * @param {Number} [times] The number of times to change navigation * **/ navigateLeft(times) { @@ -2369,8 +2406,7 @@ class Editor { /** * Moves the cursor right in the document the specified number of times. Note that this does de-select the current selection. - * @param {Number} times The number of times to change navigation - * + * @param {Number} [times] The number of times to change navigation * **/ navigateRight(times) { @@ -2443,10 +2479,9 @@ class Editor { /** * Replaces the first occurrence of `options.needle` with the value in `replacement`. - * @param {String} replacement The text to replace with - * @param {Object} options The [[Search `Search`]] options to use - * - * + * @param {String} [replacement] The text to replace with + * @param {Partial} [options] The [[Search `Search`]] options to use + * @return {number} **/ replace(replacement, options) { if (options) @@ -2469,10 +2504,9 @@ class Editor { /** * Replaces all occurrences of `options.needle` with the value in `replacement`. - * @param {String} replacement The text to replace with - * @param {Object} options The [[Search `Search`]] options to use - * - * + * @param {String} [replacement] The text to replace with + * @param {Partial} [options] The [[Search `Search`]] options to use + * @return {number} **/ replaceAll(replacement, options) { if (options) { @@ -2498,6 +2532,10 @@ class Editor { return replaced; } + /** + * @param {import("../ace-internal").Ace.IRange} range + * @param {string} [replacement] + */ $tryReplace(range, replacement) { var input = this.session.getTextRange(range); replacement = this.$search.replace(input, replacement); @@ -2512,7 +2550,7 @@ class Editor { /** * {:Search.getOptions} For more information on `options`, see [[Search `Search`]]. * @related Search.getOptions - * @returns {Object} + * @returns {Partial} **/ getLastSearchOptions() { return this.$search.getOptions(); @@ -2521,8 +2559,8 @@ class Editor { /** * Attempts to find `needle` within the document. For more information on `options`, see [[Search `Search`]]. * @param {String|RegExp|Object} needle The text to search for (optional) - * @param {Object} options An object defining various search properties - * @param {Boolean} animate If `true` animate scrolling + * @param {Partial} [options] An object defining various search properties + * @param {Boolean} [animate] If `true` animate scrolling * @related Search.find **/ find(needle, options, animate) { @@ -2566,9 +2604,8 @@ class Editor { /** * Performs another search for `needle` in the document. For more information on `options`, see [[Search `Search`]]. - * @param {Object} options search options - * @param {Boolean} animate If `true` animate scrolling - * + * @param {Partial} [options] search options + * @param {Boolean} [animate] If `true` animate scrolling * * @related Editor.find **/ @@ -2578,9 +2615,8 @@ class Editor { /** * Performs a search for `needle` backwards. For more information on `options`, see [[Search `Search`]]. - * @param {Object} options search options - * @param {Boolean} animate If `true` animate scrolling - * + * @param {Partial} [options] search options + * @param {Boolean} [animate] If `true` animate scrolling * * @related Editor.find **/ @@ -2588,6 +2624,11 @@ class Editor { this.find(options, {skipCurrent: true, backwards: true}, animate); } + /** + * + * @param {Range} range + * @param {boolean} [animate] + */ revealRange(range, animate) { this.session.unfold(range); this.selection.setSelectionRange(range); @@ -2696,7 +2737,6 @@ class Editor { }; } - $resetCursorStyle() { var style = this.$cursorStyle || "ace"; var cursorLayer = this.renderer.$cursorLayer; @@ -2835,6 +2875,9 @@ config.defineOptions(Editor.prototype, "editor", { } }, placeholder: { + /** + * @param message + */ set: function(message) { if (!this.$updatePlaceholder) { this.$updatePlaceholder = function() { @@ -2857,6 +2900,7 @@ config.defineOptions(Editor.prototype, "editor", { this.renderer.placeholderNode.textContent = this.$placeholder || ""; } }.bind(this); + // @ts-ignore this.on("input", this.$updatePlaceholder); } this.$updatePlaceholder(); @@ -2886,7 +2930,7 @@ config.defineOptions(Editor.prototype, "editor", { this.focus(); } }; - + /**@type {GutterKeyboardHandler}*/ var gutterKeyboardHandler; // If keyboard a11y mode is enabled we: @@ -3006,30 +3050,29 @@ config.defineOptions(Editor.prototype, "editor", { var relativeNumberRenderer = { - getText: function(session, row) { + getText: function(/**@type{EditSession}*/session, /**@type{number}*/row) { return (Math.abs(session.selection.lead.row - row) || (row + 1 + (row < 9 ? "\xb7" : ""))) + ""; }, - getWidth: function(session, lastLineNumber, config) { + getWidth: function(session, /**@type{number}*/lastLineNumber, config) { return Math.max( lastLineNumber.toString().length, (config.lastRow + 1).toString().length, 2 ) * config.characterWidth; }, - update: function(e, editor) { + update: function(e, /**@type{Editor}*/editor) { editor.renderer.$loop.schedule(editor.renderer.CHANGE_GUTTER); }, - attach: function(editor) { + attach: function(/**@type{Editor}*/editor) { editor.renderer.$gutterLayer.$renderer = this; editor.on("changeSelection", this.update); this.update(null, editor); }, - detach: function(editor) { + detach: function(/**@type{Editor}*/editor) { if (editor.renderer.$gutterLayer.$renderer == this) editor.renderer.$gutterLayer.$renderer = null; editor.off("changeSelection", this.update); this.update(null, editor); } }; - exports.Editor = Editor; diff --git a/src/ext/beautify.js b/src/ext/beautify.js index 10e7548bf00..be367fb4e21 100644 --- a/src/ext/beautify.js +++ b/src/ext/beautify.js @@ -13,10 +13,18 @@ exports.singletonTags = ["area", "base", "br", "col", "command", "embed", "hr", // insert a line break after block level tags exports.blockTags = ["article", "aside", "blockquote", "body", "div", "dl", "fieldset", "footer", "form", "head", "header", "html", "nav", "ol", "p", "script", "section", "style", "table", "tbody", "tfoot", "thead", "ul"]; +/** + * + * @type {{lineBreaksAfterCommasInCurlyBlock?: boolean}} + */ exports.formatOptions = { lineBreaksAfterCommasInCurlyBlock: true }; +/** + * + * @param {import("../edit_session").EditSession} session + */ exports.beautify = function(session) { var iterator = new TokenIterator(session, 0, 0); var token = iterator.getCurrentToken(); diff --git a/src/ext/code_lens.js b/src/ext/code_lens.js index 74947ff56e6..7a2c89a1228 100644 --- a/src/ext/code_lens.js +++ b/src/ext/code_lens.js @@ -1,9 +1,17 @@ "use strict"; +/** + * @typedef {import("../edit_session").EditSession} EditSession + * @typedef {import("../virtual_renderer").VirtualRenderer & {$textLayer: import("../layer/text").Text &{$lenses}}} VirtualRenderer + */ + var LineWidgets = require("../line_widgets").LineWidgets; var event = require("../lib/event"); var lang = require("../lib/lang"); var dom = require("../lib/dom"); +/** + * @param {VirtualRenderer} renderer + */ function clearLensElements(renderer) { var textLayer = renderer.$textLayer; var lensElements = textLayer.$lenses; @@ -12,6 +20,10 @@ function clearLensElements(renderer) { textLayer.$lenses = null; } +/** + * @param {number} changes + * @param {VirtualRenderer} renderer + */ function renderWidgets(changes, renderer) { var changed = changes & renderer.CHANGE_LINES || changes & renderer.CHANGE_FULL @@ -83,6 +95,9 @@ function renderWidgets(changes, renderer) { lensElements.pop().remove(); } +/** + * @param {EditSession} session + */ function clearCodeLensWidgets(session) { if (!session.lineWidgets) return; var widgetManager = session.widgetManager; @@ -92,6 +107,12 @@ function clearCodeLensWidgets(session) { }); } +/** + * + * @param {EditSession} session + * @param lenses + * @return {number} + */ exports.setLenses = function(session, lenses) { var firstRow = Number.MAX_VALUE; @@ -117,6 +138,9 @@ exports.setLenses = function(session, lenses) { return firstRow; }; +/** + * @param {import("../editor").Editor} editor + */ function attachToEditor(editor) { editor.codeLensProviders = []; editor.renderer.on("afterRender", renderWidgets); @@ -179,6 +203,9 @@ function attachToEditor(editor) { editor.on("input", editor.$updateLensesOnInput); } +/** + * @param {import("../editor").Editor} editor + */ function detachFromEditor(editor) { editor.off("input", editor.$updateLensesOnInput); editor.renderer.off("afterRender", renderWidgets); @@ -186,12 +213,19 @@ function detachFromEditor(editor) { editor.container.removeEventListener("click", editor.$codeLensClickHandler); } +/** + * @param {import("../editor").Editor} editor + * @param codeLensProvider + */ exports.registerCodeLensProvider = function(editor, codeLensProvider) { editor.setOption("enableCodeLens", true); editor.codeLensProviders.push(codeLensProvider); editor.$updateLensesOnInput(); }; +/** + * @param {EditSession} session + */ exports.clear = function(session) { exports.setLenses(session, null); }; diff --git a/src/ext/command_bar.js b/src/ext/command_bar.js index 537da0b3e22..4ee68c31e07 100644 --- a/src/ext/command_bar.js +++ b/src/ext/command_bar.js @@ -1,3 +1,7 @@ +/** + * @typedef {import("../editor").Editor} Editor + * @typedef {import("../../ace-internal").Ace.TooltipCommand} TooltipCommand + */ var Tooltip = require("../tooltip").Tooltip; var EventEmitter = require("../lib/event_emitter").EventEmitter; var lang = require("../lib/lang"); @@ -47,6 +51,10 @@ var keyDisplayMap = { * with mouse, depending on the alwaysShow property. */ class CommandBarTooltip { + /** + * @param {HTMLElement} parentNode + * @param {Partial} [options] + */ constructor(parentNode, options) { options = options || {}; this.parentNode = parentNode; @@ -82,7 +90,7 @@ class CommandBarTooltip { * The commands are added in sequential order. If there is not enough space on the main * toolbar, the remaining elements are added to the overflow menu. * - * @param {string} id + * @param {string} id * @param {TooltipCommand} command */ registerCommand(id, command) { @@ -90,7 +98,11 @@ class CommandBarTooltip { if (!registerForMainTooltip && !this.elements[MORE_OPTIONS_BUTTON_ID]) { this.$createCommand(MORE_OPTIONS_BUTTON_ID, { name: "···", - exec: function() { + exec: + /** + * @this {CommandBarTooltip} + */ + function() { this.$shouldHideMoreOptions = false; this.$setMoreOptionsVisibility(!this.isMoreOptionsShown()); }.bind(this), @@ -125,7 +137,7 @@ class CommandBarTooltip { * When true, the tooltip is always displayed while it is attached to an editor. * When false, the tooltip is displayed only when the mouse hovers over the active editor line. * - * @param {Editor} editor + * @param {boolean} alwaysShow */ setAlwaysShow(alwaysShow) { this.$alwaysShow = alwaysShow; @@ -275,6 +287,11 @@ class CommandBarTooltip { this.tooltip = this.moreOptions = this.parentNode = null; } + /** + * @param {string} id + * @param {TooltipCommand} command + * @param {boolean} forMainTooltip + */ $createCommand(id, command, forMainTooltip) { var parentEl = forMainTooltip ? this.tooltipEl : this.moreOptionsEl; var keyParts = []; @@ -298,6 +315,7 @@ class CommandBarTooltip { }); } + /**@type {any[]} */ var buttonNode; if (forMainTooltip && command.iconCssClass) { //Only support icon button for main tooltip, otherwise fall back to text button @@ -326,10 +344,15 @@ class CommandBarTooltip { } } + // @ts-ignore dom.buildDom(['div', { class: [BUTTON_CLASS_NAME, command.cssClass || ""].join(" "), ref: id }, buttonNode], parentEl, this.elements); this.commands[id] = command; - - var eventListener = function(e) { + + var eventListener = + /** + * @this {CommandBarTooltip} + */ + function(e) { if (this.editor) { this.editor.focus(); } @@ -349,6 +372,9 @@ class CommandBarTooltip { this.$updateElement(id); } + /** + * @param {boolean} visible + */ $setMoreOptionsVisibility(visible) { if (visible) { this.moreOptions.setTheme(this.editor.renderer.theme); @@ -425,6 +451,9 @@ class CommandBarTooltip { } } + /** + * @param {boolean} [enableHover] + */ $updateOnHoverHandlers(enableHover) { var tooltipEl = this.tooltip.getElement(); var moreOptionsEl = this.moreOptions.getElement(); @@ -460,7 +489,6 @@ class CommandBarTooltip { this.updatePosition(); this._signal("show"); } - $hideTooltip() { this.$mouseInTooltip = false; if (!this.isShown()) { @@ -471,6 +499,9 @@ class CommandBarTooltip { this._signal("hide"); } + /** + * @param {string} id + */ $updateElement(id) { var command = this.commands[id]; if (!command) { diff --git a/src/ext/elastic_tabstops_lite.js b/src/ext/elastic_tabstops_lite.js index f5726991789..66ce76e8c03 100644 --- a/src/ext/elastic_tabstops_lite.js +++ b/src/ext/elastic_tabstops_lite.js @@ -1,6 +1,8 @@ "use strict"; - class ElasticTabstopsLite { + /** + * @param {Editor} editor + */ constructor(editor) { this.$editor = editor; var self = this; @@ -23,7 +25,10 @@ class ElasticTabstopsLite { } }; } - + + /** + * @param {number[]} rows + */ processRows(rows) { this.$inChange = true; var checkedRows = []; @@ -48,6 +53,9 @@ class ElasticTabstopsLite { this.$inChange = false; } + /** + * @param {number} row + */ $findCellWidthsForBlock(row) { var cellWidths = [], widths; @@ -80,6 +88,10 @@ class ElasticTabstopsLite { return { cellWidths: cellWidths, firstRow: firstRow }; } + /** + * @param {number} row + * @returns {number[]} + */ $cellWidthsForRow(row) { var selectionColumns = this.$selectionColumnsForRow(row); // todo: support multicursor @@ -100,6 +112,10 @@ class ElasticTabstopsLite { return widths; } + /** + * @param {number} row + * @returns {number[]} + */ $selectionColumnsForRow(row) { var selections = [], cursor = this.$editor.getCursorPosition(); if (this.$editor.session.getSelection().isEmpty()) { @@ -111,6 +127,9 @@ class ElasticTabstopsLite { return selections; } + /** + * @param {number[][]} cellWidths + */ $setBlockCellWidthsToMax(cellWidths) { var startingNewBlock = true, blockStartRow, blockEndRow, maxWidth; var columnInfo = this.$izip_longest(cellWidths); @@ -149,6 +168,11 @@ class ElasticTabstopsLite { return cellWidths; } + /** + * @param {number[]} selectionColumns + * @param {number} cellRightEdge + * @returns {number} + */ $rightmostSelectionInCell(selectionColumns, cellRightEdge) { var rightmost = 0; @@ -166,6 +190,10 @@ class ElasticTabstopsLite { return rightmost; } + /** + * @param {number} row + * @returns {number[]} + */ $tabsForRow(row) { var rowTabs = [], line = this.$editor.session.getLine(row), re = /\t/g, match; @@ -177,6 +205,10 @@ class ElasticTabstopsLite { return rowTabs; } + /** + * @param {number} row + * @param {number[]} widths + */ $adjustRow(row, widths) { var rowTabs = this.$tabsForRow(row); @@ -217,7 +249,10 @@ class ElasticTabstopsLite { } } - // the is a (naive) Python port--but works for these purposes + /** + * The is a (naive) Python port--but works for these purposes + * @param {any[][]} iterables + */ $izip_longest(iterables) { if (!iterables[0]) return []; @@ -248,7 +283,11 @@ class ElasticTabstopsLite { return expandedSet; } - // an even more (naive) Python port + /** + * an even more (naive) Python port + * @param {string | any[]} widths + * @param {string | any[]} tabs + */ $izip(widths, tabs) { // grab the shorter size var size = widths.length >= tabs.length ? tabs.length : widths.length; @@ -268,6 +307,10 @@ exports.ElasticTabstopsLite = ElasticTabstopsLite; var Editor = require("../editor").Editor; require("../config").defineOptions(Editor.prototype, "editor", { useElasticTabstops: { + /** + * @param {boolean} val + * @this {Editor} + */ set: function(val) { if (val) { if (!this.elasticTabstops) diff --git a/src/ext/emmet.js b/src/ext/emmet.js index ba2ad441fc8..073e3baf31e 100644 --- a/src/ext/emmet.js +++ b/src/ext/emmet.js @@ -11,11 +11,14 @@ var emmet, emmetPath; */ class AceEmmetEditor { + /** + * @param {Editor} editor + */ setupContext(editor) { this.ace = editor; this.indentation = editor.session.getTabString(); if (!emmet) - emmet = window.emmet; + emmet = window["emmet"]; var resources = emmet.resources || emmet.require("resources"); resources.setVariable("indentation", this.indentation); this.$syntax = null; @@ -163,6 +166,7 @@ class AceEmmetEditor { var syntax = this.ace.session.$modeId.split("/").pop(); if (syntax == "html" || syntax == "php") { var cursor = this.ace.getCursorPosition(); + /**@type {string | string[]} */ var state = this.ace.session.getState(cursor.row); if (typeof state != "string") state = state[0]; @@ -233,6 +237,9 @@ class AceEmmetEditor { // tabstops as ${0}, so we have upgrade all caret tabstops with unique // positions but make sure that all other tabstops are not linked accidentally // based on https://github.com/sergeche/emmet-sublime/blob/master/editor.js#L119-L171 + /** + * @param {string} value + */ $updateTabstops(value) { var base = 1000; var zeroBase = 0; @@ -315,6 +322,10 @@ var keymap = { var editorProxy = new AceEmmetEditor(); exports.commands = new HashHandler(); +/** + * @param {Editor} editor + * @return {number|boolean} + */ exports.runEmmetCommand = function runEmmetCommand(editor) { if (this.action == "expand_abbreviation_with_tab") { if (!editor.selection.isEmpty()) @@ -360,6 +371,10 @@ for (var command in keymap) { }); } +/** + * @param {Editor} editor + * @param {boolean} [enabled] + */ exports.updateCommands = function(editor, enabled) { if (enabled) { editor.keyBinding.addKeyboardHandler(exports.commands); @@ -375,6 +390,11 @@ exports.isSupportedMode = function(mode) { return /css|less|scss|sass|stylus|html|php|twig|ejs|handlebars/.test(id); }; +/** + * @param {Editor} editor + * @param {string} command + * @return {boolean} + */ exports.isAvailable = function(editor, command) { if (/(evaluate_math_expression|expand_abbreviation)$/.test(command)) return true; diff --git a/src/ext/error_marker.js b/src/ext/error_marker.js index afdae823878..0fb5705bfe3 100644 --- a/src/ext/error_marker.js +++ b/src/ext/error_marker.js @@ -23,6 +23,11 @@ function binarySearch(array, needle, comparator) { return -(first + 1); } +/** + * @param {import("../edit_session").EditSession} session + * @param {number} row + * @param {number} dir + */ function findAnnotations(session, row, dir) { var annotations = session.getAnnotations().sort(Range.comparePoints); if (!annotations.length) @@ -59,6 +64,10 @@ function findAnnotations(session, row, dir) { return matched.length && matched; } +/** + * @param {import("../editor").Editor} editor + * @param {number} dir + */ exports.showErrorMarker = function(editor, dir) { var session = editor.session; if (!session.widgetManager) { @@ -127,6 +136,7 @@ exports.showErrorMarker = function(editor, dir) { w.destroy = function() { if (editor.$mouseHandler.isMousePressed) return; + // @ts-ignore editor.keyBinding.removeKeyboardHandler(kb); session.widgetManager.removeLineWidget(w); editor.off("changeSelection", w.destroy); @@ -134,7 +144,8 @@ exports.showErrorMarker = function(editor, dir) { editor.off("mouseup", w.destroy); editor.off("change", w.destroy); }; - + + // @ts-ignore editor.keyBinding.addKeyboardHandler(kb); editor.on("changeSelection", w.destroy); editor.on("changeSession", w.destroy); diff --git a/src/ext/hardwrap.js b/src/ext/hardwrap.js index d54340d2fea..cc545ac98be 100644 --- a/src/ext/hardwrap.js +++ b/src/ext/hardwrap.js @@ -2,6 +2,10 @@ var Range = require("../range").Range; +/** + * @param {import("../editor").Editor} editor + * @param {import("../../ace-internal").Ace.HardWrapOptions} options + */ function hardWrap(editor, options) { var max = options.column || editor.getOption("printMarginColumn"); var allowMerge = options.allowMerge != false; @@ -41,6 +45,11 @@ function hardWrap(editor, options) { row++; } + /** + * @param {string} line + * @param {number} max + * @param {number} min + */ function findSpace(line, max, min) { if (line.length < max) return; diff --git a/src/ext/inline_autocomplete.js b/src/ext/inline_autocomplete.js index 127d717c06b..67edac3c7fd 100644 --- a/src/ext/inline_autocomplete.js +++ b/src/ext/inline_autocomplete.js @@ -25,6 +25,9 @@ var destroyCompleter = function(e, editor) { * There is an inline ghost text renderer and an optional command bar tooltip inside. */ class InlineAutocomplete { + /** + * @param {Editor} editor + */ constructor(editor) { this.editor = editor; this.keyboardHandler = new HashHandler(this.commands); @@ -38,13 +41,20 @@ class InlineAutocomplete { this.updateCompletions(); }.bind(this)); } - + + /** + * + * @return {AceInline} + */ getInlineRenderer() { if (!this.inlineRenderer) this.inlineRenderer = new AceInline(); return this.inlineRenderer; } + /** + * @return {CommandBarTooltip} + */ getInlineTooltip() { if (!this.inlineTooltip) { this.inlineTooltip = InlineAutocomplete.createInlineTooltip(document.body || document.documentElement); @@ -55,7 +65,7 @@ class InlineAutocomplete { /** * This function is the entry point to the class. This triggers the gathering of the autocompletion and displaying the results; - * @param {CompletionOptions} options + * @param {import("../../ace-internal").Ace.CompletionOptions} options */ show(options) { this.activated = true; @@ -110,6 +120,9 @@ class InlineAutocomplete { this.detach(); } + /** + * @param {import("../../ace-internal").Ace.InlineAutocompleteAction} where + */ goTo(where) { if (!this.completions || !this.completions.filtered) { return; @@ -138,6 +151,10 @@ class InlineAutocomplete { return this.completions.filtered.length; } + /** + * @param {number} [index] + * @returns {import("../../ace-internal").Ace.Completion | undefined} + */ getData(index) { if (index == undefined || index === null) { return this.completions.filtered[this.$index]; @@ -154,6 +171,9 @@ class InlineAutocomplete { return this.$index >= 0; } + /** + * @param {number} value + */ setIndex(value) { if (!this.completions || !this.completions.filtered) { return; @@ -165,6 +185,9 @@ class InlineAutocomplete { } } + /** + * @return {CompletionProvider} + */ getCompletionProvider(initialPosition) { if (!this.completionProvider) this.completionProvider = new CompletionProvider(initialPosition); @@ -181,6 +204,9 @@ class InlineAutocomplete { } } + /** + * @return {any} + */ $updatePrefix() { var pos = this.editor.getCursorPosition(); var prefix = this.editor.session.getTextRange({start: this.base, end: pos}); @@ -191,10 +217,14 @@ class InlineAutocomplete { && this.completions.filtered[0].value == prefix && !this.completions.filtered[0].snippet) return this.detach(); + //@ts-expect-error TODO: potential wrong arguments this.$open(this.editor, prefix); return prefix; } + /** + * @param {import("../../ace-internal").Ace.CompletionOptions} [options] + */ updateCompletions(options) { var prefix = ""; @@ -203,6 +233,7 @@ class InlineAutocomplete { this.base = this.editor.session.doc.createAnchor(pos.row, pos.column); this.base.$insertRight = true; this.completions = new FilteredList(options.matches); + //@ts-expect-error TODO: potential wrong arguments return this.$open(this.editor, ""); } @@ -215,6 +246,8 @@ class InlineAutocomplete { var prefix = util.getCompletionPrefix(this.editor); this.base = session.doc.createAnchor(pos.row, pos.column - prefix.length); this.base.$insertRight = true; + + // @ts-ignore var options = { exactMatch: true, ignoreCaption: true @@ -223,22 +256,28 @@ class InlineAutocomplete { prefix, base: this.base, pos - }).provideCompletions(this.editor, options, function(err, completions, finished) { - var filtered = completions.filtered; - var prefix = util.getCompletionPrefix(this.editor); - - if (finished) { - // No results - if (!filtered.length) - return this.detach(); - - // One result equals to the prefix - if (filtered.length == 1 && filtered[0].value == prefix && !filtered[0].snippet) - return this.detach(); - } - this.completions = completions; - this.$open(this.editor, prefix); - }.bind(this)); + // @ts-ignore + }).provideCompletions(this.editor, options, + /** + * @this {InlineAutocomplete} + */ + function(err, completions, finished) { + var filtered = completions.filtered; + var prefix = util.getCompletionPrefix(this.editor); + + if (finished) { + // No results + if (!filtered.length) + return this.detach(); + + // One result equals to the prefix + if (filtered.length == 1 && filtered[0].value == prefix && !filtered[0].snippet) + return this.detach(); + } + this.completions = completions; + //@ts-expect-error TODO: potential wrong arguments + this.$open(this.editor, prefix); + }.bind(this)); } detach() { @@ -281,8 +320,15 @@ class InlineAutocomplete { this.inlineTooltip = this.editor = this.inlineRenderer = null; } + updateDocTooltip(){ + } + } +/** + * + * @type {{[key: string]: import("../../ace-internal").Ace.Command}} + */ InlineAutocomplete.prototype.commands = { "Previous": { bindKey: "Alt-[", @@ -302,7 +348,7 @@ InlineAutocomplete.prototype.commands = { bindKey: { win: "Tab|Ctrl-Right", mac: "Tab|Cmd-Right" }, name: "Accept", exec: function(editor) { - return editor.completer.insertMatch(); + return /**@type{InlineAutocomplete}*/(editor.completer).insertMatch(); } }, "Close": { @@ -342,6 +388,10 @@ var completers = [snippetCompleter, textCompleter, keyWordCompleter]; require("../config").defineOptions(Editor.prototype, "editor", { enableInlineAutocompletion: { + /** + * @this{Editor} + * @param val + */ set: function(val) { if (val) { if (!this.completers) @@ -362,8 +412,10 @@ require("../config").defineOptions(Editor.prototype, "editor", { * @returns {CommandBarTooltip} The command bar tooltip for inline autocomplete */ InlineAutocomplete.createInlineTooltip = function(parentEl) { + /**@type {CommandBarTooltip}*/ var inlineTooltip = new CommandBarTooltip(parentEl); - inlineTooltip.registerCommand("Previous", + inlineTooltip.registerCommand("Previous", + // @ts-expect-error Object.assign({}, InlineAutocomplete.prototype.commands["Previous"], { enabled: true, type: "button", @@ -372,20 +424,24 @@ InlineAutocomplete.createInlineTooltip = function(parentEl) { ); inlineTooltip.registerCommand("Position", { enabled: false, - getValue: function(editor) { - return editor ? [editor.completer.getIndex() + 1, editor.completer.getLength()].join("/") : ""; + getValue: function (editor) { + return editor ? [/**@type{InlineAutocomplete}*/ + (editor.completer).getIndex() + 1, /**@type{InlineAutocomplete}*/(editor.completer).getLength() + ].join("/") : ""; }, type: "text", cssClass: "completion_position" }); - inlineTooltip.registerCommand("Next", + inlineTooltip.registerCommand("Next", + // @ts-expect-error Object.assign({}, InlineAutocomplete.prototype.commands["Next"], { enabled: true, type: "button", iconCssClass: "ace_arrow" }) ); - inlineTooltip.registerCommand("Accept", + inlineTooltip.registerCommand("Accept", + // @ts-expect-error Object.assign({}, InlineAutocomplete.prototype.commands["Accept"], { enabled: function(editor) { return !!editor && editor.completer.getIndex() >= 0; diff --git a/src/ext/keybinding_menu.js b/src/ext/keybinding_menu.js index f13f46c9b4d..26ebbefa601 100644 --- a/src/ext/keybinding_menu.js +++ b/src/ext/keybinding_menu.js @@ -10,42 +10,57 @@ * ☭ Hial Atropa!! ☭ */ - "use strict"; - var Editor = require("../editor").Editor; - /** - * Generates a menu which displays the keyboard shortcuts. - * @author - * Matthew Christopher Kastor-Inare III
- * ☭ Hial Atropa!! ☭ - * @param {ace.Editor} editor An instance of the ace editor. - */ - function showKeyboardShortcuts (editor) { - // make sure the menu isn't open already. - if(!document.getElementById('kbshortcutmenu')) { - var overlayPage = require('./menu_tools/overlay_page').overlayPage; - var getEditorKeybordShortcuts = require('./menu_tools/get_editor_keyboard_shortcuts').getEditorKeybordShortcuts; - var kb = getEditorKeybordShortcuts(editor); - var el = document.createElement('div'); - var commands = kb.reduce(function(previous, current) { - return previous + '
' - + current.command + ' : ' - + '' + current.key + '
'; - }, ''); +"use strict"; - el.id = 'kbshortcutmenu'; - el.innerHTML = '

Keyboard Shortcuts

' + commands + ''; - overlayPage(editor, el); - } +var Editor = require("../editor").Editor; + +/** + * Generates a menu which displays the keyboard shortcuts. + * @author + * Matthew Christopher Kastor-Inare III
+ * ☭ Hial Atropa!! ☭ + * @param {Editor} editor An instance of the ace editor. + */ +function showKeyboardShortcuts(editor) { + // make sure the menu isn't open already. + if (!document.getElementById('kbshortcutmenu')) { + var overlayPage = require('./menu_tools/overlay_page').overlayPage; + var getEditorKeybordShortcuts = require('./menu_tools/get_editor_keyboard_shortcuts').getEditorKeybordShortcuts; + var kb = getEditorKeybordShortcuts(editor); + var el = document.createElement('div'); + var commands = kb.reduce(function (previous, current) { + return previous + '
' + + current.command + ' : ' + + '' + current.key + '
'; + }, ''); + + el.id = 'kbshortcutmenu'; + el.innerHTML = '

Keyboard Shortcuts

' + commands + ''; + overlayPage(editor, el); } - module.exports.init = function(editor) { - Editor.prototype.showKeyboardShortcuts = function() { - showKeyboardShortcuts(this); - }; - editor.commands.addCommands([{ - name: "showKeyboardShortcuts", - bindKey: {win: "Ctrl-Alt-h", mac: "Command-Alt-h"}, - exec: function(editor, line) { - editor.showKeyboardShortcuts(); - } - }]); +} + +/** + * @param {Editor} editor + */ +module.exports.init = function (editor) { + Editor.prototype.showKeyboardShortcuts = function () { + showKeyboardShortcuts(this); }; + editor.commands.addCommands([{ + name: "showKeyboardShortcuts", + bindKey: { + win: "Ctrl-Alt-h", + mac: "Command-Alt-h" + }, + exec: + /** + * + * @param {Editor} editor + * @param [line] + */ + function (editor, line) { + editor.showKeyboardShortcuts(); + } + }]); +}; diff --git a/src/ext/language_tools.js b/src/ext/language_tools.js index 5aeb2f3c11b..28ffe73d34e 100644 --- a/src/ext/language_tools.js +++ b/src/ext/language_tools.js @@ -1,5 +1,5 @@ "use strict"; - +/**@type{import("../snippets").snippetManager & {files?: {[key: string]: any}}}*/ var snippetManager = require("../snippets").snippetManager; var Autocomplete = require("../autocomplete").Autocomplete; var config = require("../config"); @@ -7,6 +7,7 @@ var lang = require("../lib/lang"); var util = require("../autocomplete/util"); var textCompleter = require("../autocomplete/text_completer"); +/**@type {import("../../ace-internal").Ace.Completer}*/ var keyWordCompleter = { getCompletions: function(editor, session, pos, prefix, callback) { if (session.$mode.completer) { @@ -31,7 +32,7 @@ var transformSnippetTooltip = function(str) { return record[p1]; }); }; - +/**@type {import("../../ace-internal").Ace.Completer} */ var snippetCompleter = { getCompletions: function(editor, session, pos, prefix, callback) { var scopes = []; @@ -173,6 +174,10 @@ var showLiveAutocomplete = function(e) { var Editor = require("../editor").Editor; require("../config").defineOptions(Editor.prototype, "editor", { enableBasicAutocompletion: { + /** + * @param val + * @this{Editor} + */ set: function(val) { if (val) { if (!this.completers) @@ -190,6 +195,7 @@ require("../config").defineOptions(Editor.prototype, "editor", { enableLiveAutocompletion: { /** * @param {boolean} val + * @this {Editor} */ set: function(val) { if (val) { diff --git a/src/ext/menu_tools/get_editor_keyboard_shortcuts.js b/src/ext/menu_tools/get_editor_keyboard_shortcuts.js index 1f3d05c94ed..c87320e61fc 100644 --- a/src/ext/menu_tools/get_editor_keyboard_shortcuts.js +++ b/src/ext/menu_tools/get_editor_keyboard_shortcuts.js @@ -11,14 +11,15 @@ */ "use strict"; -var keys = require("../../lib/keys"); + +/** @type{any} */var keys = require("../../lib/keys"); /** * Gets a map of keyboard shortcuts to command names for the current platform. * @author * Matthew Christopher Kastor-Inare III
* ☭ Hial Atropa!! ☭ - * @param {ace.Editor} editor An editor instance. + * @param {import("../../editor").Editor} editor An editor instance. * @returns {Array} Returns an array of objects representing the keyboard * shortcuts for the given editor. * @example @@ -34,7 +35,7 @@ module.exports.getEditorKeybordShortcuts = function(editor) { var keybindings = []; var commandMap = {}; editor.keyBinding.$handlers.forEach(function(handler) { - var ckb = handler.commandKeyBinding; + var ckb = handler["commandKeyBinding"]; for (var i in ckb) { var key = i.replace(/(^|-)\w/g, function(x) { return x.toUpperCase(); }); var commands = ckb[i]; diff --git a/src/ext/menu_tools/overlay_page.js b/src/ext/menu_tools/overlay_page.js index b216354bb7d..d1cb87e6c3e 100644 --- a/src/ext/menu_tools/overlay_page.js +++ b/src/ext/menu_tools/overlay_page.js @@ -22,10 +22,11 @@ dom.importCssString(cssText, "settings_menu.css", false); * @author * Matthew Christopher Kastor-Inare III
* ☭ Hial Atropa!! ☭ - * @param {Element} contentElement Any element which may be presented inside + * @param editor + * @param {HTMLElement} contentElement Any element which may be presented inside * a div. + * @param [callback] */ - module.exports.overlayPage = function overlayPage(editor, contentElement, callback) { var closer = document.createElement('div'); var ignoreFocusOut = false; diff --git a/src/ext/modelist.js b/src/ext/modelist.js index 44194f941f7..eb4137d497d 100644 --- a/src/ext/modelist.js +++ b/src/ext/modelist.js @@ -4,7 +4,7 @@ var modes = []; /** * Suggests a mode based on the file extension present in the given path * @param {string} path The path to the file - * @returns {object} Returns an object containing information about the + * @returns {Mode} Returns an object containing information about the * suggested mode. */ function getModeForPath(path) { @@ -20,6 +20,11 @@ function getModeForPath(path) { } class Mode { + /** + * @param {string} name + * @param {string} caption + * @param {string} extensions + */ constructor(name, caption, extensions) { this.name = name; this.caption = caption; @@ -38,6 +43,9 @@ class Mode { this.extRe = new RegExp(re, "gi"); } + /** + * @param {string} filename + */ supportsFile(filename) { return filename.match(this.extRe); } diff --git a/src/ext/options.js b/src/ext/options.js index f82b82dcc5e..f30f83f2af9 100644 --- a/src/ext/options.js +++ b/src/ext/options.js @@ -1,4 +1,7 @@ "use strict"; +/** + * @typedef {import("../editor").Editor} Editor + */ require("./menu_tools/overlay_page"); @@ -212,6 +215,11 @@ var optionGroups = { }; class OptionPanel { + /** + * + * @param {Editor} editor + * @param {HTMLElement} [element] + */ constructor(editor, element) { this.editor = editor; this.container = element || document.createElement("div"); @@ -225,7 +233,8 @@ class OptionPanel { if (config.More) oop.mixin(optionGroups.More, config.More); } - + + render() { this.container.innerHTML = ""; buildDom(["table", {role: "presentation", id: "controls"}, @@ -253,7 +262,11 @@ class OptionPanel { return this.renderOption(item.label, item); }, this); } - + + /** + * @param {string} key + * @param {Object} option + */ renderOptionControl(key, option) { var self = this; if (Array.isArray(option)) { @@ -261,6 +274,7 @@ class OptionPanel { return self.renderOptionControl(key, x); }); } + /**@type {any}*/ var control; var value = self.getOption(option); @@ -339,7 +353,12 @@ class OptionPanel { } return control; } - + + /** + * + * @param key + * @param option + */ renderOption(key, option) { if (option.path && !option.onchange && !this.editor.$options[option.path]) return; @@ -352,7 +371,11 @@ class OptionPanel { ["label", {for: safeKey, id: safeId}, key] ], ["td", control]]; } - + + /** + * @param {string | number | Object} option + * @param {string | number | boolean} value + */ setOption(option, value) { if (typeof option == "string") option = this.options[option]; diff --git a/src/ext/prompt.js b/src/ext/prompt.js index bb38277c236..54ba7d8bfae 100644 --- a/src/ext/prompt.js +++ b/src/ext/prompt.js @@ -1,24 +1,7 @@ /** - * Prompt plugin is used for getting input from user. - * - * @param {Object} editor Ouside editor related to this prompt. Will be blurred when prompt is open. - * @param {String} message Predefined value of prompt input box. - * @param {Object} options Cusomizable options for this prompt. - * @param {String} options.name Prompt name. - * @param {String} options.$type Use prompt of specific type (gotoLine|commands|modes or default if empty). - * @param {[start, end]} options.selection Defines which part of the predefined value should be highlited. - * @param {Boolean} options.hasDescription Set to true if prompt has description below input box. - * @param {String} options.prompt Description below input box. - * @param {String} options.placeholder Placeholder for value. - * @param {Object} options.$rules Specific rules for input like password or regexp. - * @param {Boolean} options.ignoreFocusOut Set to true to keep the prompt open when focus moves to another part of the editor. - * @param {Function} options.getCompletions Function for defining list of options for value. - * @param {Function} options.getPrefix Function for defining current value prefix. - * @param {Function} options.onAccept Function called when Enter is pressed. - * @param {Function} options.onInput Function called when input is added to prompt input box. - * @param {Function} options.onCancel Function called when Esc|Shift-Esc is pressed. - * @param {Function} callback Function called after done. - * */ + * @typedef {import("../editor").Editor} Editor + */ + "use strict"; @@ -34,8 +17,37 @@ var overlayPage = require("./menu_tools/overlay_page").overlayPage; var modelist = require("./modelist"); var openPrompt; +/** + * @typedef PromptOptions + * @property {String} name Prompt name. + * @property {String} $type Use prompt of specific type (gotoLine|commands|modes or default if empty). + * @property {[start: number, end: number]} selection Defines which part of the predefined value should be highlited. + * @property {Boolean} hasDescription Set to true if prompt has description below input box. + * @property {String} prompt Description below input box. + * @property {String} placeholder Placeholder for value. + * @property {Object} $rules Specific rules for input like password or regexp. + * @property {Boolean} ignoreFocusOut Set to true to keep the prompt open when focus moves to another part of the editor. + * @property {Function} getCompletions Function for defining list of options for value. + * @property {Function} getPrefix Function for defining current value prefix. + * @property {Function} onAccept Function called when Enter is pressed. + * @property {Function} onInput Function called when input is added to prompt input box. + * @property {Function} onCancel Function called when Esc|Shift-Esc is pressed. + * @property {Function} history Function for defining history list. + * @property {number} maxHistoryCount + * @property {Function} addToHistory + */ + +/** + * Prompt plugin is used for getting input from user. + * + * @param {Editor} editor Ouside editor related to this prompt. Will be blurred when prompt is open. + * @param {String | Partial} message Predefined value of prompt input box. + * @param {Partial} options Cusomizable options for this prompt. + * @param {Function} [callback] Function called after done. + * */ function prompt(editor, message, options, callback) { if (typeof message == "object") { + // @ts-ignore return prompt(editor, "", message, options); } if (openPrompt) { @@ -51,6 +63,7 @@ function prompt(editor, message, options, callback) { var cmdLine = $singleLineEditor(); cmdLine.session.setUndoManager(new UndoManager()); + /**@type {any}*/ var el = dom.buildDom(["div", {class: "ace_prompt_container" + (options.hasDescription ? " input-box-with-description" : "")}]); var overlay = overlayPage(editor, el, done); el.appendChild(cmdLine.container); @@ -82,8 +95,8 @@ function prompt(editor, message, options, callback) { popup.setRow(-1); popup.on("click", function(e) { var data = popup.getData(popup.getRow()); - if (!data.error) { - cmdLine.setValue(data.value || data.name || data); + if (!data["error"]) { + cmdLine.setValue(data.value || data["name"] || data); accept(); e.stop(); } @@ -102,6 +115,7 @@ function prompt(editor, message, options, callback) { } if (options.hasDescription) { + /**@type {any}*/ var promptTextContainer = dom.buildDom(["div", {class: "ace_prompt_text_container"}]); dom.buildDom(options.prompt || "Press 'Enter' to confirm or 'Escape' to cancel", promptTextContainer); el.appendChild(promptTextContainer); @@ -117,7 +131,7 @@ function prompt(editor, message, options, callback) { val = cmdLine.getValue(); } var curData = popup ? popup.getData(popup.getRow()) : val; - if (curData && !curData.error) { + if (curData && !curData["error"]) { done(); options.onAccept && options.onAccept({ value: val, @@ -176,7 +190,7 @@ function prompt(editor, message, options, callback) { function valueFromRecentList() { var current = popup.getData(popup.getRow()); - if (current && !current.error) + if (current && !current["error"]) return current.value || current.caption || current; } @@ -193,6 +207,11 @@ function prompt(editor, message, options, callback) { }; } +/** + * + * @param {Editor} editor + * @param {Function} [callback] + */ prompt.gotoLine = function(editor, callback) { function stringifySelection(selection) { if (!Array.isArray(selection)) @@ -218,9 +237,9 @@ prompt.gotoLine = function(editor, callback) { selection: [1, Number.MAX_VALUE], onAccept: function(data) { var value = data.value; - var _history = prompt.gotoLine._history; + var _history = prompt.gotoLine["_history"]; if (!_history) - prompt.gotoLine._history = _history = []; + prompt.gotoLine["_history"] = _history = []; if (_history.indexOf(value) != -1) _history.splice(_history.indexOf(value), 1); _history.unshift(value); @@ -275,9 +294,9 @@ prompt.gotoLine = function(editor, callback) { editor.renderer.animateScrolling(scrollTop); }, history: function() { - if (!prompt.gotoLine._history) + if (!prompt.gotoLine["_history"]) return []; - return prompt.gotoLine._history; + return prompt.gotoLine["_history"]; }, getCompletions: function(cmdLine) { @@ -300,6 +319,11 @@ prompt.gotoLine = function(editor, callback) { }); }; +/** + * + * @param {Editor} editor + * @param {Function} [callback] + */ prompt.commands = function(editor, callback) { function normalizeName(name) { return (name || "").replace(/^./, function(x) { @@ -312,8 +336,8 @@ prompt.commands = function(editor, callback) { var commandsByName = []; var commandMap = {}; editor.keyBinding.$handlers.forEach(function(handler) { - var platform = handler.platform; - var cbn = handler.byName; + var platform = handler["platform"]; + var cbn = handler["byName"]; for (var i in cbn) { var key = cbn[i].bindKey; if (typeof key !== "string") { @@ -373,10 +397,10 @@ prompt.commands = function(editor, callback) { if (this.maxHistoryCount > 0 && history.length > this.maxHistoryCount) { history.splice(history.length - 1, 1); } - prompt.commands.history = history; + prompt.commands["history"] = history; }, history: function() { - return prompt.commands.history || []; + return prompt.commands["history"] || []; }, getPrefix: function(cmdLine) { var currentPos = cmdLine.getCursorPosition(); @@ -430,7 +454,13 @@ prompt.commands = function(editor, callback) { }); }; +/** + * + * @param {Editor} editor + * @param {Function} [callback] + */ prompt.modes = function(editor, callback) { + /**@type {any[]}*/ var modesArray = modelist.modes; modesArray = modesArray.map(function(item) { return {value: item.caption, mode: item.name}; diff --git a/src/ext/rtl.js b/src/ext/rtl.js index 4be16acf43a..7c8e1f60edb 100644 --- a/src/ext/rtl.js +++ b/src/ext/rtl.js @@ -66,7 +66,9 @@ require("../config").defineOptions(Editor.prototype, "editor", { * - ensures RLE mark removal on line merge, when 'delete' is pressed and cursor stays * at last position of previous line and when 'backspace' is pressed and cursor stays at * first position of current line. This is achived by hacking range boundaries on 'remove' operation. - **/ + * @param {any} e + * @param {Editor} editor + */ function onChangeSelection(e, editor) { var lead = editor.getSelection().lead; if (editor.session.$bidiHandler.isRtlLine(lead.row)) { @@ -91,7 +93,9 @@ function onCommandEmitted(commadEvent) { * Whenever the document is changed make sure that line break operatin * on right-to-left line (like pressing Enter or pasting multi-line text) * produces new right-to-left lines - **/ + * @param {import("../../ace-internal").Ace.Delta} delta + * @param {Editor} editor + */ function onChange(delta, editor) { var session = editor.session; session.$bidiHandler.currentRow = null; @@ -103,6 +107,10 @@ function onChange(delta, editor) { } } +/** + * @param {any} e + * @param {import("../virtual_renderer").VirtualRenderer} renderer + */ function updateLineDirection(e, renderer) { var session = renderer.session; var $bidiHandler = session.$bidiHandler; @@ -122,6 +130,9 @@ function updateLineDirection(e, renderer) { }); } +/** + * @param {import("../virtual_renderer").VirtualRenderer} renderer + */ function clearTextLayer(renderer) { var lines = renderer.$textLayer.$lines; lines.cells.forEach(clear); diff --git a/src/ext/searchbox.js b/src/ext/searchbox.js index 741768ac4be..cfb1c08e3fa 100644 --- a/src/ext/searchbox.js +++ b/src/ext/searchbox.js @@ -1,5 +1,7 @@ "use strict"; - +/** + * @typedef {import("../editor").Editor} Editor + */ var dom = require("../lib/dom"); var lang = require("../lib/lang"); var event = require("../lib/event"); @@ -13,7 +15,14 @@ var MAX_COUNT = 999; dom.importCssString(searchboxCss, "ace_searchbox", false); class SearchBox { + /** + * @param {Editor} editor + * @param {undefined} [range] + * @param {undefined} [showReplaceForm] + */ constructor(editor, range, showReplaceForm) { + /**@type {any}*/ + this.activeInput; var div = dom.createElement("div"); dom.buildDom(["div", {class:"ace_search right"}, ["span", {action: "hide", class: "ace_searchbtn_close"}], @@ -38,6 +47,7 @@ class SearchBox { ["span", {action: "searchInSelection", class: "ace_button", title: nls("Search In Selection")}, "S"] ] ], div); + /**@type {any}*/ this.element = div.firstChild; this.setSession = this.setSession.bind(this); @@ -46,10 +56,14 @@ class SearchBox { this.setEditor(editor); dom.importCssString(searchboxCss, "ace_searchbox", editor.container); } - + + /** + * @param {Editor} editor + */ setEditor(editor) { editor.searchBox = this; editor.renderer.scroller.appendChild(this.element); + /**@type {Editor}*/ this.editor = editor; } @@ -58,16 +72,29 @@ class SearchBox { this.$syncOptions(true); } + /** + * @param {HTMLElement} sb + */ $initElements(sb) { + /**@type {HTMLElement}*/ this.searchBox = sb.querySelector(".ace_search_form"); + /**@type {HTMLElement}*/ this.replaceBox = sb.querySelector(".ace_replace_form"); + /**@type {HTMLInputElement}*/ this.searchOption = sb.querySelector("[action=searchInSelection]"); + /**@type {HTMLInputElement}*/ this.replaceOption = sb.querySelector("[action=toggleReplace]"); + /**@type {HTMLInputElement}*/ this.regExpOption = sb.querySelector("[action=toggleRegexpMode]"); + /**@type {HTMLInputElement}*/ this.caseSensitiveOption = sb.querySelector("[action=toggleCaseSensitive]"); + /**@type {HTMLInputElement}*/ this.wholeWordOption = sb.querySelector("[action=toggleWholeWords]"); + /**@type {HTMLInputElement}*/ this.searchInput = this.searchBox.querySelector(".ace_search_field"); + /**@type {HTMLInputElement}*/ this.replaceInput = this.replaceBox.querySelector(".ace_search_field"); + /**@type {HTMLElement}*/ this.searchCounter = sb.querySelector(".ace_search_counter"); } @@ -118,7 +145,7 @@ class SearchBox { _this.searchInput.value && _this.highlight(); }); } - + setSearchRange(range) { this.searchRange = range; if (range) { @@ -129,6 +156,9 @@ class SearchBox { } } + /** + * @param {boolean} [preventScroll] + */ $syncOptions(preventScroll) { dom.setCssClass(this.replaceOption, "checked", this.searchRange); dom.setCssClass(this.searchOption, "checked", this.searchOption.checked); @@ -142,11 +172,19 @@ class SearchBox { this.find(false, false, preventScroll); } + /** + * @param {RegExp} [re] + */ highlight(re) { this.editor.session.highlight(re || this.editor.$search.$options.re); this.editor.renderer.updateBackMarkers(); } - + + /** + * @param {boolean} skipCurrent + * @param {boolean} backwards + * @param {any} [preventScroll] + */ find(skipCurrent, backwards, preventScroll) { var range = this.editor.find(this.searchInput.value, { skipCurrent: skipCurrent, @@ -158,6 +196,7 @@ class SearchBox { preventScroll: preventScroll, range: this.searchRange }); + /**@type {any}*/ var noMatch = !range && this.searchInput.value; dom.setCssClass(this.searchBox, "ace_nomatch", noMatch); this.editor._emit("findSearchBox", { match: !noMatch }); @@ -209,6 +248,7 @@ class SearchBox { caseSensitive: this.caseSensitiveOption.checked, wholeWord: this.wholeWordOption.checked }); + /**@type {any}*/ var noMatch = !range && this.searchInput.value; dom.setCssClass(this.searchBox, "ace_nomatch", noMatch); this.editor._emit("findSearchBox", { match: !noMatch }); @@ -239,6 +279,11 @@ class SearchBox { this.editor.keyBinding.removeKeyboardHandler(this.$closeSearchBarKb); this.editor.focus(); } + + /** + * @param {string} value + * @param {boolean} [isReplace] + */ show(value, isReplace) { this.active = true; this.editor.on("changeSession", this.setSession); @@ -358,6 +403,11 @@ SearchBox.prototype.$closeSearchBarKb = $closeSearchBarKb; exports.SearchBox = SearchBox; +/** + * + * @param {Editor} editor + * @param {boolean} [isReplace] + */ exports.Search = function(editor, isReplace) { var sb = editor.searchBox || new SearchBox(editor); sb.show(editor.session.getTextRange(), isReplace); diff --git a/src/ext/settings_menu.js b/src/ext/settings_menu.js index 495997f0a52..f6e600f551f 100644 --- a/src/ext/settings_menu.js +++ b/src/ext/settings_menu.js @@ -14,12 +14,13 @@ "use strict"; var OptionPanel = require("./options").OptionPanel; var overlayPage = require('./menu_tools/overlay_page').overlayPage; + /** * This displays the settings menu if it is not already being shown. * @author * Matthew Christopher Kastor-Inare III
* ☭ Hial Atropa!! ☭ - * @param {ace.Editor} editor An instance of the ace editor. + * @param {import("../editor").Editor} editor An instance of the ace editor. */ function showSettingsMenu(editor) { // show if the menu isn't open already. @@ -28,6 +29,7 @@ function showSettingsMenu(editor) { options.render(); options.container.id = "ace_settingsmenu"; overlayPage(editor, options.container); + // @ts-ignore options.container.querySelector("select,input,button,checkbox").focus(); } } @@ -36,7 +38,6 @@ function showSettingsMenu(editor) { * Initializes the settings menu extension. It adds the showSettingsMenu * method to the given editor object and adds the showSettingsMenu command * to the editor with appropriate keyboard shortcuts. - * @param {ace.Editor} editor An instance of the Editor. */ module.exports.init = function() { var Editor = require("../editor").Editor; diff --git a/src/ext/simple_tokenizer.js b/src/ext/simple_tokenizer.js index 04a384762a6..c5e88f64e6b 100644 --- a/src/ext/simple_tokenizer.js +++ b/src/ext/simple_tokenizer.js @@ -15,7 +15,7 @@ class SimpleTokenizer { /** * @param {number} row - * @returns {import("../../ace").Ace.Token[]} + * @returns {import("../../ace-internal").Ace.Token[]} */ getTokens(row) { const line = this._lines[row]; @@ -40,8 +40,8 @@ class SimpleTokenizer { * Result is a list of list of tokens, where each line from the provided content is a separate list of tokens. * * @param {string} content to tokenize - * @param {import("ace-code").Ace.HighlightRules} highlightRules defining the language grammar - * @returns {import("ace-code/src/ext/simple_tokenizer").TokenizeResult} tokenization result containing a list of token for each of the lines from content + * @param {import("../../ace-internal").Ace.HighlightRules} highlightRules defining the language grammar + * @returns {import("../../ace-internal").Ace.TokenizeResult} tokenization result containing a list of token for each of the lines from content */ function tokenize(content, highlightRules) { const tokenizer = new SimpleTokenizer(content, new Tokenizer(highlightRules.getRules())); @@ -59,4 +59,4 @@ function tokenize(content, highlightRules) { module.exports = { tokenize -}; \ No newline at end of file +}; diff --git a/src/ext/static_highlight.js b/src/ext/static_highlight.js index 7b326478b03..427f626a8d4 100644 --- a/src/ext/static_highlight.js +++ b/src/ext/static_highlight.js @@ -8,7 +8,11 @@ var dom = require("../lib/dom"); var escapeHTML = require("../lib/lang").escapeHTML; class Element { + /** + * @param {string} type + */ constructor(type) { + /** @type{string} */this.className; this.type = type; this.style = {}; this.textContent = ""; @@ -51,10 +55,10 @@ class Element { var simpleDom = { - createTextNode: function(textContent, element) { + createTextNode: function(/** @type {string} */ textContent, /** @type {any} */ element) { return escapeHTML(textContent); }, - createElement: function(type) { + createElement: function(/** @type {string} */ type) { return new Element(type); }, createFragment: function() { @@ -63,12 +67,20 @@ var simpleDom = { }; +/**@type {any}*/ var SimpleTextLayer = function() { this.config = {}; this.dom = simpleDom; }; SimpleTextLayer.prototype = TextLayer.prototype; +/** + * + * @param {HTMLElement} el + * @param opts + * @param [callback] + * @returns {boolean} + */ var highlight = function(el, opts, callback) { var m = el.className.match(/lang-(\w+)/); var mode = opts.mode || m && ("ace/mode/" + m[1]); @@ -82,6 +94,7 @@ var highlight = function(el, opts, callback) { if (el.firstElementChild) { var textLen = 0; for (var i = 0; i < el.childNodes.length; i++) { + /**@type {any}*/ var ch = el.childNodes[i]; if (ch.nodeType == 3) { textLen += ch.data.length; @@ -99,6 +112,10 @@ var highlight = function(el, opts, callback) { highlight.render(data, mode, theme, opts.firstLineNumber, !opts.showGutter, function (highlighted) { dom.importCssString(highlighted.css, "ace_highlight", true); el.innerHTML = highlighted.html; + /** + * TODO: check if child exists + * @type {any} + */ var container = el.firstChild.firstChild; for (var i = 0; i < nodes.length; i += 2) { var pos = highlighted.session.doc.indexToPosition(nodes[i]); @@ -114,16 +131,16 @@ var highlight = function(el, opts, callback) { * Transforms a given input code snippet into HTML using the given mode * * @param {string} input Code snippet - * @param {string|mode} mode String specifying the mode to load such as + * @param {string|import("../../ace-internal").Ace.SyntaxMode} mode String specifying the mode to load such as * `ace/mode/javascript` or, a mode loaded from `/ace/mode` * (use 'ServerSideHiglighter.getMode'). - * @param {string|theme} theme String specifying the theme to load such as + * @param {string} theme String specifying the theme to load such as * `ace/theme/twilight` or, a theme loaded from `/ace/theme`. * @param {number} lineStart A number indicating the first line number. Defaults * to 1. * @param {boolean} disableGutter Specifies whether or not to disable the gutter. * `true` disables the gutter, `false` enables the gutter. Defaults to `false`. - * @param {function} callback When specifying the mode or theme as a string, + * @param {function} [callback] When specifying the mode or theme as a string, * this method has no return value and you must specify a callback function. The * callback will receive the rendered object containing the properties `html` * and `css`. @@ -151,9 +168,9 @@ highlight.render = function(input, mode, theme, lineStart, disableGutter, callba if (typeof mode == "string") { waiting++; config.loadModule(['mode', mode], function(m) { - if (!modeCache[mode] || modeOptions) - modeCache[mode] = new m.Mode(modeOptions); - mode = modeCache[mode]; + if (!modeCache[/**@type{string}*/(mode)] || modeOptions) + modeCache[/**@type{string}*/(mode)] = new m.Mode(modeOptions); + mode = modeCache[/**@type{string}*/(mode)]; --waiting || done(); }); } @@ -169,8 +186,10 @@ highlight.render = function(input, mode, theme, lineStart, disableGutter, callba /** * Transforms a given input code snippet into HTML using the given mode * @param {string} input Code snippet - * @param {mode} mode Mode loaded from /ace/mode (use 'ServerSideHiglighter.getMode') - * @param {string} r Code snippet + * @param {import("../../ace-internal").Ace.SyntaxMode|string} mode Mode loaded from /ace/mode (use 'ServerSideHiglighter.getMode') + * @param {any} theme + * @param {any} lineStart + * @param {boolean} disableGutter * @returns {object} An object containing: html, css */ highlight.renderSync = function(input, mode, theme, lineStart, disableGutter) { @@ -180,6 +199,7 @@ highlight.renderSync = function(input, mode, theme, lineStart, disableGutter) { session.setUseWorker(false); session.setMode(mode); + /**@type {TextLayer}*/ var textLayer = new SimpleTextLayer(); textLayer.setSession(session); Object.keys(textLayer.$tabStrings).forEach(function(k) { diff --git a/src/ext/statusbar.js b/src/ext/statusbar.js index ece9d0fede2..a9e649b018a 100644 --- a/src/ext/statusbar.js +++ b/src/ext/statusbar.js @@ -1,10 +1,17 @@ "use strict"; - +/** + * + * @typedef {import("../editor").Editor} Editor + */ var dom = require("../lib/dom"); var lang = require("../lib/lang"); /** simple statusbar **/ class StatusBar{ + /** + * @param {Editor} editor + * @param {HTMLElement} parentNode + */ constructor(editor, parentNode) { this.element = dom.createElement("div"); this.element.className = "ace_status-indicator"; @@ -19,13 +26,16 @@ class StatusBar{ editor.on("changeSelection", statusUpdate); editor.on("keyboardActivity", statusUpdate); } - + + /** + * @param {Editor} editor + */ updateStatus(editor) { var status = []; function add(str, separator) { str && status.push(str, separator || "|"); } - + // @ts-expect-error TODO: potential wrong argument add(editor.keyBinding.getStatusText(editor)); if (editor.commands.recording) add("REC"); diff --git a/src/ext/textarea.js b/src/ext/textarea.js index 86bff86e88d..29dca121c32 100644 --- a/src/ext/textarea.js +++ b/src/ext/textarea.js @@ -6,7 +6,7 @@ var ace = require("../ace"); module.exports = exports = ace; -/* +/** * Returns the CSS property of element. * 1) If the CSS property is on the style object of the element, use it, OR * 2) Compute the CSS property @@ -14,6 +14,9 @@ module.exports = exports = ace; * If the property can't get computed, is 'auto' or 'intrinsic', the former * calculated property is used (this can happen in cases where the textarea * is hidden and has no dimension styles). + * @param {HTMLElement} element + * @param {HTMLElement} container + * @param {string} property */ var getCSSProperty = function(element, container, property) { var ret = element.style[property]; @@ -22,6 +25,7 @@ var getCSSProperty = function(element, container, property) { if (window.getComputedStyle) { ret = window.getComputedStyle(element, '').getPropertyValue(property); } else { + // @ts-ignore ret = element.currentStyle[property]; } } @@ -32,6 +36,10 @@ var getCSSProperty = function(element, container, property) { return ret; }; +/** + * @param {HTMLElement} elm + * @param {Object} styles + */ function applyStyles(elm, styles) { for (var style in styles) { elm.style[style] = styles[style]; @@ -216,7 +224,7 @@ exports.transformTextarea = function(element, options) { editor.setDisplaySettings(); return; } - container.style.zIndex = 100000; + container.style.zIndex = "100000"; var rect = container.getBoundingClientRect(); var startX = rect.width + rect.left - e.clientX; var startY = rect.height + rect.top - e.clientY; diff --git a/src/ext/whitespace.js b/src/ext/whitespace.js index 90e3b21e836..3a42fcde289 100644 --- a/src/ext/whitespace.js +++ b/src/ext/whitespace.js @@ -1,8 +1,19 @@ "use strict"; +/** + * + * @typedef {import("../edit_session").EditSession} EditSession + */ + var lang = require("../lib/lang"); // based on http://www.freehackers.org/Indent_Finder +/** + * + * @param {string[]} lines + * @param [fallback] + * @returns {{ch?: string, length?: number}} + */ exports.$detectIndentation = function(lines, fallback) { var stats = []; var changes = []; @@ -74,6 +85,10 @@ exports.$detectIndentation = function(lines, fallback) { return {ch: " ", length: tabLength}; }; +/** + * @param {EditSession} session + * @returns {{ch?: string, length?: number}|{}} + */ exports.detectIndentation = function(session) { var lines = session.getLines(0, 1000); var indent = exports.$detectIndentation(lines) || {}; @@ -87,9 +102,10 @@ exports.detectIndentation = function(session) { }; /** - * EditSession session - * options.trimEmpty trim empty lines too - * options.keepCursorPosition do not trim whitespace before the cursor + * @param {EditSession} session + * @param {Object} options + * @param {boolean} [options.trimEmpty] trim empty lines too + * @param {boolean} [options.keepCursorPosition] do not trim whitespace before the cursor */ exports.trimTrailingSpace = function(session, options) { var doc = session.getDocument(); @@ -128,6 +144,11 @@ exports.trimTrailingSpace = function(session, options) { } }; +/** + * @param {EditSession} session + * @param {string} ch + * @param {number} len + */ exports.convertIndentation = function(session, ch, len) { var oldCh = session.getTabString()[0]; var oldLen = session.getTabSize(); @@ -161,6 +182,11 @@ exports.convertIndentation = function(session, ch, len) { session.setUseSoftTabs(ch == " "); }; +/** + * + * @param {string} text + * @returns {{}} + */ exports.$parseStringArg = function(text) { var indent = {}; if (/t/.test(text)) diff --git a/src/incremental_search.js b/src/incremental_search.js index f9b5e428abb..58ba6725414 100644 --- a/src/incremental_search.js +++ b/src/incremental_search.js @@ -12,6 +12,9 @@ function isRegExp(obj) { return obj instanceof RegExp; } +/** + * @param {RegExp} re + */ function regExpToObject(re) { var string = String(re), start = string.indexOf('/'), @@ -22,6 +25,11 @@ function regExpToObject(re) { }; } +/** + * @param {string} string + * @param {string} flags + * @return {RegExp|string} + */ function stringToRegExp(string, flags) { try { return new RegExp(string, flags); @@ -49,7 +57,10 @@ class IncrementalSearch extends Search { this.$options = {wrap: false, skipCurrent: false}; this.$keyboardHandler = new ISearchKbd(this); } - + + /** + * @param {boolean} backwards + */ activate(editor, backwards) { this.$editor = editor; this.$startPos = this.$currentPos = editor.getCursorPosition(); @@ -64,6 +75,9 @@ class IncrementalSearch extends Search { this.statusMessage(true); } + /** + * @param {boolean} [reset] + */ deactivate(reset) { this.cancelSearch(reset); var editor = this.$editor; @@ -76,6 +90,9 @@ class IncrementalSearch extends Search { this.message(''); } + /** + * @param {Editor} editor + */ selectionFix(editor) { // Fix selection bug: When clicked inside the editor // editor.selection.$isEmpty is false even if the mouse click did not @@ -87,6 +104,9 @@ class IncrementalSearch extends Search { } } + /** + * @param {RegExp} regexp + */ highlight(regexp) { var sess = this.$editor.session, hl = sess.$isearchHighlight = sess.$isearchHighlight || sess.addDynamicMarker( @@ -95,6 +115,9 @@ class IncrementalSearch extends Search { sess._emit("changeBackMarker"); // force highlight layer redraw } + /** + * @param {boolean} [reset] + */ cancelSearch(reset) { var e = this.$editor; this.$prevNeedle = this.$options.needle; @@ -109,6 +132,10 @@ class IncrementalSearch extends Search { return Range.fromPoints(this.$currentPos, this.$currentPos); } + /** + * @param {boolean} moveToNext + * @param {Function} needleUpdateFunc + */ highlightAndFindWithNeedle(moveToNext, needleUpdateFunc) { if (!this.$editor) return null; var options = this.$options; @@ -141,6 +168,9 @@ class IncrementalSearch extends Search { return found; } + /** + * @param {string} s + */ addString(s) { return this.highlightAndFindWithNeedle(false, function(needle) { if (!isRegExp(needle)) @@ -151,6 +181,9 @@ class IncrementalSearch extends Search { }); } + /** + * @param {any} c + */ removeChar(c) { return this.highlightAndFindWithNeedle(false, function(needle) { if (!isRegExp(needle)) @@ -180,6 +213,9 @@ class IncrementalSearch extends Search { return true; } + /** + * @param {string} text + */ onPaste(text) { this.addString(text); } diff --git a/src/keyboard/emacs_test.js b/src/keyboard/emacs_test.js index 9cfbb21be58..18bcd3c2b10 100644 --- a/src/keyboard/emacs_test.js +++ b/src/keyboard/emacs_test.js @@ -8,7 +8,7 @@ if (typeof process !== "undefined") { require("../multi_select"); var EditSession = require("./../edit_session").EditSession, - Editor = require("./../editor").Editor, + Editor = require("../editor").Editor, Range = require("./../range").Range, MockRenderer = require("./../test/mockrenderer").MockRenderer, emacs = require('./emacs'), diff --git a/src/keyboard/gutter_handler.js b/src/keyboard/gutter_handler.js index cc7d6ce8225..df94dee9da5 100644 --- a/src/keyboard/gutter_handler.js +++ b/src/keyboard/gutter_handler.js @@ -50,40 +50,42 @@ class GutterKeyboardHandler { this.editor.scrollToLine(row, true, true); // After scrolling is completed, find the nearest gutter icon and set focus to it. - setTimeout(function() { - var index = this.$rowToRowIndex(this.gutterLayer.$cursorCell.row); - var nearestFoldIndex = this.$findNearestFoldWidget(index); - var nearestAnnotationIndex = this.$findNearestAnnotation(index); - - if (nearestFoldIndex === null && nearestAnnotationIndex === null) - return; - - if (nearestFoldIndex === null && nearestAnnotationIndex !== null){ - this.activeRowIndex = nearestAnnotationIndex; - this.activeLane = "annotation"; - this.$focusAnnotation(this.activeRowIndex); - return; - } - - if (nearestFoldIndex !== null && nearestAnnotationIndex === null){ - this.activeRowIndex = nearestFoldIndex; - this.activeLane = "fold"; - this.$focusFoldWidget(this.activeRowIndex); - return; - } - - if (Math.abs(nearestAnnotationIndex - index) < Math.abs(nearestFoldIndex - index)){ - this.activeRowIndex = nearestAnnotationIndex; - this.activeLane = "annotation"; - this.$focusAnnotation(this.activeRowIndex); - return; - } else { - this.activeRowIndex = nearestFoldIndex; - this.activeLane = "fold"; - this.$focusFoldWidget(this.activeRowIndex); - return; - } - }.bind(this), 10); + setTimeout( + /** @this {GutterKeyboardHandler} */ + function () { + var index = this.$rowToRowIndex(this.gutterLayer.$cursorCell.row); + var nearestFoldIndex = this.$findNearestFoldWidget(index); + var nearestAnnotationIndex = this.$findNearestAnnotation(index); + + if (nearestFoldIndex === null && nearestAnnotationIndex === null) return; + + if (nearestFoldIndex === null && nearestAnnotationIndex !== null) { + this.activeRowIndex = nearestAnnotationIndex; + this.activeLane = "annotation"; + this.$focusAnnotation(this.activeRowIndex); + return; + } + + if (nearestFoldIndex !== null && nearestAnnotationIndex === null) { + this.activeRowIndex = nearestFoldIndex; + this.activeLane = "fold"; + this.$focusFoldWidget(this.activeRowIndex); + return; + } + + if (Math.abs(nearestAnnotationIndex - index) < Math.abs(nearestFoldIndex - index)) { + this.activeRowIndex = nearestAnnotationIndex; + this.activeLane = "annotation"; + this.$focusAnnotation(this.activeRowIndex); + return; + } + else { + this.activeRowIndex = nearestFoldIndex; + this.activeLane = "fold"; + this.$focusFoldWidget(this.activeRowIndex); + return; + } + }.bind(this), 10); return; } @@ -168,12 +170,14 @@ class GutterKeyboardHandler { // After folding, check that the right fold widget is still in focus. // If not (e.g. folding close to bottom of doc), put right widget in focus. - setTimeout(function() { - if (this.$rowIndexToRow(this.activeRowIndex) !== rowFoldingWidget){ - this.$blurFoldWidget(this.activeRowIndex); - this.activeRowIndex = this.$rowToRowIndex(rowFoldingWidget); - this.$focusFoldWidget(this.activeRowIndex); - } + setTimeout( + /** @this {GutterKeyboardHandler} */ + function () { + if (this.$rowIndexToRow(this.activeRowIndex) !== rowFoldingWidget) { + this.$blurFoldWidget(this.activeRowIndex); + this.activeRowIndex = this.$rowToRowIndex(rowFoldingWidget); + this.$focusFoldWidget(this.activeRowIndex); + } }.bind(this), 10); break; @@ -481,4 +485,4 @@ class GutterKeyboardEvent { } } -exports.GutterKeyboardEvent = GutterKeyboardEvent; \ No newline at end of file +exports.GutterKeyboardEvent = GutterKeyboardEvent; diff --git a/src/keyboard/hash_handler.js b/src/keyboard/hash_handler.js index f5be98745fe..d445f5f2924 100644 --- a/src/keyboard/hash_handler.js +++ b/src/keyboard/hash_handler.js @@ -1,22 +1,40 @@ "use strict"; -var keyUtil = require("../lib/keys"); +/** + * @typedef {import("../../ace-internal").Ace.Command} Command + * @typedef {import("../../ace-internal").Ace.CommandLike} CommandLike +*/ + +/** @type {any} */var keyUtil = require("../lib/keys"); var useragent = require("../lib/useragent"); var KEY_MODS = keyUtil.KEY_MODS; class MultiHashHandler { + /** + * @param {Record | Command[]} [config] + * @param {string} [platform] + */ constructor(config, platform) { this.$init(config, platform, false); } + /** + * @param {Record | Command[]} config + * @param {string} [platform] + * @param {boolean} [$singleCommand] + */ $init(config, platform, $singleCommand) { this.platform = platform || (useragent.isMac ? "mac" : "win"); + /**@type {Record}*/ this.commands = {}; this.commandKeyBinding = {}; this.addCommands(config); this.$singleCommand = $singleCommand; } + /** + * @param {Command} command + */ addCommand(command) { if (this.commands[command.name]) this.removeCommand(command); @@ -27,6 +45,10 @@ class MultiHashHandler { this._buildKeyHash(command); } + /** + * @param {Command | string} command + * @param {boolean} [keepCommand] + */ removeCommand(command, keepCommand) { var name = command && (typeof command === 'string' ? command : command.name); command = this.commands[name]; @@ -51,6 +73,11 @@ class MultiHashHandler { } } + /** + * @param {string | { win?: string; mac?: string; position?:number}} key + * @param {CommandLike | string} command + * @param {number} [position] + */ bindKey(key, command, position) { if (typeof key == "object" && key) { if (position == undefined) @@ -60,9 +87,9 @@ class MultiHashHandler { if (!key) return; if (typeof command == "function") - return this.addCommand({exec: command, bindKey: key, name: command.name || key}); + return this.addCommand({exec: command, bindKey: key, name: command.name || /**@type{string}*/(key)}); - key.split("|").forEach(function(keyPart) { + /**@type{string}*/(key).split("|").forEach(function(keyPart) { var chain = ""; if (keyPart.indexOf(" ") != -1) { var parts = keyPart.split(/\s+/); @@ -80,7 +107,12 @@ class MultiHashHandler { this._addCommandToBinding(chain + id, command, position); }, this); } - + + /** + * @param {string} keyId + * @param {any} command + * @param {number} position + */ _addCommandToBinding(keyId, command, position) { var ckb = this.commandKeyBinding, i; if (!command) { @@ -109,6 +141,9 @@ class MultiHashHandler { } } + /** + * @param {Record | Command[]} [commands] + */ addCommands(commands) { commands && Object.keys(commands).forEach(function(name) { var command = commands[name]; @@ -131,12 +166,18 @@ class MultiHashHandler { }, this); } + /** + * @param {Record} commands + */ removeCommands(commands) { Object.keys(commands).forEach(function(name) { this.removeCommand(commands[name]); }, this); } + /** + * @param {Record} keyList + */ bindKeys(keyList) { Object.keys(keyList).forEach(function(key) { this.bindKey(key, keyList[key]); @@ -147,8 +188,12 @@ class MultiHashHandler { this.bindKey(command.bindKey, command); } - // accepts keys in the form ctrl+Enter or ctrl-Enter - // keys without modifiers or shift only + /** + * Accepts keys in the form ctrl+Enter or ctrl-Enter + * keys without modifiers or shift only + * @param {string} keys + * @returns {{key: string, hashId: number} | false} + */ parseKeys(keys) { var parts = keys.toLowerCase().split(/[\-\+]([\-\+])?/).filter(function(x){return x;}); var key = parts.pop(); @@ -174,11 +219,23 @@ class MultiHashHandler { return {key: key, hashId: hashId}; } + /** + * @param {number} hashId + * @param {string} keyString + * @returns {Command} + */ findKeyCommand(hashId, keyString) { var key = KEY_MODS[hashId] + keyString; return this.commandKeyBinding[key]; } + /** + * @param {{ $keyChain: string | any[]; }} data + * @param {number} hashId + * @param {string} keyString + * @param {number} keyCode + * @returns {{command: string} | void} + */ handleKeyboard(data, hashId, keyString, keyCode) { if (keyCode < 0) return; var key = KEY_MODS[hashId] + keyString; @@ -203,7 +260,12 @@ class MultiHashHandler { } return {command: command}; } - + + /** + * @param {any} [editor] + * @param {any} [data] + * @returns {string} + */ getStatusText(editor, data) { return data.$keyChain || ""; } @@ -217,6 +279,10 @@ function getPosition(command) { } class HashHandler extends MultiHashHandler { + /** + * @param {Record | Command[]} [config] + * @param {string} [platform] + */ constructor(config, platform) { super(config, platform); this.$singleCommand = true; diff --git a/src/keyboard/keybinding.js b/src/keyboard/keybinding.js index 70c2897f5cc..5de3ab8251c 100644 --- a/src/keyboard/keybinding.js +++ b/src/keyboard/keybinding.js @@ -1,23 +1,36 @@ "use strict"; - +/** + * @typedef {import("../editor").Editor} Editor + * @typedef {import("../../ace-internal").Ace.KeyboardHandler} KeyboardHandler + */ var keyUtil = require("../lib/keys"); var event = require("../lib/event"); class KeyBinding { + /** + * @param {Editor} editor + */ constructor(editor) { this.$editor = editor; this.$data = {editor: editor}; + /**@type {(KeyboardHandler)[]}*/ this.$handlers = []; this.setDefaultHandler(editor.commands); } - + + /** + * @param {KeyboardHandler} kb + */ setDefaultHandler(kb) { this.removeKeyboardHandler(this.$defaultHandler); this.$defaultHandler = kb; this.addKeyboardHandler(kb, 0); } + /** + * @param {KeyboardHandler} kb + */ setKeyboardHandler(kb) { var h = this.$handlers; if (h[h.length - 1] == kb) @@ -29,10 +42,16 @@ class KeyBinding { this.addKeyboardHandler(kb, 1); } + /** + * @param {KeyboardHandler & {attach?: (editor: any) => void, detach?: (editor: any) => void;}} [kb] + * @param {number} [pos] + */ addKeyboardHandler(kb, pos) { if (!kb) return; + // @ts-ignore if (typeof kb == "function" && !kb.handleKeyboard) + // @ts-ignore kb.handleKeyboard = kb; var i = this.$handlers.indexOf(kb); if (i != -1) @@ -47,6 +66,10 @@ class KeyBinding { kb.attach(this.$editor); } + /** + * @param {KeyboardHandler & {attach?: (editor: any) => void, detach?: (editor: any) => void;}} kb + * @returns {boolean} + */ removeKeyboardHandler(kb) { var i = this.$handlers.indexOf(kb); if (i == -1) @@ -56,6 +79,9 @@ class KeyBinding { return true; } + /** + * @return {KeyboardHandler} + */ getKeyboardHandler() { return this.$handlers[this.$handlers.length - 1]; } @@ -75,6 +101,7 @@ class KeyBinding { for (var i = this.$handlers.length; i--;) { toExecute = this.$handlers[i].handleKeyboard( + // @ts-expect-error TODO: could be wrong arguments amount this.$data, hashId, keyString, keyCode, e ); if (!toExecute || !toExecute.command) @@ -84,11 +111,12 @@ class KeyBinding { if (toExecute.command == "null") { success = true; } else { + // @ts-expect-error //TODO: potential wrong arguments amount success = commands.exec(toExecute.command, this.$editor, toExecute.args, e); } // do not stop input events to not break repeating if (success && e && hashId != -1 && - toExecute.passEvent != true && toExecute.command.passEvent != true + toExecute["passEvent"] != true && toExecute.command["passEvent"] != true ) { event.stopEvent(e); } @@ -107,11 +135,21 @@ class KeyBinding { return success; } + /** + * @param {any} e + * @param {number} hashId + * @param {number} keyCode + * @return {boolean} + */ onCommandKey(e, hashId, keyCode) { var keyString = keyUtil.keyCodeToString(keyCode); return this.$callKeyboardHandlers(hashId, keyString, keyCode, e); } + /** + * @param {string} text + * @return {boolean} + */ onTextInput(text) { return this.$callKeyboardHandlers(-1, text); } diff --git a/src/keyboard/keybinding_test.js b/src/keyboard/keybinding_test.js index 36a8bdcfe7c..fb1114bff36 100644 --- a/src/keyboard/keybinding_test.js +++ b/src/keyboard/keybinding_test.js @@ -5,7 +5,7 @@ if (typeof process !== "undefined") { "use strict"; var EditSession = require("./../edit_session").EditSession, - Editor = require("./../editor").Editor, + Editor = require("../editor").Editor, MockRenderer = require("./../test/mockrenderer").MockRenderer, assert = require("./../test/assertions"), HashHandler = require('./hash_handler').HashHandler, diff --git a/src/keyboard/sublime_test.js b/src/keyboard/sublime_test.js index 0f3a694525c..9971586735c 100644 --- a/src/keyboard/sublime_test.js +++ b/src/keyboard/sublime_test.js @@ -8,7 +8,7 @@ if (typeof process !== "undefined") { require("../multi_select"); var EditSession = require("./../edit_session").EditSession; -var Editor = require("./../editor").Editor; +var Editor = require("../editor").Editor; var Range = require("./../range").Range; var MockRenderer = require("./../test/mockrenderer").MockRenderer; var assert = require("./../test/assertions"); diff --git a/src/keyboard/textarea.js b/src/keyboard/textarea.js index 88936fd6c84..433f5ed093a 100644 --- a/src/keyboard/textarea.js +++ b/src/keyboard/textarea.js @@ -52,4 +52,4 @@ exports.handler = new HashHandler(); bindKey = bindKey[exports.handler.platform]; exports.handler.bindKey(bindKey, k.command); }); -exports.handler.$id = "ace/keyboard/textarea"; +exports.handler["$id"] = "ace/keyboard/textarea"; diff --git a/src/keyboard/textinput.js b/src/keyboard/textinput.js index 0dfe81dc654..9c04c22e64f 100644 --- a/src/keyboard/textinput.js +++ b/src/keyboard/textinput.js @@ -11,26 +11,33 @@ var USE_IE_MIME_TYPE = useragent.isIE; var HAS_FOCUS_ARGS = useragent.isChrome > 63; var MAX_LINE_LENGTH = 400; +/** + * + * @type {{[key: string]: any}} + */ var KEYS = require("../lib/keys"); var MODS = KEYS.KEY_MODS; var isIOS = useragent.isIOS; var valueResetRegex = isIOS ? /\s/ : /\n/; var isMobile = useragent.isMobile; -var TextInput = function(parentNode, host) { +var TextInput; +TextInput= function(parentNode, host) { + /**@type {HTMLTextAreaElement & {msGetInputContext?: () => {compositionStartOffset: number}, getInputContext?: () => {compositionStartOffset: number}}}*/ var text = dom.createElement("textarea"); text.className = "ace_text-input"; text.setAttribute("wrap", "off"); text.setAttribute("autocorrect", "off"); text.setAttribute("autocapitalize", "off"); - text.setAttribute("spellcheck", false); + text.setAttribute("spellcheck", "false"); text.style.opacity = "0"; parentNode.insertBefore(text, parentNode.firstChild); var copied = false; var pasted = false; + /**@type {false | {[key: string]: any}}} */ var inComposition = false; var sendingText = false; var tempStyle = ''; @@ -111,6 +118,10 @@ var TextInput = function(parentNode, host) { else resetSelection(); }, host); + /** + * + * @type {boolean | string} + */ this.$focusScroll = false; this.focus = function() { // On focusing on the textarea, read active row number to assistive tech. @@ -135,9 +146,9 @@ var TextInput = function(parentNode, host) { var t = text.parentElement; while (t && t.nodeType == 1) { ancestors.push(t); - t.setAttribute("ace_nocontext", true); + t.setAttribute("ace_nocontext", "true"); if (!t.parentElement && t.getRootNode) - t = t.getRootNode().host; + t = t.getRootNode()["host"]; else t = t.parentElement; } @@ -211,8 +222,8 @@ var TextInput = function(parentNode, host) { // modifying selection of blured textarea can focus it (chrome mac/linux) if (!isFocused && !afterContextMenu) return; - // this prevents infinite recursion on safari 8 // see https://github.com/ajaxorg/ace/issues/2114 + // @ts-expect-error this prevents infinite recursion on safari 8 inComposition = true; var selectionStart = 0; @@ -431,7 +442,7 @@ var TextInput = function(parentNode, host) { }; var handleClipboardData = function(e, data, forceIEMime) { - var clipboardData = e.clipboardData || window.clipboardData; + var clipboardData = e.clipboardData || window["clipboardData"]; if (!clipboardData || BROKEN_SETDATA) return; // using "Text" doesn't work on old webkit but ie needs it diff --git a/src/keyboard/vim_test.js b/src/keyboard/vim_test.js index c877cd50029..fe357a16aa5 100644 --- a/src/keyboard/vim_test.js +++ b/src/keyboard/vim_test.js @@ -5,7 +5,7 @@ if (typeof process !== "undefined") { var EditSession = require("./../edit_session").EditSession; -var Editor = require("./../editor").Editor; +var Editor = require("../editor").Editor; var UndoManager = require("./../undomanager").UndoManager; var MockRenderer = require("./../test/mockrenderer").MockRenderer; var JavaScriptMode = require("./../mode/javascript").Mode; diff --git a/src/layer/cursor.js b/src/layer/cursor.js index 3b369cdce38..873f8022949 100644 --- a/src/layer/cursor.js +++ b/src/layer/cursor.js @@ -1,9 +1,14 @@ "use strict"; - +/** + * @typedef {import("../edit_session").EditSession} EditSession + */ var dom = require("../lib/dom"); class Cursor { + /** + * @param {HTMLElement} parentEl + */ constructor(parentEl) { this.element = dom.createElement("div"); this.element.className = "ace_layer ace_cursor-layer"; @@ -19,7 +24,10 @@ class Cursor { dom.addCssClass(this.element, "ace_hidden-cursors"); this.$updateCursors = this.$updateOpacity.bind(this); } - + + /** + * @param {boolean} [val] + */ $updateOpacity(val) { var cursors = this.cursors; for (var i = cursors.length; i--; ) @@ -43,15 +51,24 @@ class Cursor { this.$isAnimating = false; dom.removeCssClass(this.element, "ace_animate-blinking"); } - + + /** + * @param {number} padding + */ setPadding(padding) { this.$padding = padding; } + /** + * @param {EditSession} session + */ setSession(session) { this.session = session; } + /** + * @param {boolean} blinking + */ setBlinking(blinking) { if (blinking != this.isBlinking) { this.isBlinking = blinking; @@ -59,6 +76,9 @@ class Cursor { } } + /** + * @param {number} blinkInterval + */ setBlinkInterval(blinkInterval) { if (blinkInterval != this.blinkInterval) { this.blinkInterval = blinkInterval; @@ -66,6 +86,9 @@ class Cursor { } } + /** + * @param {boolean} smoothBlinking + */ setSmoothBlinking(smoothBlinking) { if (smoothBlinking != this.smoothBlinking) { this.smoothBlinking = smoothBlinking; @@ -133,7 +156,7 @@ class Cursor { if (dom.HAS_CSS_ANIMATION) { this.$startCssAnimation(); } else { - var blink = function(){ + var blink = /**@this{Cursor}*/function(){ this.timeoutId = setTimeout(function() { update(false); }, 0.6 * this.blinkInterval); @@ -147,6 +170,10 @@ class Cursor { } } + /** + * @param {import("../../ace-internal").Ace.Point} [position] + * @param {boolean} [onScreen] + */ getPixelPosition(position, onScreen) { if (!this.config || !this.session) return {left : 0, top : 0}; @@ -212,6 +239,9 @@ class Cursor { this.restartTimer(); } + /** + * @param {boolean} overwrite + */ $setOverwrite(overwrite) { if (overwrite != this.overwrite) { this.overwrite = overwrite; diff --git a/src/layer/font_metrics.js b/src/layer/font_metrics.js index adb95c0480c..3ffafed5942 100644 --- a/src/layer/font_metrics.js +++ b/src/layer/font_metrics.js @@ -10,7 +10,10 @@ var USE_OBSERVER = typeof ResizeObserver == "function"; var L = 200; class FontMetrics { - + + /** + * @param {HTMLElement} parentEl + */ constructor(parentEl) { this.el = dom.createElement("div"); this.$setMeasureNodeStyles(this.el.style, true); @@ -52,6 +55,9 @@ class FontMetrics { style.overflow = isRoot ? "hidden" : "visible"; } + /** + * @param size + */ checkForSizeChanges(size) { if (size === undefined) size = this.$measureSizes(); @@ -65,7 +71,7 @@ class FontMetrics { this._emit("changeCharacterSize", {data: size}); } } - + $addObserver() { var self = this; this.$observer = new window.ResizeObserver(function(e) { @@ -75,6 +81,9 @@ class FontMetrics { this.$observer.observe(this.$measureNode); } + /** + * @return {number} + */ $pollSizeChanges() { if (this.$pollSizeChangesTimer || this.$observer) return this.$pollSizeChangesTimer; @@ -85,7 +94,10 @@ class FontMetrics { event.onIdle(cb, 500); }, 500); } - + + /** + * @param {boolean} val + */ setPolling(val) { if (val) { this.$pollSizeChanges(); @@ -133,7 +145,7 @@ class FontMetrics { $getZoom(element) { if (!element || !element.parentElement) return 1; - return (window.getComputedStyle(element).zoom || 1) * this.$getZoom(element.parentElement); + return (window.getComputedStyle(element)["zoom"] || 1) * this.$getZoom(element.parentElement); } $initTransformMeasureNodes() { diff --git a/src/layer/gutter.js b/src/layer/gutter.js index 8e3772dbf65..4b87b803bf1 100644 --- a/src/layer/gutter.js +++ b/src/layer/gutter.js @@ -1,5 +1,8 @@ "use strict"; - +/** + * @typedef {import("../edit_session").EditSession} EditSession + * @typedef {import("../../ace-internal").Ace.LayerConfig} LayerConfig + */ var dom = require("../lib/dom"); var oop = require("../lib/oop"); var lang = require("../lib/lang"); @@ -8,6 +11,9 @@ var Lines = require("./lines").Lines; var nls = require("../config").nls; class Gutter{ + /** + * @param {HTMLElement} parentEl + */ constructor(parentEl) { this.element = dom.createElement("div"); this.element.className = "ace_layer ace_gutter-layer"; @@ -23,6 +29,9 @@ class Gutter{ this.$lines.$offsetCoefficient = 1; } + /** + * @param {EditSession} session + */ setSession(session) { if (this.session) this.session.off("change", this.$updateAnnotations); @@ -31,18 +40,29 @@ class Gutter{ session.on("change", this.$updateAnnotations); } + /** + * @param {number} row + * @param {string} className + */ addGutterDecoration(row, className) { if (window.console) console.warn && console.warn("deprecated use session.addGutterDecoration"); this.session.addGutterDecoration(row, className); } + /** + * @param {number} row + * @param {string} className + */ removeGutterDecoration(row, className) { if (window.console) console.warn && console.warn("deprecated use session.removeGutterDecoration"); this.session.removeGutterDecoration(row, className); } + /** + * @param {any[]} annotations + */ setAnnotations(annotations) { // iterate over sparse array this.$annotations = []; @@ -74,6 +94,9 @@ class Gutter{ } } + /** + * @param {import("../../ace-internal").Ace.Delta} delta + */ $updateAnnotations(delta) { if (!this.$annotations.length) return; @@ -90,6 +113,9 @@ class Gutter{ } } + /** + * @param {LayerConfig} config + */ update(config) { this.config = config; @@ -140,6 +166,9 @@ class Gutter{ this.$updateGutterWidth(config); } + /** + * @param {LayerConfig} config + */ $updateGutterWidth(config) { var session = this.session; @@ -159,7 +188,7 @@ class Gutter{ gutterWidth += padding.left + padding.right; if (gutterWidth !== this.gutterWidth && !isNaN(gutterWidth)) { this.gutterWidth = gutterWidth; - this.element.parentNode.style.width = + /**@type{any}*/(this.element.parentNode).style.width = this.element.style.width = Math.ceil(this.gutterWidth) + "px"; this._signal("changeGutterWidth", gutterWidth); } @@ -204,7 +233,10 @@ class Gutter{ } } } - + + /** + * @param {LayerConfig} config + */ scrollLines(config) { var oldConfig = this.config; this.config = config; @@ -248,6 +280,11 @@ class Gutter{ this.$updateGutterWidth(config); } + /** + * @param {LayerConfig} config + * @param {number} firstRow + * @param {number} lastRow + */ $renderLines(config, firstRow, lastRow) { var fragment = []; var row = firstRow; @@ -271,7 +308,14 @@ class Gutter{ } return fragment; } - + + + /** + * @param {any} cell + * @param {LayerConfig} config + * @param {import("../../ace-internal").Ace.IRange | undefined} fold + * @param {number} row + */ $renderCell(cell, config, fold, row) { var element = cell.element; @@ -443,11 +487,17 @@ class Gutter{ return cell; } - + + /** + * @param {boolean} highlightGutterLine + */ setHighlightGutterLine(highlightGutterLine) { this.$highlightGutterLine = highlightGutterLine; } - + + /** + * @param {boolean} show + */ setShowLineNumbers(show) { this.$renderer = !show && { getWidth: function() {return 0;}, @@ -458,7 +508,10 @@ class Gutter{ getShowLineNumbers() { return this.$showLineNumbers; } - + + /** + * @param {boolean} [show] + */ setShowFoldWidgets(show) { if (show) dom.addCssClass(this.element, "ace_folding-enabled"); @@ -476,7 +529,7 @@ class Gutter{ $computePadding() { if (!this.element.firstChild) return {left: 0, right: 0}; - var style = dom.computedStyle(this.element.firstChild); + var style = dom.computedStyle(/**@type{Element}*/(this.element.firstChild)); this.$padding = {}; this.$padding.left = (parseInt(style.borderLeftWidth) || 0) + (parseInt(style.paddingLeft) || 0) + 1; @@ -485,6 +538,9 @@ class Gutter{ return this.$padding; } + /** + * @param {{ x: number; }} point + */ getRegion(point) { var padding = this.$padding || this.$computePadding(); var rect = this.element.getBoundingClientRect(); diff --git a/src/layer/lines.js b/src/layer/lines.js index e2f9a1e808d..ae0cb71e262 100644 --- a/src/layer/lines.js +++ b/src/layer/lines.js @@ -1,8 +1,15 @@ "use strict"; - +/** + * @typedef {import("../edit_session").EditSession} EditSession + * @typedef {import("../../ace-internal").Ace.LayerConfig} LayerConfig + */ var dom = require("../lib/dom"); class Lines { + /** + * @param {HTMLElement} element + * @param {number} [canvasHeight] + */ constructor(element, canvasHeight) { this.element = element; this.canvasHeight = canvasHeight || 500000; @@ -12,25 +19,42 @@ class Lines { this.cellCache = []; this.$offsetCoefficient = 0; } - + + /** + * @param {LayerConfig} config + */ moveContainer(config) { dom.translate(this.element, 0, -((config.firstRowScreen * config.lineHeight) % this.canvasHeight) - config.offset * this.$offsetCoefficient); - } - + } + + /** + * @param {LayerConfig} oldConfig + * @param {LayerConfig} newConfig + */ pageChanged(oldConfig, newConfig) { return ( Math.floor((oldConfig.firstRowScreen * oldConfig.lineHeight) / this.canvasHeight) !== Math.floor((newConfig.firstRowScreen * newConfig.lineHeight) / this.canvasHeight) ); } - + + /** + * @param {number} row + * @param {Partial} config + * @param {EditSession} session + */ computeLineTop(row, config, session) { var screenTop = config.firstRowScreen * config.lineHeight; var screenPage = Math.floor(screenTop / this.canvasHeight); var lineTop = session.documentToScreenRow(row, 0) * config.lineHeight; return lineTop - (screenPage * this.canvasHeight); } - + + /** + * @param {number} row + * @param {LayerConfig} config + * @param {EditSession} session + */ computeLineHeight(row, config, session) { return config.lineHeight * session.getRowLineCount(row); } @@ -38,7 +62,10 @@ class Lines { getLength() { return this.cells.length; } - + + /** + * @param {number} index + */ get(index) { return this.cells[index]; } diff --git a/src/layer/marker.js b/src/layer/marker.js index f7edb09e58a..935e1b8a4c4 100644 --- a/src/layer/marker.js +++ b/src/layer/marker.js @@ -1,28 +1,49 @@ "use strict"; - +/** + * @typedef {import("../edit_session").EditSession} EditSession + * @typedef {import("../../ace-internal").Ace.LayerConfig} LayerConfig + */ var Range = require("../range").Range; var dom = require("../lib/dom"); class Marker { + /** + * @param {HTMLElement} parentEl + */ constructor(parentEl) { this.element = dom.createElement("div"); this.element.className = "ace_layer ace_marker-layer"; parentEl.appendChild(this.element); } - + + /** + * @param {number} padding + */ setPadding(padding) { this.$padding = padding; } + + /** + * @param {EditSession} session + */ setSession(session) { this.session = session; } - + + /** + * @param {{ [x: number]: import("../../ace-internal").Ace.MarkerLike; }} markers + */ setMarkers(markers) { this.markers = markers; } - + + /** + * @param {string} className + * @param {string} css + */ elt(className, css) { + /**@type {any}*/ var x = this.i != -1 && this.element.childNodes[this.i]; if (!x) { x = document.createElement("div"); @@ -35,6 +56,9 @@ class Marker { x.className = className; } + /** + * @param {LayerConfig} config + */ update(config) { if (!config) return; @@ -77,12 +101,23 @@ class Marker { } } + /** + * @param {number} row + * @param {Partial} layerConfig + */ $getTop(row, layerConfig) { return (row - layerConfig.firstRowScreen) * layerConfig.lineHeight; } // Draws a marker, which spans a range of text on multiple lines + /** + * @param {undefined} stringBuilder + * @param {Range} range + * @param {string} clazz + * @param {Partial} layerConfig + * @param {string} [extraStyle] + */ drawTextMarker(stringBuilder, range, clazz, layerConfig, extraStyle) { var session = this.session; var start = range.start.row; @@ -107,6 +142,13 @@ class Marker { } // Draws a multi line marker, where lines span the full width + /** + * @param {undefined} stringBuilder + * @param {Range} range + * @param {string} clazz + * @param {LayerConfig} config + * @param {string} [extraStyle] + */ drawMultiLineMarker(stringBuilder, range, clazz, config, extraStyle) { // from selection start to the end of the line var padding = this.$padding; @@ -162,6 +204,14 @@ class Marker { } // Draws a marker which covers part or whole width of a single screen line + /** + * @param {undefined} stringBuilder + * @param {Range} range + * @param {string} clazz + * @param {Partial} config + * @param {number} [extraLength] + * @param {string} [extraStyle] + */ drawSingleLineMarker(stringBuilder, range, clazz, config, extraLength, extraStyle) { if (this.session.$bidiHandler.isBidiRow(range.start.row)) return this.drawBidiSingleLineMarker(stringBuilder, range, clazz, config, extraLength, extraStyle); @@ -181,6 +231,14 @@ class Marker { } // Draws Bidi marker which covers part or whole width of a single screen line + /** + * @param {undefined} stringBuilder + * @param {Range} range + * @param {string} clazz + * @param {Partial} config + * @param {number} extraLength + * @param {string} extraStyle + */ drawBidiSingleLineMarker(stringBuilder, range, clazz, config, extraLength, extraStyle) { var height = config.lineHeight, top = this.$getTop(range.start.row, config), padding = this.$padding; var selections = this.session.$bidiHandler.getSelections(range.start.column, range.end.column); @@ -196,6 +254,13 @@ class Marker { }, this); } + /** + * @param {undefined} stringBuilder + * @param {Range} range + * @param {string} clazz + * @param {Partial} config + * @param {undefined} [extraStyle] + */ drawFullLineMarker(stringBuilder, range, clazz, config, extraStyle) { var top = this.$getTop(range.start.row, config); var height = config.lineHeight; @@ -209,7 +274,14 @@ class Marker { "left:0;right:0;"+ (extraStyle || "") ); } - + + /** + * @param {undefined} stringBuilder + * @param {Range} range + * @param {string} clazz + * @param {Partial} config + * @param {undefined} [extraStyle] + */ drawScreenLineMarker(stringBuilder, range, clazz, config, extraStyle) { var top = this.$getTop(range.start.row, config); var height = config.lineHeight; diff --git a/src/layer/text.js b/src/layer/text.js index 8120411eb18..6502a387af4 100644 --- a/src/layer/text.js +++ b/src/layer/text.js @@ -1,5 +1,8 @@ "use strict"; - +/** + * + * @typedef {import("../edit_session").EditSession} EditSession + */ var oop = require("../lib/oop"); var dom = require("../lib/dom"); var lang = require("../lib/lang"); @@ -9,6 +12,9 @@ var nls = require("../config").nls; const isTextToken = require("./text_util").isTextToken; class Text { + /** + * @param {HTMLElement} parentEl + */ constructor(parentEl) { this.dom = dom; this.element = this.dom.createElement("div"); @@ -28,23 +34,39 @@ class Text { } } + /** + * @param {number} padding + */ setPadding(padding) { this.$padding = padding; this.element.style.margin = "0 " + padding + "px"; } - + + /** + * @returns {number} + */ getLineHeight() { return this.$fontMetrics.$characterSize.height || 0; } + /** + * @returns {number} + */ getCharacterWidth() { return this.$fontMetrics.$characterSize.width || 0; } + /** + * @param {any} measure + */ $setFontMetrics(measure) { this.$fontMetrics = measure; - this.$fontMetrics.on("changeCharacterSize", function(e) { - this._signal("changeCharacterSize", e); + this.$fontMetrics.on("changeCharacterSize", + /** + * @this {Text} + */ + function (e) { + this._signal("changeCharacterSize", e); }.bind(this)); this.$pollSizeChanges(); } @@ -55,12 +77,20 @@ class Text { $pollSizeChanges() { return this.$pollSizeChangesTimer = this.$fontMetrics.$pollSizeChanges(); } + + /** + * @param {EditSession} session + */ setSession(session) { + /**@type {EditSession}*/ this.session = session; if (session) this.$computeTabString(); } - + + /** + * @param {string} showInvisibles + */ setShowInvisibles(showInvisibles) { if (this.showInvisibles == showInvisibles) return false; @@ -76,7 +106,10 @@ class Text { this.$computeTabString(); return true; } - + + /** + * @param {any} display + */ setDisplayIndentGuides(display) { if (this.displayIndentGuides == display) return false; @@ -85,7 +118,10 @@ class Text { this.$computeTabString(); return true; } - + + /** + * @param {any} highlight + */ setHighlightIndentGuides(highlight) { if (this.$highlightIndentGuides === highlight) return false; @@ -96,7 +132,7 @@ class Text { $computeTabString() { var tabSize = this.session.getTabSize(); this.tabSize = tabSize; - var tabStr = this.$tabStrings = [0]; + /**@type{any}*/var tabStr = this.$tabStrings = [0]; for (var i = 1; i < tabSize + 1; i++) { if (this.showTabs) { var span = this.dom.createElement("span"); @@ -132,6 +168,11 @@ class Text { } } + /** + * @param {{ lastRow: number; firstRow: number; lineHeight: number; }} config + * @param {number} firstRow + * @param {number} lastRow + */ updateLines(config, firstRow, lastRow) { // Due to wrap line changes there can be new lines if e.g. // the line to updated wrapped in the meantime. @@ -175,7 +216,7 @@ class Text { if (row > last) break; - var lineElement = lineElements[lineElementsIdx++]; + /**@type{any}*/var lineElement = lineElements[lineElementsIdx++]; if (lineElement) { this.dom.removeChildren(lineElement); this.$renderLine( @@ -406,7 +447,7 @@ class Text { $highlightIndentGuide() { if (!this.$highlightIndentGuides || !this.displayIndentGuides) return; - + /**@type {{ indentLevel?: number; start?: number; end?: number; dir?: number; }}*/ this.$highlightIndentGuideMarker = { indentLevel: undefined, start: undefined, @@ -645,6 +686,11 @@ class Text { } } + /** + * @param {any} row + * @param {{ walk: (arg0: (placeholder: any, row: any, column: any, lastColumn: any, isNewRow: any) => void, arg1: any, arg2: any) => void; end: { row: any; }; }} foldLine + * @return {import("../../ace-internal").Ace.Token[]} + */ $getFoldLineTokens(row, foldLine) { var session = this.session; var renderTokens = []; diff --git a/src/lib/app_config.js b/src/lib/app_config.js index 0631b01484a..3ca8dfb5725 100644 --- a/src/lib/app_config.js +++ b/src/lib/app_config.js @@ -1,5 +1,4 @@ "no use strict"; - var oop = require("./oop"); var EventEmitter = require("./event_emitter").EventEmitter; const reportError = require("./report_error").reportError; @@ -29,6 +28,7 @@ var optionsProvider = { setOption: function(name, value) { if (this["$" + name] === value) return; + //@ts-ignore var opt = this.$options[name]; if (!opt) { return warn('misspelled option "' + name + '"'); @@ -57,8 +57,6 @@ function warn(message) { console.warn.apply(console, arguments); } - - var messages; class AppConfig { @@ -66,8 +64,11 @@ class AppConfig { this.$defaultOptions = {}; } - /* - * option {name, value, initialValue, setterName, set, get } + /** + * @param {Object} obj + * @param {string} path + * @param {{ [key: string]: any }} options + * @returns {AppConfig} */ defineOptions(obj, path, options) { if (!obj.$options) @@ -90,6 +91,9 @@ class AppConfig { return this; } + /** + * @param {Object} obj + */ resetOptions(obj) { Object.keys(obj.$options).forEach(function(key) { var opt = obj.$options[key]; @@ -98,6 +102,11 @@ class AppConfig { }); } + /** + * @param {string} path + * @param {string} name + * @param {any} value + */ setDefaultValue(path, name, value) { if (!path) { for (path in this.$defaultOptions) @@ -115,16 +124,27 @@ class AppConfig { } } + /** + * @param {string} path + * @param {{ [key: string]: any; }} optionHash + */ setDefaultValues(path, optionHash) { Object.keys(optionHash).forEach(function(key) { this.setDefaultValue(path, key, optionHash[key]); }, this); } - + + /** + * @param {any} value + */ setMessages(value) { messages = value; } - + + /** + * @param {string} string + * @param {{ [x: string]: any; }} [params] + */ nls(string, params) { if (messages && !messages[string]) { warn("No message found for '" + string + "' in the provided messages, falling back to default English message."); diff --git a/src/lib/bidiutil.js b/src/lib/bidiutil.js index 72af40779c9..38e318b9b1d 100644 --- a/src/lib/bidiutil.js +++ b/src/lib/bidiutil.js @@ -298,8 +298,8 @@ exports.DOT = "\xB7"; * Performs text reordering by implementing Unicode Bidi algorithm * with aim to produce logical<->visual map and Bidi levels * @param {String} text string to be reordered - * @param {Array} unicode character types produced by call to 'hasBidiCharacters' - * @param {Boolean} 'true' for right-to-left text direction, otherwise 'false' + * @param {Array} textCharTypes unicode character types produced by call to 'hasBidiCharacters' + * @param {Boolean} isRtl 'true' for right-to-left text direction, otherwise 'false' * * @return {Object} An object containing logicalFromVisual map and Bidi levels **/ @@ -347,7 +347,7 @@ exports.doBidiReorder = function(text, textCharTypes, isRtl) { /** * Performs character classification, to be used in Unicode Bidi algorithm. * @param {String} text string to be reordered - * @param {Array} unicode character types (to be filled by this method) + * @param {Array} textCharTypes unicode character types (to be filled by this method) * * @return {Boolean} 'true' if text contains Bidi characters, otherwise 'false' **/ diff --git a/src/lib/deep_copy.js b/src/lib/deep_copy.js index 897bbbdb5db..555b282454b 100644 --- a/src/lib/deep_copy.js +++ b/src/lib/deep_copy.js @@ -4,7 +4,7 @@ exports.deepCopy = function deepCopy(obj) { var copy; if (Array.isArray(obj)) { copy = []; - for (var key = 0; key < obj.length; key++) { + for (let key = 0; key < obj.length; key++) { copy[key] = deepCopy(obj[key]); } return copy; @@ -13,7 +13,7 @@ exports.deepCopy = function deepCopy(obj) { return obj; copy = {}; - for (var key in obj) + for (let key in obj) copy[key] = deepCopy(obj[key]); return copy; }; diff --git a/src/lib/dom.js b/src/lib/dom.js index fc3125fa490..6a53ef8ae11 100644 --- a/src/lib/dom.js +++ b/src/lib/dom.js @@ -3,6 +3,13 @@ var useragent = require("./useragent"); var XHTML_NS = "http://www.w3.org/1999/xhtml"; +/** + * + * @param {any} arr + * @param {HTMLElement} [parent] + * @param [refs] + * @returns {HTMLElement | Text | any[]} + */ exports.buildDom = function buildDom(arr, parent, refs) { if (typeof arr == "string" && arr) { var txt = document.createTextNode(arr); @@ -53,39 +60,71 @@ exports.buildDom = function buildDom(arr, parent, refs) { return el; }; +/** + * + * @param {Document} [doc] + * @returns {HTMLHeadElement|HTMLElement} + */ exports.getDocumentHead = function(doc) { if (!doc) doc = document; return doc.head || doc.getElementsByTagName("head")[0] || doc.documentElement; }; + +/** + * @template {keyof HTMLElementTagNameMap} T + * @param {T | string} tag + * @param {string} [ns] + * @returns {HTMLElementTagNameMap[T]} + */ exports.createElement = function(tag, ns) { + // @ts-ignore return document.createElementNS ? document.createElementNS(ns || XHTML_NS, tag) : document.createElement(tag); }; +/** + * @param {HTMLElement} element + */ exports.removeChildren = function(element) { element.innerHTML = ""; }; +/** + * @param {string} textContent + * @param {HTMLElement} [element] + * @returns {Text} + */ exports.createTextNode = function(textContent, element) { var doc = element ? element.ownerDocument : document; return doc.createTextNode(textContent); }; +/** + * @param {HTMLElement} [element] + * @returns {DocumentFragment} + */ exports.createFragment = function(element) { var doc = element ? element.ownerDocument : document; return doc.createDocumentFragment(); }; +/** + * @param {HTMLElement} el + * @param {string} name + * @returns {boolean} + */ exports.hasCssClass = function(el, name) { var classes = (el.className + "").split(/\s+/g); return classes.indexOf(name) !== -1; }; -/* -* Add a CSS class to the list of classes on the given node +/** + * Add a CSS class to the list of classes on the given node + * @param {HTMLElement} el + * @param {string} name */ exports.addCssClass = function(el, name) { if (!exports.hasCssClass(el, name)) { @@ -93,9 +132,11 @@ exports.addCssClass = function(el, name) { } }; -/* -* Remove a CSS class from the list of classes on the given node -*/ +/** + * Remove a CSS class from the list of classes on the given node + * @param {HTMLElement} el + * @param {string} name + */ exports.removeCssClass = function(el, name) { var classes = el.className.split(/\s+/g); while (true) { @@ -108,6 +149,11 @@ exports.removeCssClass = function(el, name) { el.className = classes.join(" "); }; +/** + * @param {HTMLElement} el + * @param {string} name + * @returns {boolean} + */ exports.toggleCssClass = function(el, name) { var classes = el.className.split(/\s+/g), add = true; while (true) { @@ -125,11 +171,13 @@ exports.toggleCssClass = function(el, name) { return add; }; - -/* - * Add or remove a CSS class from the list of classes on the given node - * depending on the value of include - */ +/** + * Add or remove a CSS class from the list of classes on the given node + * depending on the value of include + * @param {HTMLElement} node + * @param {string} className + * @param {boolean} include + */ exports.setCssClass = function(node, className, include) { if (include) { exports.addCssClass(node, className); @@ -138,6 +186,11 @@ exports.setCssClass = function(node, className, include) { } }; +/** + * @param {string} id + * @param {Document} [doc] + * @returns {boolean} + */ exports.hasCssString = function(id, doc) { var index = 0, sheets; doc = doc || document; @@ -150,6 +203,10 @@ exports.hasCssString = function(id, doc) { } }; +/** + * @param {string} id + * @param {Document} [doc] + */ exports.removeElementById = function(id, doc) { doc = doc || document; if(doc.getElementById(id)) { @@ -214,9 +271,18 @@ function importCssString(cssText, id, target) { } exports.importCssString = importCssString; +/** + * @param {string} uri + * @param {Document} [doc] + */ exports.importCssStylsheet = function(uri, doc) { exports.buildDom(["link", {rel: "stylesheet", href: uri}], exports.getDocumentHead(doc)); }; + +/** + * @param {Document} [doc] + * @returns {number} + */ exports.scrollbarWidth = function(doc) { var inner = exports.createElement("ace_inner"); inner.style.width = "100%"; @@ -256,10 +322,21 @@ exports.scrollbarWidth = function(doc) { return noScrollbar - withScrollbar; }; +/** + * @param {Element} element + * @param [style] + * @returns {Partial} + */ exports.computedStyle = function(element, style) { return window.getComputedStyle(element, "") || {}; }; +/** + * + * @param {CSSStyleDeclaration} styles + * @param {string} property + * @param {string} value + */ exports.setStyle = function(styles, property, value) { if (styles[property] !== value) { //console.log("set style", property, styles[property], value); diff --git a/src/lib/event.js b/src/lib/event.js index 39af434d246..3c7c5504647 100644 --- a/src/lib/event.js +++ b/src/lib/event.js @@ -1,6 +1,6 @@ "use strict"; -var keys = require("./keys"); +/** @type {any} */var keys = require("./keys"); var useragent = require("./useragent"); var pressedKeys = null; @@ -13,6 +13,7 @@ function detectListenerOptionsSupport() { document.createComment("").addEventListener("test", function() {}, { get passive() { activeListenerOptions = {passive: false}; + return true; } }); } catch(e) {} @@ -34,7 +35,7 @@ EventListener.prototype.destroy = function() { this.elem = this.type = this.callback = undefined; }; -var addListener = exports.addListener = function(elem, type, callback, destroyer) { +var addListener = exports.addListener = function(elem, type, callback, /**@type{any?}*/destroyer) { elem.addEventListener(type, callback, getListenerOptions()); if (destroyer) destroyer.$toDestroy.push(new EventListener(elem, type, callback)); @@ -94,6 +95,11 @@ exports.capture = function(el, eventHandler, releaseCaptureHandler) { return onMouseUp; }; +/** + * @param el + * @param callback + * @param [destroyer] + */ exports.addMouseWheelListener = function(el, callback, destroyer) { addListener(el, "wheel", function(e) { var factor = 0.15; @@ -120,6 +126,13 @@ exports.addMouseWheelListener = function(el, callback, destroyer) { }, destroyer); }; +/** + * @param elements + * @param timeouts + * @param eventHandler + * @param callbackName + * @param [destroyer] + */ exports.addMultiMouseDownListener = function(elements, timeouts, eventHandler, callbackName, destroyer) { var clicks = 0; var startX, startY, timer; @@ -233,47 +246,35 @@ function normalizeCommandKeys(callback, e, keyCode) { return callback(e, hashId, keyCode); } - +/** + * @param el + * @param callback + * @param [destroyer] + */ exports.addCommandKeyListener = function(el, callback, destroyer) { - if (useragent.isOldGecko || (useragent.isOpera && !("KeyboardEvent" in window))) { - // Old versions of Gecko aka. Firefox < 4.0 didn't repeat the keydown - // event if the user pressed the key for a longer time. Instead, the - // keydown event was fired once and later on only the keypress event. - // To emulate the 'right' keydown behavior, the keyCode of the initial - // keyDown event is stored and in the following keypress events the - // stores keyCode is used to emulate a keyDown event. - var lastKeyDownKeyCode = null; - addListener(el, "keydown", function(e) { - lastKeyDownKeyCode = e.keyCode; - }, destroyer); - addListener(el, "keypress", function(e) { - return normalizeCommandKeys(callback, e, lastKeyDownKeyCode); - }, destroyer); - } else { - var lastDefaultPrevented = null; - - addListener(el, "keydown", function(e) { - pressedKeys[e.keyCode] = (pressedKeys[e.keyCode] || 0) + 1; - var result = normalizeCommandKeys(callback, e, e.keyCode); - lastDefaultPrevented = e.defaultPrevented; - return result; - }, destroyer); - - addListener(el, "keypress", function(e) { - if (lastDefaultPrevented && (e.ctrlKey || e.altKey || e.shiftKey || e.metaKey)) { - exports.stopEvent(e); - lastDefaultPrevented = null; - } - }, destroyer); + var lastDefaultPrevented = null; - addListener(el, "keyup", function(e) { - pressedKeys[e.keyCode] = null; - }, destroyer); + addListener(el, "keydown", function(e) { + pressedKeys[e.keyCode] = (pressedKeys[e.keyCode] || 0) + 1; + var result = normalizeCommandKeys(callback, e, e.keyCode); + lastDefaultPrevented = e.defaultPrevented; + return result; + }, destroyer); - if (!pressedKeys) { - resetPressedKeys(); - addListener(window, "focus", resetPressedKeys); + addListener(el, "keypress", function(e) { + if (lastDefaultPrevented && (e.ctrlKey || e.altKey || e.shiftKey || e.metaKey)) { + exports.stopEvent(e); + lastDefaultPrevented = null; } + }, destroyer); + + addListener(el, "keyup", function(e) { + pressedKeys[e.keyCode] = null; + }, destroyer); + + if (!pressedKeys) { + resetPressedKeys(); + addListener(window, "focus", resetPressedKeys); } }; function resetPressedKeys() { @@ -322,10 +323,10 @@ exports.blockIdle = function(delay) { }; exports.nextFrame = typeof window == "object" && (window.requestAnimationFrame - || window.mozRequestAnimationFrame - || window.webkitRequestAnimationFrame - || window.msRequestAnimationFrame - || window.oRequestAnimationFrame); + || window["mozRequestAnimationFrame"] + || window["webkitRequestAnimationFrame"] + || window["msRequestAnimationFrame"] + || window["oRequestAnimationFrame"]); if (exports.nextFrame) exports.nextFrame = exports.nextFrame.bind(window); diff --git a/src/lib/event_emitter.js b/src/lib/event_emitter.js index 5d1372bc947..aac6f3d70c0 100644 --- a/src/lib/event_emitter.js +++ b/src/lib/event_emitter.js @@ -1,5 +1,5 @@ "use strict"; - +/**@type {any}*/ var EventEmitter = {}; var stopPropagation = function() { this.propagationStopped = true; }; var preventDefault = function() { this.defaultPrevented = true; }; @@ -61,6 +61,7 @@ EventEmitter.once = function(eventName, callback) { EventEmitter.setDefaultHandler = function(eventName, callback) { + /**@type {any}*/ var handlers = this._defaultHandlers; if (!handlers) handlers = this._defaultHandlers = {_disabled_: {}}; @@ -119,7 +120,9 @@ EventEmitter.removeEventListener = function(eventName, callback) { if (index !== -1) listeners.splice(index, 1); }; - +/** + * @this {EventEmitter} + */ EventEmitter.removeAllListeners = function(eventName) { if (!eventName) this._eventRegistry = this._defaultHandlers = undefined; if (this._eventRegistry) this._eventRegistry[eventName] = undefined; diff --git a/src/lib/fixoldbrowsers.js b/src/lib/fixoldbrowsers.js index 86b576f4e05..4024243ed6c 100644 --- a/src/lib/fixoldbrowsers.js +++ b/src/lib/fixoldbrowsers.js @@ -12,4 +12,5 @@ "use strict"; +// @ts-ignore require("./es6-shim"); diff --git a/src/lib/keys.js b/src/lib/keys.js index 0c67bf0dfa1..ef8ddfcd3d2 100644 --- a/src/lib/keys.js +++ b/src/lib/keys.js @@ -153,6 +153,7 @@ oop.mixin(exports, Keys); exports.default = exports; +// @ts-ignore exports.keyCodeToString = function(keyCode) { // Language-switching keystroke in Chrome/Linux emits keyCode 0. var keyString = Keys[keyCode]; diff --git a/src/lib/net.js b/src/lib/net.js index 93873984d12..d8103ba82a0 100644 --- a/src/lib/net.js +++ b/src/lib/net.js @@ -24,6 +24,7 @@ exports.get = function (url, callback) { exports.loadScript = function(path, callback) { var head = dom.getDocumentHead(); + /**@type {HTMLScriptElement & {onload?: Function, onreadystatechange?: Function, readyState?: string}}*/ var s = document.createElement('script'); s.src = path; diff --git a/src/lib/oop.js b/src/lib/oop.js index 01289fe4eb4..e0489f46eb2 100644 --- a/src/lib/oop.js +++ b/src/lib/oop.js @@ -12,6 +12,13 @@ exports.inherits = function(ctor, superCtor) { }); }; +/** + * Implements mixin properties into the prototype of an object. + * @template T + * @param {T} obj - The prototype of the target object. + * @param {Object} mixin - The source object. + * @returns {T & Object} The merged prototype. + */ exports.mixin = function(obj, mixin) { for (var key in mixin) { obj[key] = mixin[key]; @@ -19,6 +26,13 @@ exports.mixin = function(obj, mixin) { return obj; }; +/** + * Implements mixin properties into the prototype of an object. + * @template T + * @param {T} proto - The prototype of the target object. + * @param {Object} mixin - The source object. + * @returns {T & Object} The merged prototype. + */ exports.implement = function(proto, mixin) { exports.mixin(proto, mixin); }; diff --git a/src/lib/report_error.js b/src/lib/report_error.js index 55f1372bd5b..43225b52fbd 100644 --- a/src/lib/report_error.js +++ b/src/lib/report_error.js @@ -1,7 +1,7 @@ exports.reportError = function reportError(msg, data) { var e = new Error(msg); - e.data = data; + e["data"] = data; if (typeof console == "object" && console.error) console.error(e); setTimeout(function() { throw e; }); diff --git a/src/lib/useragent.js b/src/lib/useragent.js index d3e57adb07a..418f11c3ec9 100644 --- a/src/lib/useragent.js +++ b/src/lib/useragent.js @@ -54,7 +54,8 @@ exports.isOldIE = exports.isIE && exports.isIE < 9; exports.isGecko = exports.isMozilla = ua.match(/ Gecko\/\d+/); // Is this Opera -exports.isOpera = typeof opera == "object" && Object.prototype.toString.call(window.opera) == "[object Opera]"; +// @ts-expect-error +exports.isOpera = typeof opera == "object" && Object.prototype.toString.call(window["opera"]) == "[object Opera]"; // Is the user using a browser that identifies itself as WebKit exports.isWebKit = parseFloat(ua.split("WebKit/")[1]) || undefined; @@ -71,7 +72,7 @@ exports.isAndroid = ua.indexOf("Android") >= 0; exports.isChromeOS = ua.indexOf(" CrOS ") >= 0; -exports.isIOS = /iPad|iPhone|iPod/.test(ua) && !window.MSStream; +exports.isIOS = /iPad|iPhone|iPod/.test(ua) && !window["MSStream"]; if (exports.isIOS) exports.isMac = true; diff --git a/src/line_widgets.js b/src/line_widgets.js index ad19e2f5821..55db6f6ccc9 100644 --- a/src/line_widgets.js +++ b/src/line_widgets.js @@ -1,10 +1,18 @@ "use strict"; +/** + * @typedef {import("./edit_session").EditSession} EditSession + * @typedef {import("./editor").Editor} Editor + * @typedef {import("./virtual_renderer").VirtualRenderer} VirtualRenderer + * @typedef {import("../ace-internal").Ace.LineWidget} LineWidget + */ var dom = require("./lib/dom"); - class LineWidgets { + /** + * @param {EditSession} session + */ constructor(session) { this.session = session; this.session.widgetManager = this; @@ -20,20 +28,27 @@ class LineWidgets { this.session.on("changeFold", this.updateOnFold); this.session.on("changeEditor", this.$onChangeEditor); } - + + /** + * @param {number} row + * @return {number} + */ getRowLength(row) { var h; if (this.lineWidgets) h = this.lineWidgets[row] && this.lineWidgets[row].rowCount || 0; else h = 0; - if (!this.$useWrapMode || !this.$wrapData[row]) { + if (!this["$useWrapMode"] || !this["$wrapData"][row]) { return 1 + h; } else { - return this.$wrapData[row].length + 1 + h; + return this["$wrapData"][row].length + 1 + h; } } + /** + * @return {number} + */ $getWidgetScreenLength() { var screenRows = 0; this.lineWidgets.forEach(function(w){ @@ -46,7 +61,11 @@ class LineWidgets { $onChangeEditor(e) { this.attach(e.editor); } - + + /** + * + * @param {Editor} editor + */ attach(editor) { if (editor && editor.widgetManager && editor.widgetManager != this) editor.widgetManager.detach(); @@ -55,6 +74,7 @@ class LineWidgets { return; this.detach(); + /**@type {Editor} */ this.editor = editor; if (editor) { @@ -82,6 +102,11 @@ class LineWidgets { }); } + /** + * + * @param e + * @param {EditSession} session + */ updateOnFold(e, session) { var lineWidgets = session.lineWidgets; if (!lineWidgets || !e.action) @@ -107,7 +132,11 @@ class LineWidgets { } } } - + + /** + * + * @param {import("../ace-internal").Ace.Delta} delta + */ updateOnChange(delta) { var lineWidgets = this.session.lineWidgets; if (!lineWidgets) return; @@ -156,6 +185,11 @@ class LineWidgets { this.session.lineWidgets = null; } + /** + * + * @param {LineWidget} w + * @return {LineWidget} + */ $registerLineWidget(w) { if (!this.session.lineWidgets) this.session.lineWidgets = new Array(this.session.getLength()); @@ -172,7 +206,12 @@ class LineWidgets { this.session.lineWidgets[w.row] = w; return w; } - + + /** + * + * @param {LineWidget} w + * @return {LineWidget} + */ addLineWidget(w) { this.$registerLineWidget(w); w.session = this.session; @@ -194,12 +233,12 @@ class LineWidgets { dom.addCssClass(w.el, w.className); } w.el.style.position = "absolute"; - w.el.style.zIndex = 5; + w.el.style.zIndex = "5"; renderer.container.appendChild(w.el); w._inDocument = true; if (!w.coverGutter) { - w.el.style.zIndex = 3; + w.el.style.zIndex = "3"; } if (w.pixelHeight == null) { w.pixelHeight = w.el.offsetHeight; @@ -227,6 +266,9 @@ class LineWidgets { return w; } + /** + * @param {LineWidget} w + */ removeLineWidget(w) { w._inDocument = false; w.session = null; @@ -254,7 +296,12 @@ class LineWidgets { this.session._emit("changeFold", {data:{start:{row: w.row}}}); this.$updateRows(); } - + + /** + * + * @param {number} row + * @return {LineWidget[]} + */ getWidgetsAtRow(row) { var lineWidgets = this.session.lineWidgets; var w = lineWidgets && lineWidgets[row]; @@ -265,12 +312,19 @@ class LineWidgets { } return list; } - + + /** + * @param {LineWidget} w + */ onWidgetChanged(w) { this.session._changedWidgets.push(w); this.editor && this.editor.renderer.updateFull(); } - + + /** + * @param {any} e + * @param {VirtualRenderer} renderer + */ measureWidgets(e, renderer) { var changedWidgets = this.session._changedWidgets; var config = renderer.layerConfig; @@ -313,7 +367,11 @@ class LineWidgets { } this.session._changedWidgets = []; } - + + /** + * @param {any} e + * @param {VirtualRenderer} renderer + */ renderWidgets(e, renderer) { var config = renderer.layerConfig; var lineWidgets = this.session.lineWidgets; diff --git a/src/marker_group.js b/src/marker_group.js index 0e559c66cc5..07b431724bd 100644 --- a/src/marker_group.js +++ b/src/marker_group.js @@ -1,4 +1,11 @@ "use strict"; +/** + * @typedef {import("./edit_session").EditSession} EditSession + * @typedef {{range: import("./range").Range, className: string}} MarkerGroupItem + */ +/** + * @typedef {import("./layer/marker").Marker} Marker + */ /* Potential improvements: @@ -6,16 +13,21 @@ Potential improvements: */ class MarkerGroup { + /** + * @param {EditSession} session + */ constructor(session) { this.markers = []; + /**@type {EditSession}*/ this.session = session; + // @ts-expect-error TODO: could potential error here, or most likely missing checks in other places session.addDynamicMarker(this); } /** * Finds the first marker containing pos - * @param {Position} pos - * @returns Ace.MarkerGroupItem + * @param {import("../ace-internal").Ace.Point} pos + * @returns import("../ace-internal").Ace.MarkerGroupItem */ getMarkerAtPosition(pos) { return this.markers.find(function(marker) { @@ -26,8 +38,8 @@ class MarkerGroup { /** * Comparator for Array.sort function, which sorts marker definitions by their positions * - * @param {Ace.MarkerGroupItem} a first marker. - * @param {Ace.MarkerGroupItem} b second marker. + * @param {MarkerGroupItem} a first marker. + * @param {MarkerGroupItem} b second marker. * @returns {number} negative number if a should be before b, positive number if b should be before a, 0 otherwise. */ markersComparator(a, b) { @@ -36,13 +48,19 @@ class MarkerGroup { /** * Sets marker definitions to be rendered. Limits the number of markers at MAX_MARKERS. - * @param {Ace.MarkerGroupItem[]} markers an array of marker definitions. + * @param {MarkerGroupItem[]} markers an array of marker definitions. */ setMarkers(markers) { this.markers = markers.sort(this.markersComparator).slice(0, this.MAX_MARKERS); this.session._signal("changeBackMarker"); } + /** + * @param {any} html + * @param {Marker} markerLayer + * @param {EditSession} session + * @param {{ firstRow: any; lastRow: any; }} config + */ update(html, markerLayer, session, config) { if (!this.markers || !this.markers.length) return; diff --git a/src/mode/behaviour.js b/src/mode/behaviour.js index c62b0fe1582..764843849e3 100644 --- a/src/mode/behaviour.js +++ b/src/mode/behaviour.js @@ -1,11 +1,19 @@ "use strict"; +/** + * @typedef {Behaviour & {[key: string]: any}} IBehaviour + */ -var Behaviour = function() { +/**@type {any}*/ +var Behaviour; +Behaviour = function() { this.$behaviours = {}; }; (function () { + /** + * @this {Behaviour & this} + */ this.add = function (name, action, callback) { switch (undefined) { case this.$behaviours: @@ -15,7 +23,10 @@ var Behaviour = function() { } this.$behaviours[name][action] = callback; }; - + + /** + * @this {Behaviour & this} + */ this.addBehaviours = function (behaviours) { for (var key in behaviours) { for (var action in behaviours[key]) { @@ -23,13 +34,19 @@ var Behaviour = function() { } } }; - + + /** + * @this {Behaviour & this} + */ this.remove = function (name) { if (this.$behaviours && this.$behaviours[name]) { delete this.$behaviours[name]; } }; - + + /** + * @this {Behaviour & this} + */ this.inherit = function (mode, filter) { if (typeof mode === "function") { var behaviours = new mode().getBehaviours(filter); @@ -38,7 +55,13 @@ var Behaviour = function() { } this.addBehaviours(behaviours); }; - + + /** + * + * @param [filter] + * @returns {{}|*} + * @this {Behaviour & this} + */ this.getBehaviours = function (filter) { if (!filter) { return this.$behaviours; diff --git a/src/mode/behaviour/cstyle.js b/src/mode/behaviour/cstyle.js index ab6c9534968..12f545ec428 100644 --- a/src/mode/behaviour/cstyle.js +++ b/src/mode/behaviour/cstyle.js @@ -1,5 +1,4 @@ "use strict"; - var oop = require("../../lib/oop"); var Behaviour = require("../behaviour").Behaviour; var TokenIterator = require("../../token_iterator").TokenIterator; @@ -53,7 +52,8 @@ var getWrapped = function(selection, selected, opening, closing) { * @param {boolean} [options.braces] - Whether to force braces auto-pairing. * @param {boolean} [options.closeDocComment] - enables automatic insertion of closing tags for documentation comments. */ -var CstyleBehaviour = function(options) { +var CstyleBehaviour; +CstyleBehaviour = function(options) { options = options || {}; this.add("braces", "insertion", function(state, action, editor, session, text) { var cursor = editor.getCursorPosition(); @@ -337,7 +337,10 @@ var CstyleBehaviour = function(options) { } }; - +/** + * @this {CstyleBehaviour} + */ +// @ts-ignore CstyleBehaviour.isSaneInsertion = function(editor, session) { var cursor = editor.getCursorPosition(); var iterator = new TokenIterator(session, cursor.row, cursor.column); @@ -357,26 +360,25 @@ CstyleBehaviour.isSaneInsertion = function(editor, session) { return iterator.getCurrentTokenRow() !== cursor.row || this.$matchTokenType(iterator.getCurrentToken() || "text", SAFE_INSERT_BEFORE_TOKENS); }; - -CstyleBehaviour.$matchTokenType = function(token, types) { +CstyleBehaviour["$matchTokenType"] = function(token, types) { return types.indexOf(token.type || token) > -1; }; -CstyleBehaviour.recordAutoInsert = function(editor, session, bracket) { +CstyleBehaviour["recordAutoInsert"] = function(editor, session, bracket) { var cursor = editor.getCursorPosition(); var line = session.doc.getLine(cursor.row); // Reset previous state if text or context changed too much - if (!this.isAutoInsertedClosing(cursor, line, context.autoInsertedLineEnd[0])) + if (!this["isAutoInsertedClosing"](cursor, line, context.autoInsertedLineEnd[0])) context.autoInsertedBrackets = 0; context.autoInsertedRow = cursor.row; context.autoInsertedLineEnd = bracket + line.substr(cursor.column); context.autoInsertedBrackets++; }; -CstyleBehaviour.recordMaybeInsert = function(editor, session, bracket) { +CstyleBehaviour["recordMaybeInsert"] = function(editor, session, bracket) { var cursor = editor.getCursorPosition(); var line = session.doc.getLine(cursor.row); - if (!this.isMaybeInsertedClosing(cursor, line)) + if (!this["isMaybeInsertedClosing"](cursor, line)) context.maybeInsertedBrackets = 0; context.maybeInsertedRow = cursor.row; context.maybeInsertedLineStart = line.substr(0, cursor.column) + bracket; @@ -384,26 +386,26 @@ CstyleBehaviour.recordMaybeInsert = function(editor, session, bracket) { context.maybeInsertedBrackets++; }; -CstyleBehaviour.isAutoInsertedClosing = function(cursor, line, bracket) { +CstyleBehaviour["isAutoInsertedClosing"] = function(cursor, line, bracket) { return context.autoInsertedBrackets > 0 && cursor.row === context.autoInsertedRow && bracket === context.autoInsertedLineEnd[0] && line.substr(cursor.column) === context.autoInsertedLineEnd; }; -CstyleBehaviour.isMaybeInsertedClosing = function(cursor, line) { +CstyleBehaviour["isMaybeInsertedClosing"] = function(cursor, line) { return context.maybeInsertedBrackets > 0 && cursor.row === context.maybeInsertedRow && line.substr(cursor.column) === context.maybeInsertedLineEnd && line.substr(0, cursor.column) == context.maybeInsertedLineStart; }; -CstyleBehaviour.popAutoInsertedClosing = function() { +CstyleBehaviour["popAutoInsertedClosing"] = function() { context.autoInsertedLineEnd = context.autoInsertedLineEnd.substr(1); context.autoInsertedBrackets--; }; -CstyleBehaviour.clearMaybeInsertedClosing = function() { +CstyleBehaviour["clearMaybeInsertedClosing"] = function() { if (context) { context.maybeInsertedBrackets = 0; context.maybeInsertedRow = -1; diff --git a/src/mode/text.js b/src/mode/text.js index 05c3c8cd14a..da1ce98c4bb 100644 --- a/src/mode/text.js +++ b/src/mode/text.js @@ -2,6 +2,7 @@ var config = require("../config"); var Tokenizer = require("../tokenizer").Tokenizer; + var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules; var CstyleBehaviour = require("./behaviour/cstyle").CstyleBehaviour; var unicode = require("../unicode"); @@ -9,21 +10,8 @@ var lang = require("../lib/lang"); var TokenIterator = require("../token_iterator").TokenIterator; var Range = require("../range").Range; -/** - * - * @constructor - * @alias TextMode - * @property {{[quote: string]: string}} [$quotes] - quotes used by language mode - * @property {string} lineCommentStart - characters that indicate the start of a line comment - * @property {{start: string, end: string}} [blockComment] - characters that indicate the start and end of a block comment - * @property {TextHighlightRules} HighlightRules - language specific highlighters - * @property {FoldMode} foldingRules - language specific folding rules - * @property {MatchingBraceOutdent} $outdent - * @property {RegExp} tokenRe - * @property {RegExp} nonTokenRe - * @property {{[quote: string]: RegExp}} [$pairQuotesAfter] - An object containing conditions to determine whether to apply matching quote or not. - */ -var Mode = function() { +var Mode; +Mode = function() { this.HighlightRules = TextHighlightRules; }; @@ -34,6 +22,9 @@ var Mode = function() { this.nonTokenRe = new RegExp("^(?:[^" + unicode.wordChars + "\\$_]|\\s])+", "g"); + /** + * @this {import("../../ace-internal").Ace.SyntaxMode} + */ this.getTokenizer = function() { if (!this.$tokenizer) { this.$highlightRules = this.$highlightRules || new this.HighlightRules(this.$highlightRuleConfig); @@ -45,6 +36,9 @@ var Mode = function() { this.lineCommentStart = ""; this.blockComment = ""; + /** + * @this {import("../../ace-internal").Ace.SyntaxMode} + */ this.toggleCommentLines = function(state, session, startRow, endRow) { var doc = session.doc; @@ -57,8 +51,10 @@ var Mode = function() { if (!this.lineCommentStart) { if (!this.blockComment) return false; + /**@type {any}*/ var lineCommentStart = this.blockComment.start; var lineCommentEnd = this.blockComment.end; + /**@type {any}*/ var regexpStart = new RegExp("^(\\s*)(?:" + lang.escapeRegExp(lineCommentStart) + ")"); var regexpEnd = new RegExp("(?:" + lang.escapeRegExp(lineCommentEnd) + ")\\s*$"); @@ -79,6 +75,7 @@ var Mode = function() { doc.removeInLine(i, m[1].length, m[0].length); }; + /**@type {any}*/ var testRemove = function(line, row) { if (regexpStart.test(line)) return true; @@ -90,10 +87,13 @@ var Mode = function() { }; } else { if (Array.isArray(this.lineCommentStart)) { + /**@type {any}*/ var regexpStart = this.lineCommentStart.map(lang.escapeRegExp).join("|"); + /**@type {any}*/ var lineCommentStart = this.lineCommentStart[0]; } else { var regexpStart = lang.escapeRegExp(this.lineCommentStart); + /**@type {any}*/ var lineCommentStart = this.lineCommentStart; } regexpStart = new RegExp("^(\\s*)(?:" + regexpStart + ") ?"); @@ -117,6 +117,7 @@ var Mode = function() { doc.insertInLine({row: i, column: minIndent}, lineCommentStart); } }; + /**@type {any}*/ var testRemove = function(line, i) { return regexpStart.test(line); }; @@ -168,6 +169,9 @@ var Mode = function() { iter(shouldRemove ? uncomment : comment); }; + /** + * @this {import("../../ace-internal").Ace.SyntaxMode} + */ this.toggleBlockComment = function(state, session, range, cursor) { var comment = this.blockComment; if (!comment) @@ -250,7 +254,7 @@ var Mode = function() { this.createModeDelegates = function (mapping) { this.$embeds = []; this.$modes = {}; - for (var i in mapping) { + for (let i in mapping) { if (mapping[i]) { var Mode = mapping[i]; var id = Mode.prototype.$id; @@ -267,17 +271,22 @@ var Mode = function() { var delegations = ["toggleBlockComment", "toggleCommentLines", "getNextLineIndent", "checkOutdent", "autoOutdent", "transformAction", "getCompletions"]; - for (var i = 0; i < delegations.length; i++) { + for (let i = 0; i < delegations.length; i++) { (function(scope) { var functionName = delegations[i]; var defaultHandler = scope[functionName]; - scope[delegations[i]] = function() { - return this.$delegator(functionName, arguments, defaultHandler); - }; + scope[delegations[i]] = + /** @this {import("../../ace-internal").Ace.SyntaxMode} */ + function () { + return this.$delegator(functionName, arguments, defaultHandler); + }; }(this)); } }; + /** + * @this {import("../../ace-internal").Ace.SyntaxMode} + */ this.$delegator = function(method, args, defaultHandler) { var state = args[0] || "start"; if (typeof state != "string") { @@ -304,6 +313,9 @@ var Mode = function() { return defaultHandler ? ret : undefined; }; + /** + * @this {import("../../ace-internal").Ace.SyntaxMode} + */ this.transformAction = function(state, action, editor, session, param) { if (this.$behaviour) { var behaviours = this.$behaviour.getBehaviours(); @@ -317,11 +329,14 @@ var Mode = function() { } } }; - + + /** + * @this {import("../../ace-internal").Ace.SyntaxMode} + */ this.getKeywords = function(append) { // this is for autocompletion to pick up regexp'ed keywords if (!this.completionKeywords) { - var rules = this.$tokenizer.rules; + var rules = this.$tokenizer["rules"]; var completionKeywords = []; for (var rule in rules) { var ruleItr = rules[rule]; @@ -348,13 +363,19 @@ var Mode = function() { return this.$keywordList; return completionKeywords.concat(this.$keywordList || []); }; - + + /** + * @this {import("../../ace-internal").Ace.SyntaxMode} + */ this.$createKeywordList = function() { if (!this.$highlightRules) this.getTokenizer(); return this.$keywordList = this.$highlightRules.$keywordList || []; }; + /** + * @this {import("../../ace-internal").Ace.SyntaxMode} + */ this.getCompletions = function(state, session, pos, prefix) { var keywords = this.$keywordList || this.$createKeywordList(); return keywords.map(function(word) { diff --git a/src/mode/text_highlight_rules.js b/src/mode/text_highlight_rules.js index a092b35d5a6..a866846445e 100644 --- a/src/mode/text_highlight_rules.js +++ b/src/mode/text_highlight_rules.js @@ -2,7 +2,9 @@ const deepCopy = require("../lib/deep_copy").deepCopy; -var TextHighlightRules = function() { +/**@type {(new() => Partial) & {prototype: import("../../ace-internal").Ace.HighlightRules}}*/ +var TextHighlightRules; +TextHighlightRules = function() { // regexp must not have capturing parentheses // regexps are ordered -> the first match is used @@ -19,6 +21,11 @@ var TextHighlightRules = function() { (function() { + /** + * @param {import("../../ace-internal").Ace.HighlightRulesMap} rules + * @param {string} [prefix] + * @this {import("../../ace-internal").Ace.HighlightRules} + */ this.addRules = function(rules, prefix) { if (!prefix) { for (var key in rules) @@ -42,10 +49,22 @@ var TextHighlightRules = function() { } }; + /** + * @returns {import("../../ace-internal").Ace.HighlightRulesMap} + * @this {import("../../ace-internal").Ace.HighlightRules} + */ this.getRules = function() { return this.$rules; }; + /** + * @param HighlightRules + * @param prefix + * @param escapeRules + * @param states + * @param append + * @this {import("../../ace-internal").Ace.HighlightRules} + */ this.embedRules = function (HighlightRules, prefix, escapeRules, states, append) { var embedRules = typeof HighlightRules == "function" ? new HighlightRules().getRules() @@ -72,6 +91,9 @@ var TextHighlightRules = function() { this.$embeds.push(prefix); }; + /** + * @this {import("../../ace-internal").Ace.HighlightRules} + */ this.getEmbeds = function() { return this.$embeds; }; @@ -87,17 +109,21 @@ var TextHighlightRules = function() { return stack.shift() || "start"; }; + /** + * @this {import("../../ace-internal").Ace.HighlightRules} + */ this.normalizeRules = function() { var id = 0; var rules = this.$rules; function processState(key) { var state = rules[key]; - state.processed = true; + state["processed"] = true; for (var i = 0; i < state.length; i++) { var rule = state[i]; var toInsert = null; if (Array.isArray(rule)) { toInsert = rule; + // @ts-ignore rule = {}; } if (!rule.regex && rule.start) { @@ -158,6 +184,10 @@ var TextHighlightRules = function() { } if (toInsert) { + /** + * @type{any[]} + */ + // @ts-ignore var args = [i, 1].concat(toInsert); if (rule.noEscape) args = args.filter(function(x) {return !x.next;}); @@ -198,6 +228,9 @@ var TextHighlightRules = function() { : function(value) {return keywords[value] || defaultToken; }; }; + /** + * @this {import("../../ace-internal").Ace.HighlightRules} + */ this.getKeywords = function() { return this.$keywords; }; diff --git a/src/mouse/default_gutter_handler.js b/src/mouse/default_gutter_handler.js index 91c78ad37bb..e3b07646853 100644 --- a/src/mouse/default_gutter_handler.js +++ b/src/mouse/default_gutter_handler.js @@ -1,9 +1,16 @@ "use strict"; +/** + * @typedef {import("./mouse_handler").MouseHandler} MouseHandler + */ var dom = require("../lib/dom"); var event = require("../lib/event"); var Tooltip = require("../tooltip").Tooltip; var nls = require("../config").nls; +/** + * @param {MouseHandler} mouseHandler + * @this {MouseHandler} + */ function GutterHandler(mouseHandler) { var editor = mouseHandler.editor; var gutter = editor.renderer.$gutterLayer; @@ -167,7 +174,7 @@ class GutterTooltip extends Tooltip { var annotationsInFold = {error: [], warning: [], info: []}; var mostSevereAnnotationInFoldType; - for (var i = row + 1; i <= fold.end.row; i++){ + for (let i = row + 1; i <= fold.end.row; i++){ if (!gutter.$annotations[i]) continue; @@ -202,7 +209,7 @@ class GutterTooltip extends Tooltip { var iconClassName = gutter.$useSvgGutterIcons ? "ace_icon_svg" : "ace_icon"; // Construct the contents of the tooltip. - for (var i = 0; i < annotation.text.length; i++) { + for (let i = 0; i < annotation.text.length; i++) { var line = ` ${annotation.text[i]}`; annotationMessages[annotation.type[i].replace("_fold","")].push(line); } diff --git a/src/mouse/default_handlers.js b/src/mouse/default_handlers.js index de020471ee5..d04954df8a9 100644 --- a/src/mouse/default_handlers.js +++ b/src/mouse/default_handlers.js @@ -1,11 +1,17 @@ "use strict"; - +/** + * @typedef {import("./mouse_handler").MouseHandler} MouseHandler + * @typedef {import("./mouse_event").MouseEvent} MouseEvent + */ var useragent = require("../lib/useragent"); var DRAG_OFFSET = 0; // pixels var SCROLL_COOLDOWN_T = 550; // milliseconds class DefaultHandlers { + /** + * @param {MouseHandler} mouseHandler + */ constructor(mouseHandler) { mouseHandler.$clickSelection = null; @@ -23,10 +29,14 @@ class DefaultHandlers { mouseHandler[x] = this[x]; }, this); - mouseHandler.selectByLines = this.extendSelectionBy.bind(mouseHandler, "getLineRange"); - mouseHandler.selectByWords = this.extendSelectionBy.bind(mouseHandler, "getWordRange"); + mouseHandler["selectByLines"] = this.extendSelectionBy.bind(mouseHandler, "getLineRange"); + mouseHandler["selectByWords"] = this.extendSelectionBy.bind(mouseHandler, "getWordRange"); } + /** + * @param {MouseEvent} ev + * @this {MouseHandler} + */ onMouseDown(ev) { var inSelection = ev.inSelection(); var pos = ev.getDocumentPosition(); @@ -67,6 +77,12 @@ class DefaultHandlers { return ev.preventDefault(); } + /** + * + * @param {import("../../ace-internal").Ace.Position} [pos] + * @param {boolean} [waitForClickSelection] + * @this {MouseHandler} + */ startSelect(pos, waitForClickSelection) { pos = pos || this.editor.renderer.screenToTextCoordinates(this.x, this.y); var editor = this.editor; @@ -82,6 +98,9 @@ class DefaultHandlers { this.setState("select"); } + /** + * @this {MouseHandler} + */ select() { var anchor, editor = this.editor; var cursor = editor.renderer.screenToTextCoordinates(this.x, this.y); @@ -103,6 +122,10 @@ class DefaultHandlers { editor.renderer.scrollCursorIntoView(); } + /** + * @param {string | number} unitName + * @this {MouseHandler} + */ extendSelectionBy(unitName) { var anchor, editor = this.editor; var cursor = editor.renderer.screenToTextCoordinates(this.x, this.y); @@ -132,12 +155,18 @@ class DefaultHandlers { editor.selection.selectToPosition(cursor); editor.renderer.scrollCursorIntoView(); } - + + /** + * @this {MouseHandler} + */ selectByLinesEnd() { this.$clickSelection = null; this.editor.unsetStyle("ace_selecting"); } + /** + * @this {MouseHandler} + */ focusWait() { var distance = calcDistance(this.mousedownEvent.x, this.mousedownEvent.y, this.x, this.y); var time = Date.now(); @@ -145,7 +174,11 @@ class DefaultHandlers { if (distance > DRAG_OFFSET || time - this.mousedownEvent.time > this.$focusTimeout) this.startSelect(this.mousedownEvent.getDocumentPosition()); } - + + /** + * @param {MouseEvent} ev + * @this {MouseHandler} + */ onDoubleClick(ev) { var pos = ev.getDocumentPosition(); var editor = this.editor; @@ -166,6 +199,10 @@ class DefaultHandlers { this.select(); } + /** + * @param {MouseEvent} ev + * @this {MouseHandler} + */ onTripleClick(ev) { var pos = ev.getDocumentPosition(); var editor = this.editor; @@ -181,6 +218,10 @@ class DefaultHandlers { this.select(); } + /** + * @param {MouseEvent} ev + * @this {MouseHandler} + */ onQuadClick(ev) { var editor = this.editor; @@ -189,6 +230,10 @@ class DefaultHandlers { this.setState("selectAll"); } + /** + * @param {MouseEvent} ev + * @this {MouseHandler} + */ onMouseWheel(ev) { if (ev.getAccelKey()) return; diff --git a/src/mouse/dragdrop_handler.js b/src/mouse/dragdrop_handler.js index 0f7cb2c0a0f..859ff299a89 100644 --- a/src/mouse/dragdrop_handler.js +++ b/src/mouse/dragdrop_handler.js @@ -1,5 +1,7 @@ "use strict"; - +/** + * @typedef {import("./mouse_handler").MouseHandler} MouseHandler + */ var dom = require("../lib/dom"); var event = require("../lib/event"); var useragent = require("../lib/useragent"); @@ -8,6 +10,9 @@ var AUTOSCROLL_DELAY = 200; var SCROLL_CURSOR_DELAY = 200; var SCROLL_CURSOR_HYSTERESIS = 5; +/** + * @param {MouseHandler} mouseHandler + */ function DragdropHandler(mouseHandler) { var editor = mouseHandler.editor; @@ -21,6 +26,7 @@ function DragdropHandler(mouseHandler) { exports.forEach(function(x) { mouseHandler[x] = this[x]; }, this); + // @ts-ignore editor.on("mousedown", this.onMouseDown.bind(mouseHandler)); var mouseTarget = editor.container; @@ -32,7 +38,11 @@ function DragdropHandler(mouseHandler) { var autoScrollStartTime; var cursorMovedTime; var cursorPointOnCaretMoved; - + /** + * @param e + * @this {MouseHandler} + * @return {*} + */ this.onDragStart = function(e) { // webkit workaround, see this.onMouseDown if (this.cancelDrag || !mouseTarget.draggable) { @@ -60,7 +70,11 @@ function DragdropHandler(mouseHandler) { isInternal = true; this.setState("drag"); }; - + /** + * @param e + * @this {MouseHandler} + * @return {*} + */ this.onDragEnd = function(e) { mouseTarget.draggable = false; isInternal = false; @@ -75,7 +89,11 @@ function DragdropHandler(mouseHandler) { this.editor.unsetStyle("ace_dragging"); this.editor.renderer.setCursorStyle(""); }; - + /** + * @param e + * @this {MouseHandler} + * @return {*} + */ this.onDragEnter = function(e) { if (editor.getReadOnly() || !canAccept(e.dataTransfer)) return; @@ -88,7 +106,11 @@ function DragdropHandler(mouseHandler) { e.dataTransfer.dropEffect = dragOperation = getDropEffect(e); return event.preventDefault(e); }; - + /** + * @param e + * @this {MouseHandler} + * @return {*} + */ this.onDragOver = function(e) { if (editor.getReadOnly() || !canAccept(e.dataTransfer)) return; @@ -114,7 +136,11 @@ function DragdropHandler(mouseHandler) { return event.preventDefault(e); } }; - + /** + * @param e + * @this {MouseHandler} + * @return {*} + */ this.onDrop = function(e) { if (!dragCursor) return; @@ -291,14 +317,23 @@ function DragdropHandler(mouseHandler) { } } +/** + * @this {MouseHandler} + */ (function() { + /** + * @this {MouseHandler & this} + */ this.dragWait = function() { var interval = Date.now() - this.mousedownEvent.time; if (interval > this.editor.getDragDelay()) this.startDrag(); }; + /** + * @this {MouseHandler & this} + */ this.dragWaitEnd = function() { var target = this.editor.container; target.draggable = false; @@ -306,6 +341,9 @@ function DragdropHandler(mouseHandler) { this.selectEnd(); }; + /** + * @this {MouseHandler & this} + */ this.dragReadyEnd = function(e) { this.editor.$resetCursorStyle(); this.editor.unsetStyle("ace_dragging"); @@ -313,6 +351,9 @@ function DragdropHandler(mouseHandler) { this.dragWaitEnd(); }; + /** + * @this {MouseHandler & this} + */ this.startDrag = function(){ this.cancelDrag = false; var editor = this.editor; @@ -325,12 +366,16 @@ function DragdropHandler(mouseHandler) { this.setState("dragReady"); }; + /** + * @this {MouseHandler & this} + */ this.onMouseDrag = function(e) { var target = this.editor.container; if (useragent.isIE && this.state == "dragReady") { // IE does not handle [draggable] attribute set after mousedown var distance = calcDistance(this.mousedownEvent.x, this.mousedownEvent.y, this.x, this.y); if (distance > 3) + // @ts-ignore target.dragDrop(); } if (this.state === "dragWait") { @@ -342,6 +387,9 @@ function DragdropHandler(mouseHandler) { } }; + /** + * @this {MouseHandler & this} + */ this.onMouseDown = function(e) { if (!this.$dragEnabled) return; diff --git a/src/mouse/mouse_event.js b/src/mouse/mouse_event.js index eca34f95ce3..21f9c0b8292 100644 --- a/src/mouse/mouse_event.js +++ b/src/mouse/mouse_event.js @@ -8,6 +8,9 @@ var useragent = require("../lib/useragent"); */ class MouseEvent { constructor(domEvent, editor) { + /** @type {number} */this.speed; + /** @type {number} */this.wheelX; + /** @type {number} */this.wheelY; this.domEvent = domEvent; this.editor = editor; diff --git a/src/mouse/mouse_handler.js b/src/mouse/mouse_handler.js index 35cca30d300..7ec0623020e 100644 --- a/src/mouse/mouse_handler.js +++ b/src/mouse/mouse_handler.js @@ -1,5 +1,7 @@ "use strict"; - +/** + * @typedef {import("../editor").Editor} Editor + */ var event = require("../lib/event"); var useragent = require("../lib/useragent"); var DefaultHandlers = require("./default_handlers").DefaultHandlers; @@ -10,7 +12,15 @@ var addTouchListeners = require("./touch_handler").addTouchListeners; var config = require("../config"); class MouseHandler { + /** + * @param {Editor} editor + */ constructor(editor) { + /** @type {boolean} */this.$dragDelay; + /** @type {boolean} */this.$dragEnabled; + /** @type {boolean} */this.$mouseMoved; + /** @type {MouseEvent} */this.mouseEvent; + /** @type {number} */this.$focusTimeout; var _self = this; this.editor = editor; @@ -70,7 +80,9 @@ class MouseHandler { } else { renderer.setCursorStyle(""); } - }, editor); + + }, //@ts-expect-error TODO: seems mistyping - should be boolean + editor); } onMouseEvent(name, e) { @@ -87,8 +99,13 @@ class MouseHandler { this.editor._emit(name, new MouseEvent(e, this.editor)); } + /** + * @param {string} name + * @param {{ wheelX: number; wheelY: number; }} e + */ onMouseWheel(name, e) { var mouseEvent = new MouseEvent(e, this.editor); + //@ts-expect-error TODO: couldn't find this property init in the ace codebase mouseEvent.speed = this.$scrollSpeed * 2; mouseEvent.wheelX = e.wheelX; mouseEvent.wheelY = e.wheelY; diff --git a/src/multi_select.js b/src/multi_select.js index 5a3c3c848b0..d639f359ff5 100644 --- a/src/multi_select.js +++ b/src/multi_select.js @@ -1,3 +1,9 @@ +/** + * @typedef {import("./anchor").Anchor} Anchor + * @typedef {import("../ace-internal").Ace.Point} Point + * @typedef {import("../ace-internal").Ace.ScreenCoordinates} ScreenCoordinates + */ + var RangeList = require("./range_list").RangeList; var Range = require("./range").Range; var Selection = require("./selection").Selection; @@ -11,6 +17,11 @@ exports.commands = commands.defaultCommands.concat(commands.multiSelectCommands) var Search = require("./search").Search; var search = new Search(); +/** + * @param {EditSession} session + * @param {string | RegExp} needle + * @param {number} dir + */ function find(session, needle, dir) { search.$options.wrap = true; search.$options.needle = needle; @@ -21,6 +32,9 @@ function find(session, needle, dir) { // extend EditSession var EditSession = require("./edit_session").EditSession; (function() { + /** + * @this {EditSession} + */ this.getSelectionMarkers = function() { return this.$selectionMarkers; }; @@ -32,13 +46,14 @@ var EditSession = require("./edit_session").EditSession; this.ranges = null; // automatically sorted list of ranges + /**@type {RangeList | null} */ this.rangeList = null; /** * Adds a range to a selection by entering multiselect mode, if necessary. * @param {Range} range The new range to add * @param {Boolean} $blockChangeEvents Whether or not to block changing events - * @method Selection.addRange + * @this {Selection} **/ this.addRange = function(range, $blockChangeEvents) { if (!range) @@ -78,7 +93,8 @@ var EditSession = require("./edit_session").EditSession; }; /** - * @method Selection.toSingleRange + * @param {Range} [range] + * @this {Selection} **/ this.toSingleRange = function(range) { range = range || this.ranges[0]; @@ -91,8 +107,8 @@ var EditSession = require("./edit_session").EditSession; /** * Removes a Range containing pos (if it exists). - * @param {Range} pos The position to remove, as a `{row, column}` object - * @method Selection.substractPoint + * @param {Point} pos The position to remove, as a `{row, column}` object + * @this {Selection} **/ this.substractPoint = function(pos) { var removed = this.rangeList.substractPoint(pos); @@ -104,7 +120,7 @@ var EditSession = require("./edit_session").EditSession; /** * Merges overlapping ranges ensuring consistency after changes - * @method Selection.mergeOverlappingRanges + * @this {Selection} **/ this.mergeOverlappingRanges = function() { var removed = this.rangeList.merge(); @@ -112,12 +128,20 @@ var EditSession = require("./edit_session").EditSession; this.$onRemoveRange(removed); }; + /** + * @param {Range} range + * @this {Selection} + */ this.$onAddRange = function(range) { this.rangeCount = this.rangeList.ranges.length; this.ranges.unshift(range); this._signal("addRange", {range: range}); }; + /** + * @param {Range[]} removed + * @this {Selection} + */ this.$onRemoveRange = function(removed) { this.rangeCount = this.rangeList.ranges.length; if (this.rangeCount == 1 && this.inMultiSelectMode) { @@ -137,6 +161,7 @@ var EditSession = require("./edit_session").EditSession; this.inMultiSelectMode = false; this._signal("singleSelect"); this.session.$undoSelect = true; + // @ts-expect-error TODO: possible bug, no args in parameters this.rangeList.detach(this.session); } @@ -145,7 +170,10 @@ var EditSession = require("./edit_session").EditSession; this.fromOrientedRange(lastRange); }; - // adds multicursor support to selection + /** + * adds multicursor support to selection + * @this {Selection} + */ this.$initRangeList = function() { if (this.rangeList) return; @@ -158,7 +186,7 @@ var EditSession = require("./edit_session").EditSession; /** * Returns a concatenation of all the ranges. * @returns {Range[]} - * @method Selection.getAllRanges + * @this {Selection} **/ this.getAllRanges = function() { return this.rangeCount ? this.rangeList.ranges.concat() : [this.getRange()]; @@ -166,7 +194,7 @@ var EditSession = require("./edit_session").EditSession; /** * Splits all the ranges into lines. - * @method Selection.splitIntoLines + * @this {Selection} **/ this.splitIntoLines = function () { var ranges = this.ranges.length ? this.ranges : [this.getRange()]; @@ -190,7 +218,10 @@ var EditSession = require("./edit_session").EditSession; for (var i = newRanges.length; i--;) this.addRange(newRanges[i]); }; - + + /** + * @this {Selection} + */ this.joinSelections = function () { var ranges = this.rangeList.ranges; var lastRange = ranges[ranges.length - 1]; @@ -201,7 +232,7 @@ var EditSession = require("./edit_session").EditSession; }; /** - * @method Selection.toggleBlockSelection + * @this {Selection} **/ this.toggleBlockSelection = function () { if (this.rangeCount > 1) { @@ -216,19 +247,19 @@ var EditSession = require("./edit_session").EditSession; var anchor = this.session.documentToScreenPosition(this.anchor); var rectSel = this.rectangularRangeBlock(cursor, anchor); + // @ts-expect-error TODO: possible bug rectSel.forEach(this.addRange, this); } }; /** - * * Gets list of ranges composing rectangular block on the screen * - * @param {Position} screenCursor The cursor to use - * @param {Position} screenAnchor The anchor to use - * @param {Boolean} includeEmptyLines If true, this includes ranges inside the block which are empty due to clipping - * @returns {Range} - * @method Selection.rectangularRangeBlock + * @param {ScreenCoordinates} screenCursor The cursor to use + * @param {ScreenCoordinates} screenAnchor The anchor to use + * @param {Boolean} [includeEmptyLines] If true, this includes ranges inside the block which are empty due to clipping + * @returns {Range[]} + * @this {Selection} **/ this.rectangularRangeBlock = function(screenCursor, screenAnchor, includeEmptyLines) { var rectSel = []; @@ -308,7 +339,7 @@ var Editor = require("./editor").Editor; * * Updates the cursor and marker layers. * @method Editor.updateSelectionMarkers - * + * @this {Editor} **/ this.updateSelectionMarkers = function() { this.renderer.updateCursor(); @@ -317,9 +348,9 @@ var Editor = require("./editor").Editor; /** * Adds the selection and cursor. - * @param {Range} orientedRange A range containing a cursor - * @returns {Range} - * @method Editor.addSelectionMarker + * @param {Range & {marker?}} orientedRange A range containing a cursor + * @returns {Range & {marker?}} + * @this {Editor} **/ this.addSelectionMarker = function(orientedRange) { if (!orientedRange.cursor) @@ -335,8 +366,8 @@ var Editor = require("./editor").Editor; /** * Removes the selection marker. - * @param {Range} range The selection range added with [[Editor.addSelectionMarker `addSelectionMarker()`]]. - * @method Editor.removeSelectionMarker + * @param {Range & {marker?}} range The selection range added with [[Editor.addSelectionMarker `addSelectionMarker()`]]. + * @this {Editor} **/ this.removeSelectionMarker = function(range) { if (!range.marker) @@ -348,6 +379,10 @@ var Editor = require("./editor").Editor; this.session.selectionMarkerCount = this.session.$selectionMarkers.length; }; + /** + * @param {(Range & {marker?})[]} ranges + * @this {Editor} + */ this.removeSelectionMarkers = function(ranges) { var markerList = this.session.$selectionMarkers; for (var i = ranges.length; i--; ) { @@ -362,18 +397,30 @@ var Editor = require("./editor").Editor; this.session.selectionMarkerCount = markerList.length; }; + /** + * @param e + * @this {Editor} + */ this.$onAddRange = function(e) { this.addSelectionMarker(e.range); this.renderer.updateCursor(); this.renderer.updateBackMarkers(); }; + /** + * @param e + * @this {Editor} + */ this.$onRemoveRange = function(e) { this.removeSelectionMarkers(e.ranges); this.renderer.updateCursor(); this.renderer.updateBackMarkers(); }; + /** + * @param e + * @this {Editor} + */ this.$onMultiSelect = function(e) { if (this.inMultiSelectMode) return; @@ -387,6 +434,10 @@ var Editor = require("./editor").Editor; this.renderer.updateBackMarkers(); }; + /** + * @param e + * @this {Editor} + */ this.$onSingleSelect = function(e) { if (this.session.multiSelect.inVirtualMode) return; @@ -401,6 +452,10 @@ var Editor = require("./editor").Editor; this._emit("changeSelection"); }; + /** + * @param e + * @this {Editor} + */ this.$onMultiSelectExec = function(e) { var command = e.command; var editor = e.editor; @@ -426,8 +481,9 @@ var Editor = require("./editor").Editor; /** * Executes a command for each selection range. * @param {Object} cmd The command to execute - * @param {String} args Any arguments for the command - * @method Editor.forEachSelection + * @param {String} [args] Any arguments for the command + * @param {Object} [options] + * @this {Editor} **/ this.forEachSelection = function(cmd, args, options) { if (this.inVirtualSelectionMode) @@ -445,7 +501,6 @@ var Editor = require("./editor").Editor; var reg = selection._eventRegistry; selection._eventRegistry = {}; - var tmpSel = new Selection(session); this.inVirtualSelectionMode = true; for (var i = ranges.length; i--;) { @@ -481,7 +536,7 @@ var Editor = require("./editor").Editor; /** * Removes all the selections except the last added one. - * @method Editor.exitMultiSelectMode + * @this {Editor} **/ this.exitMultiSelectMode = function() { if (!this.inMultiSelectMode || this.inVirtualSelectionMode) @@ -489,6 +544,10 @@ var Editor = require("./editor").Editor; this.multiSelect.toSingleRange(); }; + /** + * @this {Editor} + * @return {string} + */ this.getSelectedText = function() { var text = ""; if (this.inMultiSelectMode && !this.inVirtualSelectionMode) { @@ -506,7 +565,12 @@ var Editor = require("./editor").Editor; } return text; }; - + + /** + * @param e + * @param {Anchor} anchor + * @this {Editor} + */ this.$checkMultiselectChange = function(e, anchor) { if (this.inMultiSelectMode && !this.inVirtualSelectionMode) { var range = this.multiSelect.ranges[0]; @@ -525,12 +589,12 @@ var Editor = require("./editor").Editor; /** * Finds and selects all the occurrences of `needle`. - * @param {String} The text to find - * @param {Object} The search options - * @param {Boolean} keeps + * @param {String} [needle] The text to find + * @param {Partial} [options] The search options + * @param {Boolean} [additive] keeps * * @returns {Number} The cumulative count of all found matches - * @method Editor.findAll + * @this {Editor} **/ this.findAll = function(needle, options, additive) { options = options || {}; @@ -566,9 +630,9 @@ var Editor = require("./editor").Editor; * Adds a cursor above or below the active cursor. * * @param {Number} dir The direction of lines to select: -1 for up, 1 for down - * @param {Boolean} skip If `true`, removes the active selection range + * @param {Boolean} [skip] If `true`, removes the active selection range * - * @method Editor.selectMoreLines + * @this {Editor} */ this.selectMoreLines = function(dir, skip) { var range = this.selection.toOrientedRange(); @@ -588,9 +652,11 @@ var Editor = require("./editor").Editor; } if (isBackwards) { + /**@type {Range & {desiredColumn?: number}}*/ var newRange = Range.fromPoints(lead, anchor); newRange.cursor = newRange.start; } else { + /**@type {Range & {desiredColumn?: number}}*/ var newRange = Range.fromPoints(anchor, lead); newRange.cursor = newRange.end; } @@ -611,7 +677,7 @@ var Editor = require("./editor").Editor; /** * Transposes the selected ranges. * @param {Number} dir The direction to rotate selections - * @method Editor.transposeSelections + * @this {Editor} **/ this.transposeSelections = function(dir) { var session = this.session; @@ -621,7 +687,7 @@ var Editor = require("./editor").Editor; for (var i = all.length; i--; ) { var range = all[i]; if (range.isEmpty()) { - var tmp = session.getWordRange(range.start.row, range.start.column); + let tmp = session.getWordRange(range.start.row, range.start.column); range.start.row = tmp.start.row; range.start.column = tmp.start.column; range.end.row = tmp.end.row; @@ -654,8 +720,9 @@ var Editor = require("./editor").Editor; /** * Finds the next occurrence of text in an active selection and adds it to the selections. * @param {Number} dir The direction of lines to select: -1 for up, 1 for down - * @param {Boolean} skip If `true`, removes the active selection range - * @method Editor.selectMore + * @param {Boolean} [skip] If `true`, removes the active selection range + * @param {Boolean} [stopAtFirst] + * @this {Editor} **/ this.selectMore = function(dir, skip, stopAtFirst) { var session = this.session; @@ -684,7 +751,7 @@ var Editor = require("./editor").Editor; /** * Aligns the cursors or selected text. - * @method Editor.alignCursors + * @this {Editor} **/ this.alignCursors = function() { var session = this.session; @@ -762,6 +829,12 @@ var Editor = require("./editor").Editor; } }; + /** + * @param {string[]} lines + * @param {boolean} [forceLeft] + * @return {*} + * @this {Editor} + */ this.$reAlignText = function(lines, forceLeft) { var isLeftAligned = true, isRightAligned = true; var startW, textW, endW; @@ -817,12 +890,18 @@ var Editor = require("./editor").Editor; }).call(Editor.prototype); +/** @param {Point} p1 @param {Point} p2 */ function isSamePoint(p1, p2) { return p1.row == p2.row && p1.column == p2.column; } -// patch -// adds multicursor support to a session + +/** + * patch + * adds multicursor support to a session + * @this {Editor} + * @type {(e) => void} + */ exports.onSessionChange = function(e) { var session = e.session; if (session && !session.multiSelect) { @@ -862,6 +941,9 @@ exports.onSessionChange = function(e) { // MultiSelect(editor) // adds multiple selection support to the editor // (note: should be called only once for each editor instance) +/** + * @param {Editor} editor + */ function MultiSelect(editor) { if (editor.$multiselectOnSessionChange) return; @@ -881,6 +963,9 @@ function MultiSelect(editor) { addAltCursorListeners(editor); } +/** + * @param {Editor} editor + */ function addAltCursorListeners(editor){ if (!editor.textInput) return; var el = editor.textInput.getElement(); @@ -914,6 +999,10 @@ exports.MultiSelect = MultiSelect; require("./config").defineOptions(Editor.prototype, "editor", { enableMultiselect: { + /** + * @param {boolean} val + * @this {Editor} + */ set: function(val) { MultiSelect(this); if (val) { diff --git a/src/occur.js b/src/occur.js index 34c03f34fde..734cd07b86e 100644 --- a/src/occur.js +++ b/src/occur.js @@ -1,4 +1,9 @@ "use strict"; +/** + * @typedef {import("./editor").Editor} Editor + * @typedef {import("../ace-internal").Ace.Point} Point + * @typedef {import("../ace-internal").Ace.SearchOptions} SearchOptions + */ var oop = require("./lib/oop"); var Search = require("./search").Search; @@ -6,12 +11,9 @@ var EditSession = require("./edit_session").EditSession; var SearchHighlight = require("./search_highlight").SearchHighlight; /** - * @class Occur - * * Finds all lines matching a search term in the current [[Document * `Document`]] and displays them instead of the original `Document`. Keeps * track of the mapping between the occur doc and the original doc. - * **/ class Occur extends Search { @@ -53,6 +55,10 @@ class Occur extends Search { return true; } + /** + * @param {EditSession} sess + * @param {RegExp} regexp + */ highlight(sess, regexp) { var hl = sess.$occurHighlight = sess.$occurHighlight || sess.addDynamicMarker( new SearchHighlight(null, "ace_occur-highlight", "text")); @@ -60,11 +66,16 @@ class Occur extends Search { sess._emit("changeBackMarker"); // force highlight layer redraw } + /** + * @param {Editor} editor + * @param {Partial} options + */ displayOccurContent(editor, options) { // this.setSession(session || new EditSession("")) this.$originalSession = editor.session; var found = this.matchingLines(editor.session, options); var lines = found.map(function(foundLine) { return foundLine.content; }); + /**@type {EditSession}*/ var occurSession = new EditSession(lines.join('\n')); occurSession.$occur = this; occurSession.$occurMatchingLines = found; @@ -75,6 +86,9 @@ class Occur extends Search { occurSession._emit('changeBackMarker'); } + /** + * @param {Editor} editor + */ displayOriginalContent(editor) { editor.setSession(this.$originalSession); this.$originalSession.$useEmacsStyleLineStart = this.$useEmacsStyleLineStart; @@ -85,8 +99,8 @@ class Occur extends Search { * the document or the beginning if the doc {row: 0, column: 0} if not * found. * @param {EditSession} session The occur session - * @param {Object} pos The position in the original document - * @return {Object} position in occur doc + * @param {Point} pos The position in the original document + * @return {Point} position in occur doc **/ originalToOccurPosition(session, pos) { var lines = session.$occurMatchingLines; @@ -103,8 +117,8 @@ class Occur extends Search { * Translates the position from the occur document to the original document * or `pos` if not found. * @param {EditSession} session The occur session - * @param {Object} pos The position in the occur session document - * @return {Object} position + * @param {Point} pos The position in the occur session document + * @return {Point} position **/ occurToOriginalPosition(session, pos) { var lines = session.$occurMatchingLines; @@ -113,6 +127,10 @@ class Occur extends Search { return {row: lines[pos.row].row, column: pos.column}; } + /** + * @param {EditSession} session + * @param {Partial} options + */ matchingLines(session, options) { options = oop.mixin({}, options); if (!session || !options.needle) return []; diff --git a/src/placeholder.js b/src/placeholder.js index a90ea2e5f95..a3e84f76e78 100644 --- a/src/placeholder.js +++ b/src/placeholder.js @@ -1,15 +1,17 @@ "use strict"; - +/** + * @typedef {import("./edit_session").EditSession} EditSession + */ var Range = require("./range").Range; var EventEmitter = require("./lib/event_emitter").EventEmitter; var oop = require("./lib/oop"); -class PlaceHolder { +class PlaceHolder { /** - * @param {Document} session The document to associate with the anchor - * @param {Number} length The starting row position - * @param {Number} pos The starting column position - * @param {String} others + * @param {EditSession} session + * @param {Number} length + * @param {import("../ace-internal").Ace.Point} pos + * @param {any[]} others * @param {String} mainClass * @param {String} othersClass **/ @@ -32,7 +34,7 @@ class PlaceHolder { this.$pos = pos; // Used for reset - var undoStack = session.getUndoManager().$undoStack || session.getUndoManager().$undostack || {length: -1}; + var undoStack = session.getUndoManager().$undoStack || session.getUndoManager()["$undostack"] || {length: -1}; this.$undoStackDepth = undoStack.length; this.setup(); @@ -103,8 +105,8 @@ class PlaceHolder { * PlaceHolder@onUpdate(e) * * Emitted when the place holder updates. - * - **/ + * @param {import("../ace-internal").Ace.Delta} delta + */ onUpdate(delta) { if (this.$updating) return this.updateAnchors(delta); @@ -141,7 +143,10 @@ class PlaceHolder { this.$updating = false; this.updateMarkers(); } - + + /** + * @param {import("../ace-internal").Ace.Delta} delta + */ updateAnchors(delta) { this.pos.onChange(delta); for (var i = this.others.length; i--;) @@ -162,14 +167,14 @@ class PlaceHolder { for (var i = this.others.length; i--;) updateMarker(this.others[i], this.othersClass); } - + + /** * PlaceHolder@onCursorChange(e) * * Emitted when the cursor changes. - * - **/ - + * @param {any} [event] + */ onCursorChange(event) { if (this.$updating || !this.session) return; var pos = this.session.selection.getCursor(); @@ -207,7 +212,7 @@ class PlaceHolder { if (this.$undoStackDepth === -1) return; var undoManager = this.session.getUndoManager(); - var undosRequired = (undoManager.$undoStack || undoManager.$undostack).length - this.$undoStackDepth; + var undosRequired = (undoManager.$undoStack || undoManager["$undostack"]).length - this.$undoStackDepth; for (var i = 0; i < undosRequired; i++) { undoManager.undo(this.session, true); } diff --git a/src/range.js b/src/range.js index afa70a6c410..321ad67d2d6 100644 --- a/src/range.js +++ b/src/range.js @@ -1,27 +1,29 @@ "use strict"; -var comparePoints = function(p1, p2) { - return p1.row - p2.row || p1.column - p2.column; -}; + +/** + * @typedef {import("./edit_session").EditSession} EditSession + * @typedef {import("../ace-internal").Ace.IRange} IRange + * @typedef {import("../ace-internal").Ace.Point} Point + */ /** * This object is used in various places to indicate a region within the editor. To better visualize how this works, imagine a rectangle. Each quadrant of the rectangle is analogous to a range, as ranges contain a starting row and starting column, and an ending row, and ending column. - * @class Range **/ - class Range { /** * Creates a new `Range` object with the given starting and ending rows and columns. - * @param {Number} startRow The starting row - * @param {Number} startColumn The starting column - * @param {Number} endRow The ending row - * @param {Number} endColumn The ending column + * @param {Number} [startRow] The starting row + * @param {Number} [startColumn] The starting column + * @param {Number} [endRow] The ending row + * @param {Number} [endColumn] The ending column * @constructor **/ constructor(startRow, startColumn, endRow, endColumn) { + /**@type {Point}*/ this.start = { row: startRow, column: startColumn }; - + /**@type {Point}*/ this.end = { row: endRow, column: endColumn @@ -30,7 +32,7 @@ class Range { /** * Returns `true` if and only if the starting row and column, and ending row and column, are equivalent to those given by `range`. - * @param {Range} range A range to check against + * @param {IRange} range A range to check against * @return {Boolean} **/ isEqual(range) { @@ -70,7 +72,7 @@ class Range { /** * Compares `this` range (A) with another range (B). - * @param {Range} range A range to compare with + * @param {IRange} range A range to compare with * @related [[Range.compare]] * @returns {Number} This method returns one of the following numbers: * * `-2`: (B) is in front of (A), and doesn't intersect with (A) @@ -111,7 +113,7 @@ class Range { /** * Compares the row and column of `p` with the starting and ending [[Point]]'s of the calling range (by calling [[Range.compare]]). - * @param {Ace.Point} p A point to compare with + * @param {Point} p A point to compare with * @related [[Range.compare]] * @returns {Number} **/ @@ -121,7 +123,7 @@ class Range { /** * Checks the start and end [[Point]]'s of `range` and compares them to the calling range. Returns `true` if the `range` is contained within the caller's range. - * @param {Range} range A range to compare with + * @param {IRange} range A range to compare with * @returns {Boolean} * @related [[Range.comparePoint]] **/ @@ -131,7 +133,7 @@ class Range { /** * Returns `true` if passed in `range` intersects with the one calling this method. - * @param {Range} range A range to compare with + * @param {IRange} range A range to compare with * @returns {Boolean} **/ intersects(range) { @@ -161,8 +163,8 @@ class Range { /** * Sets the starting row and column for the range. - * @param {Number|Ace.Point} row A row to set - * @param {Number} column A column to set + * @param {Number|Point} row A row to set + * @param {Number} [column] A column to set * **/ setStart(row, column) { @@ -177,8 +179,8 @@ class Range { /** * Sets the starting row and column for the range. - * @param {Number|Ace.Point} row A row to set - * @param {Number} column A column to set + * @param {Number|Point} row A row to set + * @param {Number} [column] A column to set * **/ setEnd(row, column) { @@ -449,12 +451,11 @@ class Range { Range.fromPoints = function(start, end) { return new Range(start.row, start.column, end.row, end.column); }; -Range.comparePoints = comparePoints; /** * Compares `p1` and `p2` [[Point]]'s, useful for sorting - * @param {Ace.Point} p1 - * @param {Ace.Point} p2 + * @param {Point} p1 + * @param {Point} p2 * @returns {Number} */ Range.comparePoints = function(p1, p2) { diff --git a/src/range_list.js b/src/range_list.js index bc5322caaa5..463aea29a9e 100644 --- a/src/range_list.js +++ b/src/range_list.js @@ -1,4 +1,8 @@ "use strict"; +/** + * @typedef {import("./edit_session").EditSession} EditSession + * @typedef {import("../ace-internal").Ace.Point} Point + */ var Range = require("./range").Range; var comparePoints = Range.comparePoints; @@ -9,6 +13,12 @@ class RangeList { this.$bias = 1; } + /** + * @param {Point} pos + * @param {boolean} [excludeEdges] + * @param {number} [startIndex] + * @return {number} + */ pointIndex(pos, excludeEdges, startIndex) { var list = this.ranges; @@ -28,6 +38,9 @@ class RangeList { return -i - 1; } + /** + * @param {Range} range + */ add(range) { var excludeEdges = !range.isEmpty(); var startIndex = this.pointIndex(range.start, excludeEdges); @@ -43,6 +56,9 @@ class RangeList { return this.ranges.splice(startIndex, endIndex - startIndex, range); } + /** + * @param {Range[]} list + */ addList(list) { var removed = []; for (var i = list.length; i--; ) { @@ -51,6 +67,9 @@ class RangeList { return removed; } + /** + * @param {Point} pos + */ substractPoint(pos) { var i = this.pointIndex(pos); @@ -94,14 +113,24 @@ class RangeList { return removed; } + /** + * @param {number} row + * @param {number} column + */ contains(row, column) { return this.pointIndex({row: row, column: column}) >= 0; } + /** + * @param {Point} pos + */ containsPoint(pos) { return this.pointIndex(pos) >= 0; } + /** + * @param {Point} pos + */ rangeAtPoint(pos) { var i = this.pointIndex(pos); if (i >= 0) @@ -109,6 +138,10 @@ class RangeList { } + /** + * @param {number} startRow + * @param {number} endRow + */ clipRows(startRow, endRow) { var list = this.ranges; if (list[0].start.row > endRow || list[list.length - 1].start.row < startRow) @@ -117,6 +150,7 @@ class RangeList { var startIndex = this.pointIndex({row: startRow, column: 0}); if (startIndex < 0) startIndex = -startIndex - 1; + //@ts-expect-error TODO: potential wrong argument var endIndex = this.pointIndex({row: endRow, column: 0}, startIndex); if (endIndex < 0) endIndex = -endIndex - 1; @@ -132,6 +166,9 @@ class RangeList { return this.ranges.splice(0, this.ranges.length); } + /** + * @param {EditSession} session + */ attach(session) { if (this.session) this.detach(); @@ -149,6 +186,9 @@ class RangeList { this.session = null; } + /** + * @param {import("../ace-internal").Ace.Delta} delta + */ $onChange(delta) { var start = delta.start; var end = delta.end; diff --git a/src/scrollbar.js b/src/scrollbar.js index 1098aaa5b25..70cc2cc02af 100644 --- a/src/scrollbar.js +++ b/src/scrollbar.js @@ -33,6 +33,7 @@ class Scrollbar { this.setVisible(false); this.skipEvent = false; + // @ts-expect-error event.addListener(this.element, "scroll", this.onScroll.bind(this)); event.addListener(this.element, "mousedown", event.preventDefault); } @@ -75,8 +76,8 @@ class VScrollBar extends Scrollbar { /** * Emitted when the scroll bar, well, scrolls. * @event scroll - * @param {Object} e Contains one property, `"data"`, which indicates the current scroll top position **/ + onScroll() { if (!this.skipEvent) { this.scrollTop = this.element.scrollTop; @@ -170,7 +171,6 @@ class HScrollBar extends Scrollbar { /** * Emitted when the scroll bar, well, scrolls. * @event scroll - * @param {Object} e Contains one property, `"data"`, which indicates the current scroll left position **/ onScroll() { if (!this.skipEvent) { diff --git a/src/search.js b/src/search.js index 81672e3d931..abe0de20c33 100644 --- a/src/search.js +++ b/src/search.js @@ -1,5 +1,7 @@ "use strict"; - +/** + * @typedef {import("./edit_session").EditSession} EditSession + */ var lang = require("./lib/lang"); var oop = require("./lib/oop"); var Range = require("./range").Range; @@ -19,7 +21,7 @@ class Search { * @property {boolean} [wholeWord] - Whether the search matches only on whole words * @property {Range|null} [range] - The [[Range]] to search within. Set this to `null` for the whole document * @property {boolean} [regExp] - Whether the search is a regular expression or not - * @property {Range|Position} [start] - The starting [[Range]] or cursor position to begin the search + * @property {Range|import("../ace-internal").Ace.Position} [start] - The starting [[Range]] or cursor position to begin the search * @property {boolean} [skipCurrent] - Whether or not to include the current line in the search * @property {boolean} [$isMultiLine] - true, if needle has \n or \r\n * @property {boolean} [preserveCase] @@ -29,15 +31,13 @@ class Search { **/ constructor() { - /** - * @type {SearchOptions} - */ + /**@type {SearchOptions}*/ this.$options = {}; } /** * Sets the search options via the `options` parameter. - * @param {SearchOptions} options An object containing all the new search properties + * @param {Partial} options An object containing all the new search properties * @returns {Search} * @chainable **/ @@ -48,7 +48,7 @@ class Search { /** * [Returns an object containing all the search options.]{: #Search.getOptions} - * @returns {SearchOptions} + * @returns {Partial} **/ getOptions() { return lang.copyObject(this.$options); @@ -66,7 +66,7 @@ class Search { /** * Searches for `options.needle`. If found, this method returns the [[Range `Range`]] where the text first occurs. If `options.backwards` is `true`, the search goes backwards in the session. * @param {EditSession} session The session to search with - * @returns {Range|boolean} + * @returns {Range|false} **/ find(session) { var options = this.$options; @@ -77,8 +77,8 @@ class Search { var firstRange = null; iterator.forEach(function(sr, sc, er, ec) { firstRange = new Range(sr, sc, er, ec); - if (sc == ec && options.start && options.start.start - && options.skipCurrent != false && firstRange.isEqual(options.start) + if (sc == ec && options.start && /**@type{Range}*/(options.start).start + && options.skipCurrent != false && firstRange.isEqual(/**@type{Range}*/(options.start)) ) { firstRange = null; return false; @@ -167,7 +167,7 @@ class Search { /** * Searches for `options.needle` in `input`, and, if found, replaces it with `replacement`. * @param {String} input The text to search in - * @param {String} replacement The replacing text + * @param {any} replacement The replacing text * + (String): If `options.regExp` is `true`, this function returns `input` with the replacement already made. Otherwise, this function just returns `replacement`.
* If `options.needle` was not found, this function returns `null`. * @@ -187,7 +187,6 @@ class Search { var match = re.exec(input); if (!match || match[0].length != input.length) return null; - replacement = input.replace(re, replacement); if (options.preserveCase) { replacement = replacement.split(""); @@ -207,7 +206,7 @@ class Search { /** * * @param {SearchOptions} options - * @param $disableFakeMultiline + * @param {boolean} [$disableFakeMultiline] * @return {RegExp|boolean|*[]|*} */ $assembleRegExp(options, $disableFakeMultiline) { @@ -240,6 +239,7 @@ class Search { return options.re = this.$assembleMultilineRegExp(needle, modifier); try { + /**@type {RegExp|false}*/ var re = new RegExp(needle, modifier); } catch(e) { re = false; @@ -247,6 +247,10 @@ class Search { return options.re = re; } + /** + * @param {string} needle + * @param {string} modifier + */ $assembleMultilineRegExp(needle, modifier) { var parts = needle.replace(/\r\n|\r|\n/g, "$\n^").split("\n"); var re = []; @@ -258,6 +262,9 @@ class Search { return re; } + /** + * @param {EditSession} session + */ $matchIterator(session, options) { var re = this.$assembleRegExp(options); if (!re) diff --git a/src/search_highlight.js b/src/search_highlight.js index 8c941e82a51..6a5e864c74d 100644 --- a/src/search_highlight.js +++ b/src/search_highlight.js @@ -1,9 +1,16 @@ "use strict"; - +/** + * @typedef {import("./layer/marker").Marker} Marker + * @typedef {import("./edit_session").EditSession} EditSession + */ var lang = require("./lib/lang"); var Range = require("./range").Range; class SearchHighlight { + /** + * @param {any} regExp + * @param {string} clazz + */ constructor(regExp, clazz, type = "text") { this.setRegexp(regExp); this.clazz = clazz; @@ -17,6 +24,12 @@ class SearchHighlight { this.cache = []; } + /** + * @param {any} html + * @param {Marker} markerLayer + * @param {EditSession} session + * @param {Partial} config + */ update(html, markerLayer, session, config) { if (!this.regExp) return; diff --git a/src/selection.js b/src/selection.js index 1400695c2ff..6ab284305af 100644 --- a/src/selection.js +++ b/src/selection.js @@ -4,37 +4,28 @@ var oop = require("./lib/oop"); var lang = require("./lib/lang"); var EventEmitter = require("./lib/event_emitter").EventEmitter; var Range = require("./range").Range; - /** - * Contains the cursor position and the text selection of an edit session. - * - * The row/columns used in the selection are in document coordinates representing the coordinates as they appear in the document before applying soft wrap and folding. - * @class Selection - **/ - + * @typedef {import("./edit_session").EditSession} EditSession + * @typedef {import("./anchor").Anchor} Anchor + * @typedef {import("../ace-internal").Ace.Point} Point + */ -/** - * Emitted when the cursor position changes. - * @event changeCursor - * - **/ -/** - * Emitted when the cursor selection changes. - * - * @event changeSelection - **/ class Selection { /** * Creates a new `Selection` object. * @param {EditSession} session The session to use - * + * @constructor **/ constructor(session) { + /**@type {EditSession}*/ this.session = session; + /**@type {import("./document").Document}*/ this.doc = session.getDocument(); this.clearSelection(); + /**@type {Anchor}*/ this.cursor = this.lead = this.doc.createAnchor(0, 0); + /**@type {Anchor}*/ this.anchor = this.doc.createAnchor(0, 0); this.$silent = false; @@ -77,7 +68,7 @@ class Selection { /** * Returns an object containing the `row` and `column` current position of the cursor. - * @returns {Object} + * @returns {Point} **/ getCursor() { return this.lead.getPosition(); @@ -98,7 +89,7 @@ class Selection { /** * Returns an object containing the `row` and `column` of the calling selection anchor. * - * @returns {Object} + * @returns {Point} * @related Anchor.getPosition **/ getAnchor() { @@ -162,9 +153,8 @@ class Selection { /** * Sets the selection to the provided range. - * @param {Range} range The range of text to select - * @param {Boolean} reverse Indicates if the range should go backwards (`true`) or not - * + * @param {import("../ace-internal").Ace.IRange} range The range of text to select + * @param {Boolean} [reverse] Indicates if the range should go backwards (`true`) or not **/ setRange(range, reverse) { var start = reverse ? range.end : range.start; @@ -172,6 +162,12 @@ class Selection { this.$setSelection(start.row, start.column, end.row, end.column); } + /** + * @param {number} anchorRow + * @param {number} anchorColumn + * @param {number} cursorRow + * @param {number} cursorColumn + */ $setSelection(anchorRow, anchorColumn, cursorRow, cursorColumn) { if (this.$silent) return; @@ -210,7 +206,7 @@ class Selection { /** * Moves the selection cursor to the row and column indicated by `pos`. - * @param {Object} pos An object containing the row and column + * @param {Point} pos An object containing the row and column **/ selectToPosition(pos) { this.$moveSelection(function() { @@ -222,7 +218,6 @@ class Selection { * Moves the selection cursor to the indicated row and column. * @param {Number} row The row to select to * @param {Number} column The column to select to - * **/ moveTo(row, column) { this.clearSelection(); @@ -380,7 +375,7 @@ class Selection { /** * * Returns `true` if moving the character next to the cursor in the specified direction is a soft tab. - * @param {Object} cursor the current cursor position + * @param {Point} cursor the current cursor position * @param {Number} tabSize the tab size * @param {Number} direction 1 for right, -1 for left */ @@ -436,6 +431,9 @@ class Selection { } else { var tabSize = this.session.getTabSize(); + /** + * @type {Point} + */ var cursor = this.lead; if (this.wouldMoveIntoSoftTab(cursor, tabSize, 1) && !this.session.getNavigateWithinSoftTabs()) { this.moveCursorBy(0, tabSize); @@ -750,7 +748,7 @@ class Selection { /** * Moves the selection to the position indicated by its `row` and `column`. - * @param {Object} position The position to move to + * @param {Point} position The position to move to **/ moveCursorToPosition(position) { this.moveCursorTo(position.row, position.column); @@ -760,8 +758,7 @@ class Selection { * Moves the cursor to the row and column provided. [If `preventUpdateDesiredColumn` is `true`, then the cursor stays in the same column position as its original point.]{: #preventUpdateBoolDesc} * @param {Number} row The row to move to * @param {Number} column The column to move to - * @param {Boolean} keepDesiredColumn [If `true`, the cursor move does not respect the previous column]{: #preventUpdateBool} - * + * @param {Boolean} [keepDesiredColumn] [If `true`, the cursor move does not respect the previous column]{: #preventUpdateBool} **/ moveCursorTo(row, column, keepDesiredColumn) { // Ensure the row/column is not inside of a fold. @@ -792,7 +789,6 @@ class Selection { * @param {Number} row The row to move to * @param {Number} column The column to move to * @param {Boolean} keepDesiredColumn {:preventUpdateBool} - * **/ moveCursorToScreen(row, column, keepDesiredColumn) { var pos = this.session.screenToDocumentPosition(row, column); @@ -805,11 +801,17 @@ class Selection { this.anchor.detach(); } + /** + * @param {Range & {desiredColumn?: number}} range + */ fromOrientedRange(range) { this.setSelectionRange(range, range.cursor == range.start); this.$desiredColumn = range.desiredColumn || this.$desiredColumn; } + /** + * @param {Range & {desiredColumn?: number}} [range] + */ toOrientedRange(range) { var r = this.getRange(); if (range) { @@ -830,9 +832,8 @@ class Selection { * Saves the current cursor position and calls `func` that can change the cursor * postion. The result is the range of the starting and eventual cursor position. * Will reset the cursor position. - * @param {Function} The callback that should change the cursor position + * @param {Function} func The callback that should change the cursor position * @returns {Range} - * **/ getRangeOfMovements(func) { var start = this.getCursor(); @@ -847,20 +848,28 @@ class Selection { } } + /** + * + * @returns {Range|Range[]} + */ toJSON() { if (this.rangeCount) { - var data = this.ranges.map(function(r) { + /**@type{Range|Range[]}*/var data = this.ranges.map(function(r) { var r1 = r.clone(); r1.isBackwards = r.cursor == r.start; return r1; }); } else { - var data = this.getRange(); + /**@type{Range|Range[]}*/var data = this.getRange(); data.isBackwards = this.isBackwards(); } return data; } + /** + * + * @param data + */ fromJSON(data) { if (data.start == undefined) { if (this.rangeList && data.length > 1) { @@ -881,6 +890,11 @@ class Selection { this.setSelectionRange(data, data.isBackwards); } + /** + * + * @param data + * @return {boolean} + */ isEqual(data) { if ((data.length || this.rangeCount) && data.length != this.rangeCount) return false; diff --git a/src/snippets.js b/src/snippets.js index def6de3e367..f014395855d 100644 --- a/src/snippets.js +++ b/src/snippets.js @@ -1,4 +1,22 @@ "use strict"; +/** + * @typedef Snippet + * @property {string} [content] + * @property {string} [replaceBefore] + * @property {string} [replaceAfter] + * @property {RegExp} [startRe] + * @property {RegExp} [endRe] + * @property {RegExp} [triggerRe] + * @property {RegExp} [endTriggerRe] + * @property {string} [trigger] + * @property {string} [endTrigger] + * @property {string[]} [matchBefore] + * @property {string[]} [matchAfter] + * @property {string} [name] + * @property {string} [tabTrigger] + * @property {string} [guard] + * @property {string} [endGuard] + */ var dom = require("./lib/dom"); var oop = require("./lib/oop"); var EventEmitter = require("./lib/event_emitter").EventEmitter; @@ -94,9 +112,12 @@ class SnippetManager { this.variables = VARIABLES; } - + + /** + * @return {Tokenizer} + */ getTokenizer() { - return SnippetManager.$tokenizer || this.createTokenizer(); + return SnippetManager["$tokenizer"] || this.createTokenizer(); } createTokenizer() { @@ -121,7 +142,7 @@ class SnippetManager { next: "formatString" }; - SnippetManager.$tokenizer = new Tokenizer({ + SnippetManager["$tokenizer"] = new Tokenizer({ start: [ {regex: /\\./, onMatch: function(val, state, stack) { var ch = val[1]; @@ -217,7 +238,7 @@ class SnippetManager { {regex: "([^:}\\\\]|\\\\.)*:?", token: "", next: "formatString"} ] }); - return SnippetManager.$tokenizer; + return SnippetManager["$tokenizer"]; } tokenizeTmSnippet(str, startState) { @@ -360,6 +381,7 @@ class SnippetManager { var tabstopManager = new TabstopManager(editor); var selectionId = editor.inVirtualSelectionMode && editor.selection.index; + //@ts-expect-error TODO: potential wrong arguments tabstopManager.addTabstops(processedSnippet.tabstops, range.start, end, selectionId); } @@ -430,6 +452,7 @@ class SnippetManager { var after = line.substr(cursor.column); var snippetMap = this.snippetMap; + /**@type {Snippet}*/ var snippet; this.getActiveScopes(editor).some(function(scope) { var snippets = snippetMap[scope]; @@ -454,6 +477,12 @@ class SnippetManager { return true; } + /** + * @param {Snippet[]} snippetList + * @param {string} before + * @param {string} after + * @return {Snippet} + */ findMatchingSnippet(snippetList, before, after) { for (var i = snippetList.length; i--;) { var s = snippetList[i]; @@ -472,6 +501,11 @@ class SnippetManager { } } + + /** + * @param {any[]} snippets + * @param {string} scope + */ register(snippets, scope) { var snippetMap = this.snippetMap; var snippetNameMap = this.snippetNameMap; @@ -548,7 +582,8 @@ class SnippetManager { addSnippet(snippets[key]); }); } - + + // @ts-ignore this._signal("registerSnippets", {scope: scope}); } unregister(snippets, scope) { @@ -572,7 +607,7 @@ class SnippetManager { } parseSnippetFile(str) { str = str.replace(/\r/g, ""); - var list = [], snippet = {}; + var list = [], /**@type{Snippet}*/snippet = {}; var re = /^#.*|^({[\s\S]*})\s*$|^(\S+) (.*)$|^((?:\n*\t.*)+)/gm; var m; while (m = re.exec(str)) { @@ -913,6 +948,7 @@ class TabstopManager { for (var i = 0; i < ts.length; i++) { var p = ts[i]; + /**@type {Range & {original?: Range, tabstop?: any, linked?: boolean}}}*/ var range = Range.fromPoints(p.start, p.end || p.start); movePoint(range.start, start); movePoint(range.end, start); diff --git a/src/split.js b/src/split.js index 0a3aa817ea6..c1a8cbdcb66 100644 --- a/src/split.js +++ b/src/split.js @@ -3,18 +3,20 @@ var oop = require("./lib/oop"); var lang = require("./lib/lang"); var EventEmitter = require("./lib/event_emitter").EventEmitter; - var Editor = require("./editor").Editor; var Renderer = require("./virtual_renderer").VirtualRenderer; var EditSession = require("./edit_session").EditSession; -/** - * @class Split - * - **/ - +/** + * @typedef {import("../ace-internal").Ace.EventEmitter & {[key: string]: any}} ISplit + */ -var Split = function(container, theme, splits) { +var Split; +/** + * @constructor + * @this {ISplit} + */ +Split = function(container, theme, splits) { this.BELOW = 1; this.BESIDE = 0; @@ -38,6 +40,10 @@ var Split = function(container, theme, splits) { oop.implement(this, EventEmitter); + /** + * @returns {Editor} + * @this {ISplit} + */ this.$createEditor = function() { var el = document.createElement("div"); el.className = this.$editorCSS; @@ -54,6 +60,11 @@ var Split = function(container, theme, splits) { return editor; }; + /** + * + * @param splits + * @this {ISplit} + */ this.setSplits = function(splits) { var editor; if (splits < 1) { @@ -87,6 +98,7 @@ var Split = function(container, theme, splits) { * * Returns the number of splits. * @returns {Number} + * @this {ISplit} **/ this.getSplits = function() { return this.$splits; @@ -96,7 +108,7 @@ var Split = function(container, theme, splits) { * @param {Number} idx The index of the editor you want * * Returns the editor identified by the index `idx`. - * + * @this {ISplit} **/ this.getEditor = function(idx) { return this.$editors[idx]; @@ -106,6 +118,7 @@ var Split = function(container, theme, splits) { * * Returns the current editor. * @returns {Editor} + * @this {ISplit} **/ this.getCurrentEditor = function() { return this.$cEditor; @@ -114,6 +127,7 @@ var Split = function(container, theme, splits) { /** * Focuses the current editor. * @related Editor.focus + * @this {ISplit} **/ this.focus = function() { this.$cEditor.focus(); @@ -122,6 +136,7 @@ var Split = function(container, theme, splits) { /** * Blurs the current editor. * @related Editor.blur + * @this {ISplit} **/ this.blur = function() { this.$cEditor.blur(); @@ -133,6 +148,7 @@ var Split = function(container, theme, splits) { * * Sets a theme for each of the available editors. * @related Editor.setTheme + * @this {ISplit} **/ this.setTheme = function(theme) { this.$editors.forEach(function(editor) { @@ -146,6 +162,7 @@ var Split = function(container, theme, splits) { * * Sets the keyboard handler for the editor. * @related editor.setKeyboardHandler + * @this {ISplit} **/ this.setKeyboardHandler = function(keybinding) { this.$editors.forEach(function(editor) { @@ -159,7 +176,7 @@ var Split = function(container, theme, splits) { * @param {String} scope The default scope for the callback * * Executes `callback` on all of the available editors. - * + * @this {ISplit} **/ this.forEach = function(callback, scope) { this.$editors.forEach(callback, scope); @@ -171,7 +188,7 @@ var Split = function(container, theme, splits) { * @param {Number} size The new font size * * Sets the font size, in pixels, for all the available editors. - * + * @this {ISplit} **/ this.setFontSize = function(size) { this.$fontSize = size; @@ -180,6 +197,11 @@ var Split = function(container, theme, splits) { }); }; + /** + * + * @param {EditSession} session + * @return {EditSession} + */ this.$cloneSession = function(session) { var s = new EditSession(session.getDocument(), session.getMode()); @@ -190,6 +212,7 @@ var Split = function(container, theme, splits) { s.setTabSize(session.getTabSize()); s.setUseSoftTabs(session.getUseSoftTabs()); s.setOverwrite(session.getOverwrite()); + // @ts-expect-error TODO: string[] != number[] s.setBreakpoints(session.getBreakpoints()); s.setUseWrapMode(session.getUseWrapMode()); s.setUseWorker(session.getUseWorker()); @@ -207,6 +230,7 @@ var Split = function(container, theme, splits) { * * Sets a new [[EditSession `EditSession`]] for the indicated editor. * @related Editor.setSession + * @this {ISplit} **/ this.setSession = function(session, idx) { var editor; @@ -238,6 +262,7 @@ var Split = function(container, theme, splits) { * * Returns the orientation. * @returns {Number} + * @this {ISplit} **/ this.getOrientation = function() { return this.$orientation; @@ -247,7 +272,7 @@ var Split = function(container, theme, splits) { * * Sets the orientation. * @param {Number} orientation The new orientation value - * + * @this {ISplit} * **/ this.setOrientation = function(orientation) { @@ -260,6 +285,7 @@ var Split = function(container, theme, splits) { /** * Resizes the editor. + * @this {ISplit} **/ this.resize = function() { var width = this.$container.clientWidth; diff --git a/src/token_iterator.js b/src/token_iterator.js index ae1caa48a69..3e3ffee8854 100644 --- a/src/token_iterator.js +++ b/src/token_iterator.js @@ -1,4 +1,7 @@ "use strict"; +/** + * @typedef {import("./edit_session").EditSession} EditSession + */ var Range = require("./range").Range; @@ -23,7 +26,7 @@ class TokenIterator { /** * Moves iterator position to the start of previous token. - * @returns {Token|null} + * @returns {import("../ace-internal").Ace.Token|null} **/ stepBackward() { this.$tokenIndex -= 1; @@ -44,7 +47,7 @@ class TokenIterator { /** * Moves iterator position to the start of next token. - * @returns {Token|null} + * @returns {import("../ace-internal").Ace.Token|null} **/ stepForward() { this.$tokenIndex += 1; @@ -68,7 +71,7 @@ class TokenIterator { /** * * Returns current token. - * @returns {Token} + * @returns {import("../ace-internal").Ace.Token} **/ getCurrentToken() { return this.$rowTokens[this.$tokenIndex]; @@ -108,7 +111,7 @@ class TokenIterator { /** * Return the current token position. - * @returns {Position} + * @returns {import("../ace-internal").Ace.Point} */ getCurrentTokenPosition() { return {row: this.$row, column: this.getCurrentTokenColumn()}; diff --git a/src/tokenizer.js b/src/tokenizer.js index 8b25f1c0613..900651d7ca3 100644 --- a/src/tokenizer.js +++ b/src/tokenizer.js @@ -7,12 +7,13 @@ var MAX_TOKEN_COUNT = 2000; * This class takes a set of highlighting rules, and creates a tokenizer out of them. For more information, see [the wiki on extending highlighters](https://github.com/ajaxorg/ace/wiki/Creating-or-Extending-an-Edit-Mode#wiki-extendingTheHighlighter). **/ class Tokenizer { - /** * Constructs a new tokenizer based on the given rules and flags. * @param {Object} rules The highlighting rules **/ constructor(rules) { + /**@type {RegExp}*/ + this.splitRegex; this.states = rules; this.regExps = {}; @@ -100,13 +101,21 @@ class Tokenizer { this.regExps[key] = new RegExp("(" + ruleRegExps.join(")|(") + ")|($)", flag); } } - + + /** + * @param {number} m + */ $setMaxTokenCount(m) { MAX_TOKEN_COUNT = m | 0; } - + + /** + * @param {string} str + * @return {import("../ace-internal").Ace.Token[]} + */ $applyToken(str) { var values = this.splitRegex.exec(str).slice(1); + //@ts-ignore var types = this.token.apply(this, values); // required for compatibility with old modes @@ -124,6 +133,10 @@ class Tokenizer { return tokens; } + /** + * @param {string} str + * @return {import("../ace-internal").Ace.Token[] | string} + */ $arrayTokens(str) { if (!str) return []; @@ -131,6 +144,7 @@ class Tokenizer { if (!values) return "text"; var tokens = []; + //@ts-ignore var types = this.tokenArray; for (var i = 0, l = types.length; i < l; i++) { if (values[i + 1]) @@ -142,6 +156,10 @@ class Tokenizer { return tokens; } + /** + * @param {string} src + * @returns {string} + */ removeCapturingGroups(src) { var r = src.replace( /\\.|\[(?:\\.|[^\\\]])*|\(\?[:=!<]|(\()/g, @@ -150,6 +168,10 @@ class Tokenizer { return r; } + /** + * @param {string} src + * @param {string} flag + */ createSplitterRegexp(src, flag) { if (src.indexOf("(?=") != -1) { var stack = 0; @@ -191,10 +213,13 @@ class Tokenizer { /** * Returns an object containing two properties: `tokens`, which contains all the tokens; and `state`, the current state. - * @returns {Object} - **/ + * @param {string} line + * @param {string | string[]} startState + * @returns {{tokens:import("../ace-internal").Ace.Token[], state: string|string[]}} + */ getLineTokens(line, startState) { if (startState && typeof startState != "string") { + /**@type {any[]}*/ var stack = startState.slice(0); startState = stack[0]; if (startState === "#tmp") { @@ -204,7 +229,7 @@ class Tokenizer { } else var stack = []; - var currentState = startState || "start"; + var currentState = /**@type{string}*/(startState) || "start"; var state = this.states[currentState]; if (!state) { currentState = "start"; diff --git a/src/tokenizer_dev.js b/src/tokenizer_dev.js index 62f6bc62f97..a005882bd9c 100644 --- a/src/tokenizer_dev.js +++ b/src/tokenizer_dev.js @@ -16,6 +16,7 @@ class Tokenizer extends BaseTokenizer { **/ getLineTokens(line, startState) { if (startState && typeof startState != "string") { + /**@type {any[]}*/ var stack = startState.slice(0); startState = stack[0]; } else @@ -40,7 +41,8 @@ class Tokenizer extends BaseTokenizer { stateTransitions = []; onStateChange(); } - + + /**@type {any}*/ var token = { type: null, value: "", diff --git a/src/tooltip.js b/src/tooltip.js index cc720f73d97..da6badf4092 100644 --- a/src/tooltip.js +++ b/src/tooltip.js @@ -1,4 +1,9 @@ "use strict"; +/** + * @typedef {import("./editor").Editor} Editor + * @typedef {import("./mouse/mouse_event").MouseEvent} MouseEvent + * @typedef {import("./edit_session").EditSession} EditSession + */ var dom = require("./lib/dom"); var event = require("./lib/event"); @@ -61,15 +66,18 @@ class Tooltip { dom.addCssClass(this.getElement(), className); } + /** + * @param {import("../ace-internal").Ace.Theme} theme + */ setTheme(theme) { this.$element.className = CLASSNAME + " " + (theme.isDark? "ace_dark " : "") + (theme.cssClass || ""); } /** - * @param {String} text - * @param {Number} x - * @param {Number} y + * @param {String} [text] + * @param {Number} [x] + * @param {Number} [y] **/ show(text, x, y) { if (text != null) @@ -82,7 +90,7 @@ class Tooltip { } } - hide() { + hide(e) { if (this.isOpen) { this.getElement().style.display = "none"; this.getElement().className = CLASSNAME; @@ -115,14 +123,21 @@ class Tooltip { class PopupManager { constructor () { + /**@type{Tooltip[]} */ this.popups = []; } - + + /** + * @param {Tooltip} popup + */ addPopup(popup) { this.popups.push(popup); this.updatePopups(); } + /** + * @param {Tooltip} popup + */ removePopup(popup) { const index = this.popups.indexOf(popup); if (index !== -1) { @@ -132,6 +147,7 @@ class PopupManager { } updatePopups() { + // @ts-expect-error TODO: could be actually an error this.popups.sort((a, b) => b.priority - a.priority); let visiblepopups = []; @@ -152,6 +168,11 @@ class PopupManager { } } + /** + * @param {Tooltip} popupA + * @param {Tooltip} popupB + * @return {boolean} + */ doPopupsOverlap (popupA, popupB) { const rectA = popupA.getElement().getBoundingClientRect(); const rectB = popupB.getElement().getBoundingClientRect(); @@ -193,13 +214,19 @@ class HoverTooltip extends Tooltip { el.addEventListener("wheel", event.stopPropagation); } - + + /** + * @param {Editor} editor + */ addToEditor(editor) { editor.on("mousemove", this.onMouseMove); editor.on("mousedown", this.hide); editor.renderer.getMouseEventTarget().addEventListener("mouseout", this.onMouseOut, true); } + /** + * @param {Editor} editor + */ removeFromEditor(editor) { editor.off("mousemove", this.onMouseMove); editor.off("mousedown", this.hide); @@ -210,6 +237,10 @@ class HoverTooltip extends Tooltip { } } + /** + * @param {MouseEvent} e + * @param {Editor} editor + */ onMouseMove(e, editor) { this.lastEvent = e; this.lastT = Date.now(); @@ -243,6 +274,9 @@ class HoverTooltip extends Tooltip { } } + /** + * @param {MouseEvent} e + */ isOutsideOfText(e) { var editor = e.editor; var docPos = e.getDocumentPosition(); @@ -259,11 +293,20 @@ class HoverTooltip extends Tooltip { } return false; } - + + /** + * @param {any} value + */ setDataProvider(value) { this.$gatherData = value; } - + + /** + * @param {Editor} editor + * @param {Range} range + * @param {any} domNode + * @param {MouseEvent} startingEvent + */ showForRange(editor, range, domNode, startingEvent) { var MARGIN = 10; if (startingEvent && startingEvent != this.lastEvent) return; @@ -311,7 +354,11 @@ class HoverTooltip extends Tooltip { // try to align tooltip left with the range, but keep it on screen element.style.left = Math.min(position.pageX, window.innerWidth - labelWidth - MARGIN) + "px"; } - + + /** + * @param {Range} range + * @param {EditSession} [session] + */ addMarker(range, session) { if (this.marker) { this.$markerSession.removeMarker(this.marker); @@ -364,4 +411,4 @@ class HoverTooltip extends Tooltip { } } -exports.HoverTooltip = HoverTooltip; \ No newline at end of file +exports.HoverTooltip = HoverTooltip; diff --git a/src/undomanager.js b/src/undomanager.js index e1874fe419d..92c40fd3800 100644 --- a/src/undomanager.js +++ b/src/undomanager.js @@ -1,20 +1,31 @@ "use strict"; +/** + * @typedef {import("./edit_session").EditSession} EditSession + * @typedef {import("../ace-internal").Ace.Delta} Delta + * @typedef {import("../ace-internal").Ace.Point} Point + * @typedef {import("../ace-internal").Ace.IRange} IRange + */ /** * This object maintains the undo stack for an [[EditSession `EditSession`]]. **/ class UndoManager { - /** * Resets the current undo state and creates a new `UndoManager`. **/ constructor() { + /**@type {boolean}*/ + this.$keepRedoStack; this.$maxRev = 0; this.$fromUndo = false; this.$undoDepth = Infinity; this.reset(); } - + + /** + * + * @param {EditSession} session + */ addSession(session) { this.$session = session; } @@ -24,8 +35,9 @@ class UndoManager { * - `args[0]` is an array of deltas * - `args[1]` is the document to associate with * - * @param {Object} options Contains additional properties - * + * @param {import("../ace-internal").Ace.Delta} delta + * @param {boolean} allowMerge + * @param {EditSession} [session] **/ add(delta, allowMerge, session) { if (this.$fromUndo) return; @@ -44,7 +56,12 @@ class UndoManager { this.$lastDelta = delta; this.lastDeltas.push(delta); } - + + /** + * + * @param {any} selection + * @param {number} [rev] + */ addSelection(selection, rev) { this.selections.push({ value: selection, @@ -56,7 +73,12 @@ class UndoManager { this.lastDeltas = null; return this.$rev; } - + + /** + * + * @param {number} from + * @param {number} [to] + */ markIgnored(from, to) { if (to == null) to = this.$rev + 1; var stack = this.$undoStack; @@ -69,7 +91,13 @@ class UndoManager { } this.lastDeltas = null; } - + + /** + * + * @param {number} rev + * @param {boolean} [after] + * @return {{ value: string, rev: number }} + */ getSelection(rev, after) { var stack = this.selections; for (var i = stack.length; i--;) { @@ -81,11 +109,20 @@ class UndoManager { } } } - + + /** + * @return {number} + */ getRevision() { return this.$rev; } - + + /** + * + * @param {number} from + * @param {number} [to] + * @return {import("../ace-internal").Ace.Delta[]} + */ getDeltas(from, to) { if (to == null) to = this.$rev + 1; var stack = this.$undoStack; @@ -101,12 +138,21 @@ class UndoManager { } return stack.slice(start, end); } - + + /** + * + * @param {number} from + * @param {number} [to] + */ getChangedRanges(from, to) { if (to == null) to = this.$rev + 1; - } - + + /** + * + * @param {number} from + * @param {number} [to] + */ getChangedLines(from, to) { if (to == null) to = this.$rev + 1; @@ -115,9 +161,7 @@ class UndoManager { /** * [Perform an undo operation on the document, reverting the last change.]{: #UndoManager.undo} * @param {EditSession} session - * @param {Boolean} dontSelect {:dontSelect} - * - * @returns {Range} The range of the undo. + * @param {Boolean} [dontSelect] {:dontSelect} **/ undo(session, dontSelect) { this.lastDeltas = null; @@ -149,7 +193,8 @@ class UndoManager { /** * [Perform a redo operation on the document, reimplementing the last change.]{: #UndoManager.redo} - * @param {Boolean} dontSelect {:dontSelect} + * @param {EditSession} session + * @param {Boolean} [dontSelect] {:dontSelect} * **/ redo(session, dontSelect) { @@ -218,10 +263,11 @@ class UndoManager { canRedo() { return this.$redoStack.length > 0; } - + /** * Marks the current status clean - **/ + * @param {number} [rev] + */ bookmark(rev) { if (rev == undefined) rev = this.$rev; @@ -261,18 +307,26 @@ class UndoManager { this.$redoStack = json.$redoStack; } - + + /** + * @param {Delta} delta + */ $prettyPrint(delta) { if (delta) return stringifyDelta(delta); return stringifyDelta(this.$undoStack) + "\n---\n" + stringifyDelta(this.$redoStack); } } + UndoManager.prototype.hasUndo = UndoManager.prototype.canUndo; UndoManager.prototype.hasRedo = UndoManager.prototype.canRedo; UndoManager.prototype.isClean = UndoManager.prototype.isAtBookmark; UndoManager.prototype.markClean = UndoManager.prototype.bookmark; +/** + * @param {any[]} stack + * @param {number} pos + */ function rearrangeUndoStack(stack, pos) { for (var i = pos; i--; ) { var deltaSet = stack[i]; @@ -292,6 +346,9 @@ var Range = require("./range").Range; var cmp = Range.comparePoints; var comparePoints = Range.comparePoints; +/** + * @param {Delta} delta + */ function $updateMarkers(delta) { var isInsert = delta.action == "insert"; var start = delta.start; @@ -332,9 +389,16 @@ function $updateMarkers(delta) { } } +/** + * @param {Point} pos + */ function clonePos(pos) { return {row: pos.row,column: pos.column}; } + +/** + * @param {Delta} d + */ function cloneDelta(d) { return { start: clonePos(d.start), @@ -367,6 +431,11 @@ function stringifyDelta(d) { } return type; } + +/** + * @param {Range} r + * @return {string} + */ function stringifyRange(r) { return r.start.row + ":" + r.start.column + "=>" + r.end.row + ":" + r.end.column; @@ -393,6 +462,10 @@ function stringifyRange(r) { * d2.s < d1.s < d2.e // can split */ +/** + * @param {Delta} d1 + * @param {Delta} d2 + */ function swap(d1, d2) { var i1 = d1.action == "insert"; var i2 = d2.action == "insert"; @@ -460,6 +533,11 @@ function swapGroups(ds1, ds2) { o<---o c1 */ +/** + * + * @param {Delta} d1 + * @param {Delta} c1 + */ function xform(d1, c1) { var i1 = d1.action == "insert"; var i2 = c1.action == "insert"; @@ -516,17 +594,38 @@ function xform(d1, c1) { } return [c1, d1]; } - + +/** + * + * @param {IRange} d1 + * @param {IRange} d2 + * @param {number} dir + */ function shift(d1, d2, dir) { shiftPos(d1.start, d2.start, d2.end, dir); shiftPos(d1.end, d2.start, d2.end, dir); } + +/** + * + * @param {Point} pos + * @param {Point} start + * @param {Point} end + * @param {number} dir + */ function shiftPos(pos, start, end, dir) { if (pos.row == (dir == 1 ? start : end).row) { pos.column += dir * (end.column - start.column); } pos.row += dir * (end.row - start.row); } + +/** + * + * @param {Delta} c + * @param {Point} pos + * @return {Delta} + */ function splitDelta(c, pos) { var lines = c.lines; var end = c.end; @@ -546,6 +645,10 @@ function splitDelta(c, pos) { return rest; } +/** + * @param {any[]} redoStack + * @param {Delta} d + */ function moveDeltasByOne(redoStack, d) { d = cloneDelta(d); for (var j = redoStack.length; j--;) { @@ -570,6 +673,7 @@ function moveDeltasByOne(redoStack, d) { } return redoStack; } + function rebaseRedoStack(redoStack, deltaSets) { for (var i = 0; i < deltaSets.length; i++) { var deltas = deltaSets[i]; diff --git a/src/virtual_renderer.js b/src/virtual_renderer.js index 916b800a27a..4ef7553caac 100644 --- a/src/virtual_renderer.js +++ b/src/virtual_renderer.js @@ -1,5 +1,9 @@ "use strict"; - +/** + * @typedef {import("./edit_session").EditSession} EditSession + * @typedef {import("../ace-internal").Ace.Point} Point + * @typedef {import("../ace-internal").Ace.Theme} Theme + */ var oop = require("./lib/oop"); var dom = require("./lib/dom"); var lang = require("./lib/lang"); @@ -29,12 +33,12 @@ dom.importCssString(editorCss, "ace_editor.css", false); class VirtualRenderer { /** * Constructs a new `VirtualRenderer` within the `container` specified, applying the given `theme`. - * @param {Element} container The root element of the editor + * @param {HTMLElement | null} [container] The root element of the editor * @param {String} [theme] The starting theme + **/ constructor(container, theme) { var _self = this; - this.container = container || dom.createElement("div"); dom.addCssClass(this.container, "ace_editor"); @@ -47,13 +51,13 @@ class VirtualRenderer { this.$gutter = dom.createElement("div"); this.$gutter.className = "ace_gutter"; this.container.appendChild(this.$gutter); - this.$gutter.setAttribute("aria-hidden", true); - + this.$gutter.setAttribute("aria-hidden", "true"); + /**@type {HTMLElement}*/ this.scroller = dom.createElement("div"); this.scroller.className = "ace_scroller"; this.container.appendChild(this.scroller); - + /**@type {HTMLElement}*/ this.content = dom.createElement("div"); this.content.className = "ace_content"; this.scroller.appendChild(this.content); @@ -62,7 +66,6 @@ class VirtualRenderer { this.$gutterLayer.on("changeGutterWidth", this.onGutterResize.bind(this)); this.$markerBack = new MarkerLayer(this.content); - var textLayer = this.$textLayer = new TextLayer(this.content); this.canvas = textLayer.element; @@ -176,7 +179,9 @@ class VirtualRenderer { // }; updateCharacterSize() { + // @ts-expect-error TODO: missing property initialization anywhere in codebase if (this.$textLayer.allowBoldFonts != this.$allowBoldFonts) { + // @ts-expect-error TODO: missing property initialization anywhere in codebase this.$allowBoldFonts = this.$textLayer.allowBoldFonts; this.setStyle("ace_nobold", !this.$allowBoldFonts); } @@ -193,6 +198,7 @@ class VirtualRenderer { /** * * Associates the renderer with an [[EditSession `EditSession`]]. + * @param {EditSession} session The session to associate with **/ setSession(session) { if (this.session) @@ -223,7 +229,7 @@ class VirtualRenderer { * Triggers a partial update of the text, from the range given by the two parameters. * @param {Number} firstRow The first row to update * @param {Number} lastRow The last row to update - * + * @param {boolean} [force] **/ updateLines(firstRow, lastRow, force) { if (lastRow === undefined) @@ -278,7 +284,8 @@ class VirtualRenderer { /** * Triggers a full update of all the layers, for all the rows. - * @param {Boolean} force If `true`, forces the changes through + * @param {Boolean} [force] If `true`, forces the changes through + **/ updateFull(force) { if (force) @@ -302,11 +309,11 @@ class VirtualRenderer { } /** * [Triggers a resize of the editor.]{: #VirtualRenderer.onResize} - * @param {Boolean} force If `true`, recomputes the size, even if the height and width haven't changed - * @param {Number} gutterWidth The width of the gutter in pixels - * @param {Number} width The width of the editor in pixels - * @param {Number} height The hiehgt of the editor, in pixels - * + * @param {Boolean} [force] If `true`, recomputes the size, even if the height and width haven't changed + * @param {Number} [gutterWidth] The width of the gutter in pixels + * @param {Number} [width] The width of the editor in pixels + * @param {Number} [height] The hiehgt of the editor, in pixels + **/ onResize(force, gutterWidth, width, height) { if (this.resizing > 2) @@ -346,7 +353,15 @@ class VirtualRenderer { this.$updateCustomScrollbar(true); } } - + + /** + * @param [force] + * @param [gutterWidth] + * @param [width] + * @param [height] + * @return {number} + + */ $updateCachedSize(force, gutterWidth, width, height) { height -= (this.$extraHeight || 0); var changes = 0; @@ -405,6 +420,11 @@ class VirtualRenderer { return changes; } + /** + * + * @param {number} width + + */ onGutterResize(width) { var gutterWidth = this.$showGutter ? width : 0; if (gutterWidth != this.gutterWidth) @@ -421,6 +441,7 @@ class VirtualRenderer { /** * Adjusts the wrap limit, which is the number of characters that can fit within the width of the edit area on screen. + **/ adjustWrapLimit() { var availableWidth = this.$size.scrollerWidth - this.$padding * 2; @@ -431,7 +452,7 @@ class VirtualRenderer { /** * Identifies whether you want to have an animated scroll or not. * @param {Boolean} shouldAnimate Set to `true` to show animated scrolls - * + **/ setAnimatedScroll(shouldAnimate){ this.setOption("animatedScroll", shouldAnimate); @@ -440,6 +461,7 @@ class VirtualRenderer { /** * Returns whether an animated scroll happens or not. * @returns {Boolean} + **/ getAnimatedScroll() { return this.$animatedScroll; @@ -448,7 +470,7 @@ class VirtualRenderer { /** * Identifies whether you want to show invisible characters or not. * @param {Boolean} showInvisibles Set to `true` to show invisibles - * + **/ setShowInvisibles(showInvisibles) { this.setOption("showInvisibles", showInvisibles); @@ -458,22 +480,40 @@ class VirtualRenderer { /** * Returns whether invisible characters are being shown or not. * @returns {Boolean} + **/ getShowInvisibles() { return this.getOption("showInvisibles"); } + + /** + * @return {boolean} + + */ getDisplayIndentGuides() { return this.getOption("displayIndentGuides"); } + /** + * @param {boolean} display + + */ setDisplayIndentGuides(display) { this.setOption("displayIndentGuides", display); } + /** + + * @return {boolean} + */ getHighlightIndentGuides() { return this.getOption("highlightIndentGuides"); } + /** + + * @param {boolean} highlight + */ setHighlightIndentGuides(highlight) { this.setOption("highlightIndentGuides", highlight); } @@ -481,7 +521,7 @@ class VirtualRenderer { /** * Identifies whether you want to show the print margin or not. * @param {Boolean} showPrintMargin Set to `true` to show the print margin - * + **/ setShowPrintMargin(showPrintMargin) { this.setOption("showPrintMargin", showPrintMargin); @@ -490,22 +530,24 @@ class VirtualRenderer { /** * Returns whether the print margin is being shown or not. * @returns {Boolean} + **/ getShowPrintMargin() { return this.getOption("showPrintMargin"); } /** * Identifies whether you want to show the print margin column or not. - * @param {Boolean} showPrintMargin Set to `true` to show the print margin column - * + * @param {number} printMarginColumn Set to `true` to show the print margin column + **/ - setPrintMarginColumn(showPrintMargin) { - this.setOption("printMarginColumn", showPrintMargin); + setPrintMarginColumn(printMarginColumn) { + this.setOption("printMarginColumn", printMarginColumn); } /** * Returns whether the print margin column is being shown or not. - * @returns {Boolean} + * @returns {number} + **/ getPrintMarginColumn() { return this.getOption("printMarginColumn"); @@ -514,6 +556,7 @@ class VirtualRenderer { /** * Returns `true` if the gutter is being shown. * @returns {Boolean} + **/ getShowGutter(){ return this.getOption("showGutter"); @@ -522,28 +565,47 @@ class VirtualRenderer { /** * Identifies whether you want to show the gutter or not. * @param {Boolean} show Set to `true` to show the gutter - * + **/ setShowGutter(show){ return this.setOption("showGutter", show); } + /** + + * @returns {boolean} + */ getFadeFoldWidgets(){ return this.getOption("fadeFoldWidgets"); } + /** + + * @param {boolean} show + */ setFadeFoldWidgets(show) { this.setOption("fadeFoldWidgets", show); } + /** + * + * @param {boolean} shouldHighlight + */ setHighlightGutterLine(shouldHighlight) { this.setOption("highlightGutterLine", shouldHighlight); } + /** + + * @returns {boolean} + */ getHighlightGutterLine() { return this.getOption("highlightGutterLine"); } + /** + + */ $updatePrintMargin() { if (!this.$showPrintMargin && !this.$printMarginEl) return; @@ -568,7 +630,7 @@ class VirtualRenderer { /** * * Returns the root element containing this renderer. - * @returns {Element} + * @returns {HTMLElement} **/ getContainerElement() { return this.container; @@ -577,7 +639,7 @@ class VirtualRenderer { /** * * Returns the element that the mouse events are attached to - * @returns {Element} + * @returns {HTMLElement} **/ getMouseEventTarget() { return this.scroller; @@ -586,7 +648,7 @@ class VirtualRenderer { /** * * Returns the element to which the hidden text area is added. - * @returns {Element} + * @returns {HTMLElement} **/ getTextAreaContainer() { return this.container; @@ -594,6 +656,9 @@ class VirtualRenderer { // move text input over the cursor // this is required for IME + /** + + */ $moveTextAreaToCursor() { if (this.$isMousePressed) return; var style = this.textarea.style; @@ -688,7 +753,7 @@ class VirtualRenderer { /** * Sets the padding for all the layers. * @param {Number} padding A new padding value (in pixels) - * + **/ setPadding(padding) { this.$padding = padding; @@ -699,7 +764,15 @@ class VirtualRenderer { this.$loop.schedule(this.CHANGE_FULL); this.$updatePrintMargin(); } - + + /** + * + * @param {number} [top] + * @param {number} [bottom] + * @param {number} [left] + * @param {number} [right] + + */ setScrollMargin(top, bottom, left, right) { var sm = this.scrollMargin; sm.top = top|0; @@ -712,7 +785,15 @@ class VirtualRenderer { this.session.setScrollTop(-sm.top); this.updateFull(); } - + + /** + * + * @param {number} [top] + * @param {number} [bottom] + * @param {number} [left] + * @param {number} [right] + + */ setMargin(top, bottom, left, right) { var sm = this.margin; sm.top = top|0; @@ -728,6 +809,7 @@ class VirtualRenderer { /** * Returns whether the horizontal scrollbar is set to be always visible. * @returns {Boolean} + **/ getHScrollBarAlwaysVisible() { return this.$hScrollBarAlwaysVisible; @@ -736,6 +818,7 @@ class VirtualRenderer { /** * Identifies whether you want to show the horizontal scrollbar or not. * @param {Boolean} alwaysVisible Set to `true` to make the horizontal scroll bar visible + **/ setHScrollBarAlwaysVisible(alwaysVisible) { this.setOption("hScrollBarAlwaysVisible", alwaysVisible); @@ -743,6 +826,7 @@ class VirtualRenderer { /** * Returns whether the horizontal scrollbar is set to be always visible. * @returns {Boolean} + **/ getVScrollBarAlwaysVisible() { return this.$vScrollBarAlwaysVisible; @@ -756,6 +840,9 @@ class VirtualRenderer { this.setOption("vScrollBarAlwaysVisible", alwaysVisible); } + /** + + */ $updateScrollBarV() { var scrollHeight = this.layerConfig.maxHeight; var scrollerHeight = this.$size.scrollerHeight; @@ -782,6 +869,13 @@ class VirtualRenderer { this.$frozen = false; } + /** + * + * @param {number} changes + * @param {boolean} [force] + * @returns {number} + + */ $renderChanges(changes, force) { if (this.$changes) { changes |= this.$changes; @@ -919,6 +1013,7 @@ class VirtualRenderer { } else if (changes & this.CHANGE_CURSOR) { if (this.$highlightGutterLine) + // @ts-expect-error TODO: potential wrong param this.$gutterLayer.updateLineHighlight(config); if (this.$customScrollbar) { this.$scrollDecorator.$updateDecorators(config); @@ -941,7 +1036,9 @@ class VirtualRenderer { this._signal("afterRender", changes); } - + /** + + */ $autosize() { var height = this.session.getScreenLength() * this.lineHeight; var maxHeight = this.$maxLines * this.lineHeight; @@ -972,7 +1069,11 @@ class VirtualRenderer { this._signal("autosize"); } } - + + /** + + * @returns {number} + */ $computeLayerConfig() { var session = this.session; var size = this.$size; @@ -1079,6 +1180,10 @@ class VirtualRenderer { return changes; } + /** + * @returns {boolean | undefined} + + */ $updateLines() { if (!this.$changedLines) return; var firstRow = this.$changedLines.firstRow; @@ -1103,6 +1208,11 @@ class VirtualRenderer { return true; } + /** + * + * @returns {number} + + */ $getLongestLine() { var charCount = this.session.getScreenWidth(); if (this.showInvisibles && !this.session.$useWrapMode) @@ -1149,16 +1259,18 @@ class VirtualRenderer { } /** - * + * * Redraw breakpoints. - **/ + * @param {any} [rows] + */ updateBreakpoints(rows) { + this._rows = rows; this.$loop.schedule(this.CHANGE_GUTTER); } /** * Sets annotations for the gutter. - * @param {Annotation[]} annotations An array containing annotations + * @param {import("../ace-internal").Ace.Annotation[]} annotations An array containing annotations * **/ setAnnotations(annotations) { @@ -1190,6 +1302,12 @@ class VirtualRenderer { this.$cursorLayer.showCursor(); } + /** + * + * @param {Point} anchor + * @param {Point} lead + * @param {number} [offset] + */ scrollSelectionIntoView(anchor, lead, offset) { // first scroll anchor into view then scroll lead into view this.scrollCursorIntoView(anchor, offset); @@ -1197,9 +1315,12 @@ class VirtualRenderer { } /** - * + * * Scrolls the cursor into the first visibile area of the editor - **/ + * @param {Point} [cursor] + * @param {number} [offset] + * @param {{ top?: any; bottom?: any; }} [$viewMargin] + */ scrollCursorIntoView(cursor, offset, $viewMargin) { // the editor is not visible if (this.$size.scrollerHeight === 0) @@ -1295,6 +1416,12 @@ class VirtualRenderer { this.session.setScrollTop(row * this.lineHeight); } + /** + * + * @param {Point} cursor + * @param {number} [alignment] + * @returns {number} + */ alignCursor(cursor, alignment) { if (typeof cursor == "number") cursor = {row: cursor, column: 0}; @@ -1307,6 +1434,12 @@ class VirtualRenderer { return offset; } + /** + * + * @param {number} fromValue + * @param {number} toValue + * @returns {*[]} + */ $calcSteps(fromValue, toValue){ var i = 0; var l = this.STEPS; @@ -1327,8 +1460,8 @@ class VirtualRenderer { * @param {Number} line A line number * @param {Boolean} center If `true`, centers the editor the to indicated line * @param {Boolean} animate If `true` animates scrolling - * @param {Function} callback Function to be called after the animation has finished - * + * @param {() => void} [callback] Function to be called after the animation has finished + **/ scrollToLine(line, center, animate, callback) { var pos = this.$cursorLayer.getPixelPosition({row: line, column: 0}); @@ -1342,6 +1475,12 @@ class VirtualRenderer { this.animateScrolling(initialScroll, callback); } + /** + * + * @param fromValue + * @param [callback] + + */ animateScrolling(fromValue, callback) { var toValue = this.scrollTop; if (!this.$animatedScroll) @@ -1370,6 +1509,7 @@ class VirtualRenderer { _self.session.$scrollTop = toValue; function endAnimation() { + // @ts-ignore _self.$timer = clearInterval(_self.$timer); _self.$scrollAnimation = null; _self.$stopAnimation = false; @@ -1401,8 +1541,6 @@ class VirtualRenderer { /** * Scrolls the editor to the y pixel indicated. * @param {Number} scrollTop The position to scroll to - * - * @returns {Number} **/ scrollToY(scrollTop) { // after calling scrollBar.setScrollTop @@ -1416,8 +1554,6 @@ class VirtualRenderer { /** * Scrolls the editor across the x-axis to the pixel indicated. * @param {Number} scrollLeft The position to scroll to - * - * @returns {Number} **/ scrollToX(scrollLeft) { if (this.scrollLeft !== scrollLeft) @@ -1465,6 +1601,13 @@ class VirtualRenderer { return true; } + /** + * + * @param {number} x + * @param {number} y + * @returns {import("../ace-internal").Ace.ScreenCoordinates} + + */ pixelToScreenCoordinates(x, y) { var canvasPos; if (this.$hasCssTransforms) { @@ -1484,6 +1627,13 @@ class VirtualRenderer { return {row: row, column: col, side: offset - col > 0 ? 1 : -1, offsetX: offsetX}; } + /** + * + * @param {number} x + * @param {number} y + * @returns {Point} + + */ screenToTextCoordinates(x, y) { var canvasPos; if (this.$hasCssTransforms) { @@ -1509,7 +1659,7 @@ class VirtualRenderer { * @param {Number} row The document row position * @param {Number} column The document column position * - * @returns {Object} + * @returns {{ pageX: number, pageY: number}} **/ textToScreenCoordinates(row, column) { var canvasPos = this.scroller.getBoundingClientRect(); @@ -1545,8 +1695,7 @@ class VirtualRenderer { /** * @param {Object} composition - * - * @private + **/ showComposition(composition) { this.$composition = composition; @@ -1571,6 +1720,7 @@ class VirtualRenderer { * @param {String} text A string of text to use * * Sets the inner text of the current composition to `text`. + **/ setCompositionText(text) { var cursor = this.session.selection.cursor; @@ -1581,6 +1731,7 @@ class VirtualRenderer { /** * * Hides the current composition. + **/ hideComposition() { if (!this.$composition) @@ -1597,6 +1748,10 @@ class VirtualRenderer { this.$cursorLayer.element.style.display = ""; } + /** + * @param {string} text + * @param {Point} [position] + */ setGhostText(text, position) { var cursor = this.session.selection.cursor; var insertPosition = position || { row: cursor.row, column: cursor.column }; @@ -1655,6 +1810,12 @@ class VirtualRenderer { this.$ghostText = null; } + /** + * @param {string} text + * @param {string} type + * @param {number} row + * @param {number} [column] + */ addToken(text, type, row, column) { var session = this.session; session.bgTokenizer.lines[row] = null; @@ -1687,22 +1848,27 @@ class VirtualRenderer { /** * [Sets a new theme for the editor. `theme` should exist, and be a directory path, like `ace/theme/textmate`.]{: #VirtualRenderer.setTheme} - * @param {String} [theme] The path to a theme - * @param {Function} [cb] optional callback - * + * @param {String | Theme} [theme] The path to a theme + * @param {() => void} [cb] optional callback + **/ setTheme(theme, cb) { var _self = this; + /**@type {any}*/ this.$themeId = theme; _self._dispatchEvent('themeChange',{theme:theme}); if (!theme || typeof theme == "string") { + // @ts-ignore var moduleName = theme || this.$options.theme.initialValue; config.loadModule(["theme", moduleName], afterLoad); } else { afterLoad(theme); } + /** + * @param {Theme} module + */ function afterLoad(module) { if (_self.$themeId != theme) return cb && cb(); @@ -1715,10 +1881,9 @@ class VirtualRenderer { module.cssClass, _self.container ); - if (_self.theme) dom.removeCssClass(_self.container, _self.theme.cssClass); - + /**@type {any}*/ var padding = "padding" in module ? module.padding : "padding" in (_self.theme || {}) ? 4 : _self.$padding; if (_self.$padding && padding != _self.$padding) @@ -1757,7 +1922,7 @@ class VirtualRenderer { /** * [Adds a new class, `style`, to the editor.]{: #VirtualRenderer.setStyle} * @param {String} style A class name - * + * @param {boolean}[include] **/ setStyle(style, include) { dom.setCssClass(this.container, style, include !== false); @@ -1771,14 +1936,16 @@ class VirtualRenderer { unsetStyle(style) { dom.removeCssClass(this.container, style); } - + + /** + * @param {string} style + */ setCursorStyle(style) { dom.setStyle(this.scroller.style, "cursor", style); } /** * @param {String} cursorStyle A css cursor style - * **/ setMouseCursor(cursorStyle) { dom.setStyle(this.scroller.style, "cursor", cursorStyle); @@ -1790,6 +1957,7 @@ class VirtualRenderer { /** * Destroys the text and cursor layers for this renderer. + **/ destroy() { this.freeze(); @@ -1800,6 +1968,10 @@ class VirtualRenderer { this.setOption("useResizeObserver", false); } + /** + * + * @param {boolean} [val] + */ $updateCustomScrollbar(val) { var _self = this; this.$horizScroll = this.$vScroll = null; @@ -1809,7 +1981,9 @@ class VirtualRenderer { delete this.$scrollDecorator; } if (val === true) { + /**@type {import("../ace-internal").Ace.VScrollbar}*/ this.scrollBarV = new VScrollBarCustom(this.container, this); + /**@type {import("../ace-internal").Ace.HScrollbar}*/ this.scrollBarH = new HScrollBarCustom(this.container, this); this.scrollBarV.setHeight(this.$size.scrollerHeight); this.scrollBarH.setWidth(this.$size.scrollerWidth); @@ -1835,6 +2009,9 @@ class VirtualRenderer { } } + /** + + */ $addResizeObserver() { if (!window.ResizeObserver || this.$resizeObserver) return; var self = this; @@ -1878,6 +2055,10 @@ oop.implement(VirtualRenderer.prototype, EventEmitter); config.defineOptions(VirtualRenderer.prototype, "renderer", { useResizeObserver: { + /** + * @param value + * @this{VirtualRenderer} + */ set: function(value) { if (!value && this.$resizeObserver) { this.$resizeObserver.disconnect(); @@ -1905,6 +2086,10 @@ config.defineOptions(VirtualRenderer.prototype, "renderer", { initialValue: 80 }, printMargin: { + /** + * @param val + * @this{VirtualRenderer} + */ set: function(val) { if (typeof val == "number") this.$printMarginColumn = val; @@ -2008,6 +2193,10 @@ config.defineOptions(VirtualRenderer.prototype, "renderer", { } }, minLines: { + /** + * @param val + * @this{VirtualRenderer} + */ set: function(val) { if (!(this.$minLines < 0x1ffffffffffff)) this.$minLines = 0; @@ -2021,6 +2210,10 @@ config.defineOptions(VirtualRenderer.prototype, "renderer", { initialValue: 0 }, scrollPastEnd: { + /** + * @param val + * @this{VirtualRenderer} + */ set: function(val) { val = +val || 0; if (this.$scrollPastEnd == val) diff --git a/src/worker/worker_client.js b/src/worker/worker_client.js index fd4f9022d04..3166374734f 100644 --- a/src/worker/worker_client.js +++ b/src/worker/worker_client.js @@ -1,6 +1,9 @@ // not implemented -exports.WorkerClient = function() { +var WorkerClient; +WorkerClient = function() { this.attachToDocument = function() {}; this.on = function() {}; this.terminate = function() {}; }; + +exports.WorkerClient = WorkerClient; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000000..2f50ebab64c --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "strict": false, + "noImplicitAny": false, + "skipLibCheck": true, + "module": "commonjs", + "target": "es2019", + "allowJs": true, + "checkJs": true, + "declaration": true, + "emitDeclarationOnly": true, + "outFile": "./types/index.d.ts" + }, + "exclude": [ + "node_modules", + "src/test", + "src/**/*_test.js", + "src/keyboard/vim.js", + "src/keyboard/emacs.js", + "src/keyboard/sublime.js", + "src/keyboard/vscode.js", + "src/mode", + "./ace-internal.d.ts", + "src/**/* *" + ], + "include": [ + "./src/**/*", + "./ace.d.ts" + ] +} diff --git a/version.js b/version.js index eaf1e5feaf4..e8e425289c8 100755 --- a/version.js +++ b/version.js @@ -1,4 +1,4 @@ #!/usr/bin/env node var x; x = require('./package'); -console.log(x.version) +console.log(x.version);