Lynx Eye
Lynx Eye is a code analysis tool similar to Lizard, built with Rust and tree-sitter. It analyzes code metrics for JavaScript and TypeScript, focusing on function-level static analysis to help identify complex or oversized functions that may need refactoring.
Features
- Multi-language Support: Analyzes JavaScript and TypeScript code
- Function-level Analysis: Extracts detailed metrics for each function
- Code Complexity Metrics:
- NLOC (Non-commenting Lines of Code)
- CCN (Cyclomatic Complexity Number)
- Token count
- Parameter count
- Configurable Thresholds: Set custom limits for complexity warnings
- Fast Performance: Built with Rust for efficient processing
Installation
Prerequisites
- Rust (edition 2024 or later)
- Cargo
Build from Source
The release binary will be available at target/release/lynx_eye.
Usage
Command Line Options
# Show help
# Show version
Input Options
# Analyze a single file
# Analyze multiple files
# Analyze an entire directory (non-recursive)
# Analyze directory recursively (includes subdirectories)
Output Formats
Table Format (Default)
Output:
| | | | | | | | |
+======================================================================+
| | | | | | | | |
|
| | | | | | | | |
|
| | | | | | | | |
JSON Format
Output:
CSV Format
Output:
NLOC,CCN,Token,Param,Score,Function,Line,File
7,2,30,1,24.4,calculateTotal,1,test.js
3,1,13,2,17.2,anonymous,9,test.js
Filtering Functions
Filter functions based on complexity metrics:
# Show only functions with complexity score >= 40
# Show only highly complex functions (CCN >= 10)
# Show only large functions (NLOC >= 20)
# Show functions with many parameters (>= 5)
# Combine multiple filters
Output to File
Save analysis results to a file:
# Save JSON output
# Save CSV output
# Save table output
File Type Support
Lynx Eye currently supports:
✅ JavaScript (.js files)
✅ TypeScript (.ts files)
Other file types (.py, .html, .css, etc.) are automatically ignored.
Metrics Explained
NLOC (Non-commenting Lines of Code)
What it measures: The number of lines containing actual code, excluding empty lines and comments.
How it's calculated:
- Counts lines within function boundaries
- Excludes empty lines (
line.trim().is_empty()) - Excludes single-line comments starting with
// - Excludes multi-line comment blocks starting with
/*
Example:
// NLOC: 1 ✅
// Total NLOC: 3
Why it matters: High NLOC indicates large functions that may be harder to understand and maintain. Functions with NLOC > 20 often need refactoring.
CCN (Cyclomatic Complexity Number)
What it measures: The number of independent paths through the code, representing decision complexity.
How it's calculated:
- Base complexity: 1
- +1 for each decision point (branch nodes):
ifstatementsfor,while,doloopsswitchcases- Ternary expressions (
condition ? a : b) - Logical operators (
&&,||) catchclauses
Example:
Why it matters: CCN > 10 indicates complex code that's hard to test and maintain. Each additional branch increases testing requirements exponentially.
Token Count
What it measures: The number of smallest syntactic units in the code.
How it's calculated: Counts all leaf nodes in the tree-sitter syntax tree.
Example breakdown:
// Tokens: function, add, (, a, ,, b, ), {, return, a, +, b, ;, }
// Total: 13 tokens
Why it matters: High token density (Token/NLOC ratio) can indicate complex expressions and potentially hard-to-read code.
Parameter Count
What it measures: The number of parameters a function accepts.
How it's calculated: Counts function parameters, supporting:
- Regular parameters:
function(a, b) - Optional parameters:
function(a?: number) - Rest parameters:
function(...args) - Default parameters:
function(a = 1)
Example:
// Parameter count: 3 (required, optional, rest)
Why it matters: Functions with many parameters (>3-4) are often doing too much and may benefit from parameter objects or refactoring.
Line Number
What it measures: The starting line number of the function in the source file.
Why it matters: Helps locate functions in the codebase for review and debugging.
Function Name
What it measures: The identifier of the function. Shows as "anonymous" for unnamed functions.
Why it matters: Identifies specific functions that may need attention or refactoring.
Complexity Score
What it measures: A composite score (0-100) that combines NLOC, CCN, and code density into a single complexity metric.
How it's calculated:
Score = 0.5·CCN_norm + 0.3·NLOC_norm + 0.2·Density_norm
Where:
- CCN_norm = min(CCN / 15, 1.0) - Normalized cyclomatic complexity (15 = threshold)
- NLOC_norm = min(NLOC / 30, 1.0) - Normalized lines of code (30 = threshold)
- Density_norm = min((Token/NLOC) / 8, 1.0) - Normalized token density (8 = high density threshold)
Example breakdown:
Why it matters:
- Score < 30: Simple functions, easy to understand and maintain
- Score 30-60: Moderate complexity, may need review
- Score > 60: Highly complex functions, strong candidates for refactoring
The score prioritizes cyclomatic complexity (50% weight) as the most important factor, followed by code size (30%) and code density (20%). This provides a balanced view that identifies genuinely complex code rather than just large functions.
Example
| | | | | | | | |
+======================================================================+
| | | | | | | | |
|
| | | | | | | | |
|
| | | | | | | | |
|
| | | | | | | | |
This output shows:
calculateTotal(lines 1-7): NLOC=7, CCN=2 (contains a for loop), 30 tokens, 1 parameter, Score=24.4 (low complexity)calculateTax(lines 9-11): NLOC=3, CCN=1 (simple function), 13 tokens, 2 parameters, Score=17.2 (very low complexity)addandsubtract(lines 14-21): NLOC=3, CCN=1 (simple methods), 13 tokens, 2 parameters each, Score=17.2 (very low complexity)
Development
Build and Run
# Build the project
# Run in debug mode
# Build optimized release version
Testing
# Run all tests
# Run a specific test
Development Tools
# Quick check without building
# Run linter
# Format code
# Clean build artifacts
Architecture
Lynx Eye uses tree-sitter to parse source code into a Concrete Syntax Tree (CST), then:
- Tree Traversal: Identifies function nodes (function declarations, arrow functions, method definitions, etc.)
- Metadata Extraction: Captures function name, start/end lines, and parameters
- NLOC Calculation: Counts non-empty, non-comment lines within function boundaries
- CCN Calculation: Counts branch nodes (if statements, loops, ternary expressions, etc.)
Supported Function Types
function_declaration-function foo() {}function_expression-const foo = function() {}arrow_function-const foo = () => {}method_definition- Class methods, getters, settersgenerator_function_declaration-function* foo() {}
Complexity Nodes
The tool counts the following branch nodes for CCN calculation:
if_statement,for_statement,for_in_statementwhile_statement,do_statementswitch_case(each case)ternary_expression/conditional_expressioncatch_clause&&and||binary expressions
License
This project is licensed under the MIT License - see the LICENSE file for details.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
Compatibility Requirements
Please ensure changes maintain compatibility with Rust 1.70.0+. See COMPATIBILITY.md for detailed compatibility notes and upgrade guidelines.
Acknowledgments
- tree-sitter - Parser generator tool
- Lizard - Inspiration for this project
- The Rust community for excellent tooling and libraries