'use strict' ; import { TextDocument , CancellationToken , FormattingOptions , TextEdit , OnTypeFormattingEditProvider , Position , Range , WorkspaceEdit , workspace , commands } from 'vscode' ; import { CodePddlWorkspace } from '../workspace/CodePddlWorkspace' ; import { PddlLanguage , DomainInfo , ProblemInfo , parser } from 'pddl-workspace' ; export class PddlOnTypeFormatter implements OnTypeFormattingEditProvider { constructor ( private pddlWorkspace ? , private testMode = false ) { } async provideOnTypeFormattingEdits ( document , position , ch , options , token ) { const fileInfo = await this . pddlWorkspace ?. upsertAndParseFile ( document ) ; if ( token . isCancellationRequested ) { return undefined ; } const offset = document . offsetAt ( position ) ; if ( fileInfo && ( fileInfo . getLanguage ( ) !== PddlLanguage . PDDL ) ) { return undefined ; } let tree ; if ( fileInfo && ( fileInfo instanceof DomainInfo ) ) { tree = ( fileInfo as DomainInfo ) . syntaxTree ; } else if ( fileInfo && ( fileInfo instanceof ProblemInfo ) ) { tree = ( fileInfo as ProblemInfo ) . syntaxTree ; } else { tree = new parser . PddlSyntaxTreeBuilder ( document . getText ( ) ) . getTree ( ) ; } const currentNode = tree . getNodeAt ( offset ) ; const parentIndent = this . getParentStartCharacterIndent ( currentNode , document ) ; if ( parentIndent === null ) { return [ ] ; } const previousIndent = this . getPreviousSiblingStartCharacterIndent ( currentNode , document ) ; const rangeBefore = new Range ( position . with ( { character : 0 } ) , position ) ; const rangeAfter = new Range ( position , position . with ( { character : Number . MAX_VALUE } ) ) ; if ( ch === '\n' && currentNode . isType ( parser . PddlTokenType . Whitespace ) ) { const insertBeforeText = previousIndent !== null ? PddlOnTypeFormatter . createIndent ( previousIndent , 0 , options ) : PddlOnTypeFormatter . createIndent ( parentIndent , + 1 , options ) ; const insertAfterText = '\n' + PddlOnTypeFormatter . createIndent ( previousIndent ?? parentIndent , 0 , options ) ; const trailingText = document . getText ( rangeAfter ) . trim ( ) ; if ( trailingText && ! trailingText . match ( /\w/ ) ) { const edits = [ TextEdit . replace ( rangeBefore , insertBeforeText + insertAfterText ) , ] ; return this . applyAndMoveCursor ( document , edits , { to : 'left' , value : insertAfterText . length } ) ; } else { const edits = [ TextEdit . replace ( rangeBefore , insertBeforeText ) , ] ; return edits ; } } else if ( ch === '(-disabled' ) { const leadingText = document . getText ( rangeBefore ) ; if ( leadingText . trim ( ) === '(' ) { const insertBeforeText = PddlOnTypeFormatter . createIndent ( parentIndent , + 1 , options ) + '(' ; return [ TextEdit . replace ( rangeBefore , insertBeforeText ) ] ; } return [ ] ; } else { return [ ] ; } } async applyAndMoveCursor ( document , edits , cursorMoveOptions ) { if ( this . testMode ) { return edits ; } else { const edit = new WorkspaceEdit ( ) ; edit . set ( document . uri , edits ) ; await workspace . applyEdit ( edit ) ; await commands . executeCommand ( 'cursorMove' , cursorMoveOptions ) ; return [ ] ; } } getParentStartCharacterIndent ( currentNode , document ) { const parent = currentNode . getParent ( ) ?. expand ( ) ; if ( parent === undefined ) { throw new Error ( `Unexpected PDDL structure in ${ currentNode . getText ( ) }` ) ; } if ( parent . isDocument ( ) ) { return null ; } else { const lineOfParent = document . lineAt ( document . positionAt ( parent . getStart ( ) ) . line ) ; const firstNonWhitespaceCharacter = lineOfParent . firstNonWhitespaceCharacterIndex ; return lineOfParent . text . substr ( 0 , firstNonWhitespaceCharacter ) ; } } getPreviousSiblingStartCharacterIndent ( currentNode , document ) { const allPreviousSiblings = currentNode . getPrecedingSiblings ( parser . PddlTokenType . OpenBracket ) . concat ( currentNode . getPrecedingSiblings ( parser . PddlTokenType . OpenBracketOperator ) ) . sort ( ( n1 , n2 ) => n1 . getStart ( ) - n2 . getStart ( ) ) ; if ( allPreviousSiblings . length === 0 ) { return null ; } const previous = allPreviousSiblings [ allPreviousSiblings . length - 1 ] ; if ( previous . isDocument ( ) ) { return null ; } const lineOfPrevious = document . lineAt ( document . positionAt ( previous . getStart ( ) ) . line ) ; const firstNonWhitespaceCharacter = lineOfPrevious . firstNonWhitespaceCharacterIndex ; return lineOfPrevious . text . substr ( 0 , firstNonWhitespaceCharacter ) ; } static createIndent ( parentIndentText , levelIncrement , options ) { const singleIndent = options . insertSpaces ? " " . repeat ( options . tabSize ) : "\t" ; return parentIndentText + singleIndent . repeat ( levelIncrement ) ; } }