'use strict';

// MODULES //

var isArray = require( '@stdlib/assert/is-array' );


// VARIABLES //

var RETURN_ANNOTATION_REGEXP = /\s*(\* ){0,1}(\/\/|\/\*) (e\.g\.,){0,1}(returns|=>)/;
var CASE_STATEMENT = /case [^:]:/;


// MAIN //

/**
* Rule for validating that comments are preceded by empty lines.
*
* @param {Object} context - ESLint context
* @returns {Object} validators
*/
function main( context ) {
	var source = context.getSourceCode();

	/**
	* Reports the error message.
	*
	* @private
	* @param {ASTNode} node - node to report
	*/
	function report( node ) {
		context.report({
			'node': node,
			'message': 'Missing empty line before comment'
		});
	} // end FUNCTION report()

	/**
	* Checks whether comments are preceded by empty lines.
	*
	* @private
	* @param {ASTNode} node - node to examine
	*/
	function validate( node ) {
		var startsLine;
		var firstLine;
		var prevLine;
		var comments;
		var current;
		var token;
		var line;
		var i;

		comments = source.getCommentsInside( node );
		if ( isArray( comments ) && comments.length > 0 ) {
			firstLine = source.getFirstToken( node ).loc.start.line;
			for ( i = 0; i < comments.length; i++ ) {
				current = comments[ i ];
				line = current.loc.start.line;
				prevLine = source.lines[ line-2 ];
				token = source.getTokenBefore( current );
				if ( token.loc.start.line === line ) {
					startsLine = false;
				} else {
					startsLine = true;
				}
				if (
					// Flag single-line comments which are not preceded by an empty line:
					prevLine !== '' &&

					// Only flag comments that start a line:
					startsLine &&

					// Don't raise an error when comment is at the beginning of a code block:
					firstLine !== line - 1 &&

					// Don't raise an error when the comment is preceded by e.g. the opening curly brace of an object literal or an if-clause:
					token.value !== '{' &&

					// Don't raise an error when the comment is after the end of a preceding code block:
					token.value !== '}' &&

					// Don't raise an error when the comment is at the start of the condition of an if-clause:
					token.value !== '(' &&

					// Don't raise an error when the comment is after a case statement:
					!CASE_STATEMENT.test( prevLine ) &&

					// Don't raise an error for return annotations in example code:
					!RETURN_ANNOTATION_REGEXP.test( current )
				) {
					report( node );
				}
			}
		}
	} // end FUNCTION validate()

	return {
		'FunctionDeclaration': validate,
		'FunctionExpression': validate,
		'BlockExpression': validate
	};
} // end FUNCTION main()


// EXPORTS //

module.exports = {
	'meta': {
		'docs': {
			'description': 'enforce empty lines before comments'
		},
		'schema': []
	},
	'create': main
};

'use strict';

// MODULES //

var isArray = require( '@stdlib/assert/is-array' );


// VARIABLES //

var RETURN_ANNOTATION_REGEXP = /\s*(\* ){0,1}(\/\/|\/\*) (e\.g\.,){0,1}(returns|=>)/;
var CASE_STATEMENT = /case [^:]:/;


// MAIN //

/**
* Rule for validating that comments are preceded by empty lines.
*
* @param {Object} context - ESLint context
* @returns {Object} validators
*/
function main( context ) {
	var source = context.getSourceCode();

	/**
	* Reports the error message.
	*
	* @private
	* @param {ASTNode} node - node to report
	*/
	function report( node ) {
		context.report({
			'node': node,
			'message': 'Missing empty line before comment'
		});
	} // end FUNCTION report()

	/**
	* Checks whether comments are preceded by empty lines.
	*
	* @private
	* @param {ASTNode} node - node to examine
	*/
	function validate( node ) {
		var startsLine;
		var firstLine;
		var prevLine;
		var comments;
		var current;
		var token;
		var line;
		var i;

		comments = source.getCommentsInside( node );
		if ( isArray( comments ) && comments.length > 0 ) {
			firstLine = source.getFirstToken( node ).loc.start.line;
			for ( i = 0; i < comments.length; i++ ) {
				current = comments[ i ];
				line = current.loc.start.line;
				prevLine = source.lines[ line-2 ];
				token = source.getTokenBefore( current );
				if ( token.loc.start.line === line ) {
					startsLine = false;
				} else {
					startsLine = true;
				}
				if (
					// Flag single-line comments which are not preceded by an empty line:
					prevLine !== '' &&

					// Only flag comments that start a line:
					startsLine &&

					// Don't raise an error when comment is at the beginning of a code block:
					firstLine !== line - 1 &&

					// Don't raise an error when the comment is preceded by the opening curly brace of e.g. an object literal or if-clause:
					token.value !== '{' &&

					// Don't raise an error when the comment is after the end of a preceding code block:
					token.value !== '}' &&

					// Don't raise an error when the comment is at the start of the condition of an if-clause:
					token.value !== '(' &&

					// Don't raise an error when the comment is after a case statement:
					!CASE_STATEMENT.test( prevLine ) &&

					// Don't raise an error for return annotations in example code:
					!RETURN_ANNOTATION_REGEXP.test( current )
				) {
					report( node );
				}
			}
		}
	} // end FUNCTION validate()

	return {
		'FunctionDeclaration': validate,
		'FunctionExpression': validate,
		'BlockExpression': validate
	};
} // end FUNCTION main()


// EXPORTS //

module.exports = {
	'meta': {
		'docs': {
			'description': 'enforce empty lines before comments'
		},
		'schema': []
	},
	'create': main
};

Update comment