'use strict' ; import * as vscode from 'vscode' ; import { CppSettings } from './settings' ; import { getOutputChannel } from '../logger' ; import * as nls from 'vscode-nls' ; import { isString } from '../common' ; nls . config ( { messageFormat : nls . MessageFormat . bundle , bundleFormat : nls . BundleFormat . standalone } ) ( ) ; const localize = nls . loadMessageBundle ( ) ; export interface CommentPattern { begin : string ; continue : string ; } interface Rules { begin : vscode . OnEnterRule [ ] ; continue : vscode . OnEnterRule [ ] ; end : vscode . OnEnterRule [ ] ; } const escapeChars = /[\\\^\$\*\+\?\{\}\(\)\.\!\=\|\[\]\ \/]/ ; function escape ( chars ) { let result = "" ; for ( const char of chars ) { if ( char . match ( escapeChars ) ) { result += `\\${ char }` ; } else { result += char ; } } return result ; } function getMLBeginPattern ( insert ) { if ( insert . startsWith ( "/*" ) ) { const match = escape ( insert . substr ( 2 ) ) ; return `^\\s*\\/\\*${ match }(?!\\/)([^\\*]|\\*(?!\\/))*$` ; } return undefined ; } function getMLSplitAfterPattern ( ) { return "^\\s*\\*\\/$" ; } function getMLPreviousLinePattern ( insert ) { if ( insert . startsWith ( "/*" ) ) { return `(?=^(\\s*(\\/\\*\\*|\\*)).*)(?=(?!(\\s*\\*\\/)))` ; } return undefined ; } function getMLContinuePattern ( insert ) { if ( insert ) { const match = escape ( insert . trimRight ( ) ) ; if ( match ) { const right = escape ( insert . substr ( insert . trimRight ( ) . length ) ) ; return `^(\\t|[ ])*${ match }(${ right }([^\\*]|\\*(?!\\/))*)?$` ; } } return undefined ; } function getMLEmptyEndPattern ( insert ) { insert = insert . trimRight ( ) ; if ( insert !== "" ) { if ( insert . endsWith ( '*' ) ) { insert = insert . substr ( 0 , insert . length - 1 ) ; } const match = escape ( insert . trimRight ( ) ) ; return `^(\\t|[ ])*${ match }\\*\\/\\s*$` ; } return undefined ; } function getMLEndPattern ( insert ) { const match = escape ( insert . trimRight ( ) . trimLeft ( ) ) ; if ( match ) { return `^(\\t|[ ])*${ match }[^/]*\\*\\/\\s*$` ; } return undefined ; } function getSLBeginPattern ( insert ) { const match = escape ( insert . trimRight ( ) ) ; return `^\\s*${ match }.*$` ; } function getSLContinuePattern ( insert ) { const match = escape ( insert . trimRight ( ) ) ; return `^\\s*${ match }.+$` ; } function getSLEndPattern ( insert ) { let match = escape ( insert ) ; const trimmed = escape ( insert . trimRight ( ) ) ; if ( match !== trimmed ) { match = `(${ match }|${ trimmed })` ; } return `^\\s*${ match }$` ; } function getMLSplitRule ( comment ) { const beforePattern = getMLBeginPattern ( comment . begin ) ; if ( beforePattern ) { return { beforeText : new RegExp ( beforePattern ) , afterText : new RegExp ( getMLSplitAfterPattern ( ) ) , action : { indentAction : vscode . IndentAction . IndentOutdent , appendText : comment . continue ? comment . continue : '' } } ; } return undefined ; } function getMLFirstLineRule ( comment ) { const beforePattern = getMLBeginPattern ( comment . begin ) ; if ( beforePattern ) { return { beforeText : new RegExp ( beforePattern ) , action : { indentAction : vscode . IndentAction . None , appendText : comment . continue ? comment . continue : '' } } ; } return undefined ; } function getMLContinuationRule ( comment ) { const previousLinePattern = getMLPreviousLinePattern ( comment . begin ) ; if ( previousLinePattern ) { const beforePattern = getMLContinuePattern ( comment . continue ) ; if ( beforePattern ) { return { beforeText : new RegExp ( beforePattern ) , previousLineText : new RegExp ( previousLinePattern ) , action : { indentAction : vscode . IndentAction . None , appendText : comment . continue . trimLeft ( ) } } ; } } return undefined ; } function getMLEndRule ( comment ) { const beforePattern = getMLEndPattern ( comment . continue ) ; if ( beforePattern ) { return { beforeText : new RegExp ( beforePattern ) , action : { indentAction : vscode . IndentAction . None , removeText : comment . continue . length - comment . continue . trimLeft ( ) . length } } ; } return undefined ; } function getMLEmptyEndRule ( comment ) { const beforePattern = getMLEmptyEndPattern ( comment . continue ) ; if ( beforePattern ) { return { beforeText : new RegExp ( beforePattern ) , action : { indentAction : vscode . IndentAction . None , removeText : comment . continue . length - comment . continue . trimLeft ( ) . length } } ; } return undefined ; } function getSLFirstLineRule ( comment ) { const beforePattern = getSLBeginPattern ( comment . begin ) ; return { beforeText : new RegExp ( beforePattern ) , action : { indentAction : vscode . IndentAction . None , appendText : comment . continue . trimLeft ( ) } } ; } function getSLContinuationRule ( comment ) { const beforePattern = getSLContinuePattern ( comment . continue ) ; return { beforeText : new RegExp ( beforePattern ) , action : { indentAction : vscode . IndentAction . None , appendText : comment . continue . trimLeft ( ) } } ; } function getSLEndRule ( comment ) { const beforePattern = getSLEndPattern ( comment . continue ) ; return { beforeText : new RegExp ( beforePattern ) , action : { indentAction : vscode . IndentAction . None , removeText : comment . continue . length - comment . continue . trimLeft ( ) . length } } ; } export function getLanguageConfig ( languageId ) { const settings = new CppSettings ( ) ; const patterns = settings . commentContinuationPatterns ; return getLanguageConfigFromPatterns ( languageId , patterns ) ; } export function getLanguageConfigFromPatterns ( languageId , patterns ? ) { const beginPatterns = [ ] ; const continuePatterns = [ ] ; let duplicates = false ; let beginRules = [ ] ; let continueRules = [ ] ; let endRules = [ ] ; if ( ! patterns ) { patterns = [ "/**" ] ; } patterns . forEach ( pattern => { const c = isString ( pattern ) ? { begin : pattern , continue : pattern . startsWith ( '/*' ) ? " * " : pattern } : < CommentPattern > pattern ; const r = constructCommentRules ( c , languageId ) ; if ( beginPatterns . indexOf ( c . begin ) < 0 ) { if ( r . begin && r . begin . length > 0 ) { beginRules = beginRules . concat ( r . begin ) ; } beginPatterns . push ( c . begin ) ; } else { duplicates = true ; } if ( continuePatterns . indexOf ( c . continue ) < 0 ) { if ( r . continue && r . continue . length > 0 ) { continueRules = continueRules . concat ( r . continue ) ; } if ( r . end && r . end . length > 0 ) { endRules = endRules . concat ( r . end ) ; } continuePatterns . push ( c . continue ) ; } } ) ; if ( duplicates ) { getOutputChannel ( ) . appendLine ( localize ( "duplicate.multiline.patterns" , "Duplicate multiline comment patterns detected." ) ) ; } return { onEnterRules : beginRules . concat ( continueRules ) . concat ( endRules ) . filter ( e => ( e ) ) } ; } function constructCommentRules ( comment , languageId ) { if ( comment ?. begin ?. startsWith ( '/*' ) && ( languageId === 'c' || languageId === 'cpp' || languageId === 'cuda-cpp' ) ) { const mlBegin1 = getMLSplitRule ( comment ) ; if ( ! mlBegin1 ) { throw new Error ( "Failure in constructCommentRules() - mlBegin1" ) ; } const mlBegin2 = getMLFirstLineRule ( comment ) ; if ( ! mlBegin2 ) { throw new Error ( "Failure in constructCommentRules() - mlBegin2" ) ; } const mlContinue = getMLContinuationRule ( comment ) ; if ( ! mlContinue ) { throw new Error ( "Failure in constructCommentRules() - mlContinue" ) ; } const mlEnd1 = getMLEmptyEndRule ( comment ) ; if ( ! mlEnd1 ) { throw new Error ( "Failure in constructCommentRules() - mlEnd1" ) ; } const mlEnd2 = getMLEndRule ( comment ) ; if ( ! mlEnd2 ) { throw new Error ( "Failure in constructCommentRules() = mlEnd2" ) ; } return { begin : [ mlBegin1 , mlBegin2 ] , continue : [ mlContinue ] , end : [ mlEnd1 , mlEnd2 ] } ; } else if ( comment ?. begin ?. startsWith ( '//' ) && ( languageId === 'cpp' || languageId === 'cuda-cpp' ) ) { const slContinue = getSLContinuationRule ( comment ) ; const slEnd = getSLEndRule ( comment ) ; if ( comment . begin !== comment . continue ) { const slBegin = getSLFirstLineRule ( comment ) ; return { begin : ( comment . begin === comment . continue ) ? [ ] : [ slBegin ] , continue : [ slContinue ] , end : [ slEnd ] } ; } else { return { begin : [ ] , continue : [ slContinue ] , end : [ slEnd ] } ; } } return { begin : [ ] , continue : [ ] , end : [ ] } ; }