diff --git a/codelimit/commands/check.py b/codelimit/commands/check.py index f0d7318..481214b 100644 --- a/codelimit/commands/check.py +++ b/codelimit/commands/check.py @@ -30,9 +30,9 @@ def check_command(paths: list[Path], quiet: bool): check_file(abs_path, check_result) exit_code = 1 if check_result.unmaintainable > 0 else 0 if ( - not quiet - or check_result.hard_to_maintain > 0 - or check_result.unmaintainable > 0 + not quiet + or check_result.hard_to_maintain > 0 + or check_result.unmaintainable > 0 ): check_result.report() raise typer.Exit(code=exit_code) diff --git a/codelimit/common/Language.py b/codelimit/common/Language.py index 2029ae3..98395bf 100644 --- a/codelimit/common/Language.py +++ b/codelimit/common/Language.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from abc import ABC, abstractmethod from codelimit.common.Token import Token @@ -17,6 +19,6 @@ def extract_headers(self, tokens: list[Token]) -> list[Header]: @abstractmethod def extract_blocks( - self, tokens: list[Token], headers: list[Header] + self, tokens: list[Token], headers: list[Header] ) -> list[TokenRange]: pass diff --git a/codelimit/common/report/format_markdown.py b/codelimit/common/report/format_markdown.py index 9b633f2..053250a 100644 --- a/codelimit/common/report/format_markdown.py +++ b/codelimit/common/report/format_markdown.py @@ -126,5 +126,6 @@ def _print_findings_with_repository(report_units: list[ReportUnit], link = (f'https://github.com/{owner}/{name}/blob/{branch}/{unit.file}#L{unit.measurement.start.line}-L' f'{unit.measurement.end.line}') console.print( - f"| {violation_type} \[{unit.measurement.unit_name}]({link}) | {unit.measurement.value} | {unit.file} |" + f"| {violation_type} " + '\[' + unit.measurement.unit_name + ']' + f"({link}) | {unit.measurement.value} " + f"| {unit.file} |" ) diff --git a/codelimit/common/token_matching/predicate/TokenValue.py b/codelimit/common/token_matching/predicate/TokenValue.py index e713beb..42c1526 100644 --- a/codelimit/common/token_matching/predicate/TokenValue.py +++ b/codelimit/common/token_matching/predicate/TokenValue.py @@ -22,4 +22,4 @@ def __hash__(self): return hash(self.value) def __str__(self): - return f"" + return self.value diff --git a/codelimit/languages/CSharp.py b/codelimit/languages/CSharp.py new file mode 100644 index 0000000..a943716 --- /dev/null +++ b/codelimit/languages/CSharp.py @@ -0,0 +1,20 @@ +from codelimit.common.Language import Language +from codelimit.common.gsm.operator.OneOrMore import OneOrMore +from codelimit.common.scope.scope_utils import ( + get_blocks, + get_headers, +) +from codelimit.common.token_matching.predicate.Balanced import Balanced +from codelimit.common.token_matching.predicate.Name import Name +from codelimit.common.token_matching.predicate.Symbol import Symbol + + +class CSharp(Language): + def __init__(self): + super().__init__('C#') + + def extract_headers(self, tokens: list) -> list: + return get_headers(tokens, [Name(), OneOrMore(Balanced('(', ')'))], Symbol('{')) + + def extract_blocks(self, tokens: list, headers: list) -> list: + return get_blocks(tokens, "{", "}") diff --git a/codelimit/languages/Cpp.py b/codelimit/languages/Cpp.py index e8a4f62..7fab1ba 100644 --- a/codelimit/languages/Cpp.py +++ b/codelimit/languages/Cpp.py @@ -17,6 +17,6 @@ def extract_headers(self, tokens: list[Token]) -> list[Header]: return get_headers(tokens, [Name(), OneOrMore(Balanced("(", ")"))], Symbol("{")) def extract_blocks( - self, tokens: list[Token], headers: list[Header] + self, tokens: list[Token], headers: list[Header] ) -> list[TokenRange]: return get_blocks(tokens, "{", "}") diff --git a/codelimit/languages/TypeScript.py b/codelimit/languages/TypeScript.py index 9fc8747..1a18e1c 100644 --- a/codelimit/languages/TypeScript.py +++ b/codelimit/languages/TypeScript.py @@ -6,10 +6,10 @@ from codelimit.common.scope.Header import Header from codelimit.common.scope.scope_utils import get_blocks, get_headers from codelimit.common.token_matching.predicate.Balanced import Balanced -from codelimit.common.token_matching.predicate.Or import Or from codelimit.common.token_matching.predicate.Keyword import Keyword from codelimit.common.token_matching.predicate.Name import Name from codelimit.common.token_matching.predicate.Operator import Operator +from codelimit.common.token_matching.predicate.Or import Or from codelimit.common.token_matching.predicate.Symbol import Symbol @@ -38,6 +38,6 @@ def extract_headers(self, tokens: list[Token]) -> list[Header]: return functions + arrow_functions def extract_blocks( - self, tokens: list[Token], headers: list[Header] + self, tokens: list[Token], headers: list[Header] ) -> list[TokenRange]: return get_blocks(tokens, "{", "}") diff --git a/codelimit/languages/__init__.py b/codelimit/languages/__init__.py index 1c70853..516a82d 100644 --- a/codelimit/languages/__init__.py +++ b/codelimit/languages/__init__.py @@ -1,4 +1,6 @@ +from codelimit.common.Language import Language from codelimit.languages.C import C +from codelimit.languages.CSharp import CSharp from codelimit.languages.Cpp import Cpp from codelimit.languages.Java import Java from codelimit.languages.JavaScript import JavaScript @@ -9,16 +11,14 @@ class Languages: C = C() Cpp = Cpp() + CSharp = CSharp() Java = Java() JavaScript = JavaScript() Python = Python() TypeScript = TypeScript() - by_name = { - C.name: C, - Cpp.name: Cpp, - Java.name: Java, - JavaScript.name: JavaScript, - Python.name: Python, - TypeScript.name: TypeScript, - } + by_name: dict[str, Language] = {} + for subclass in Language.__subclasses__(): + language = subclass() # type: ignore + by_name[language.name] = language + diff --git a/examples/TheRenegadeCoder_sample-programs_1000-snip/codelimit.json b/examples/TheRenegadeCoder_sample-programs_1000-snip/codelimit.json index 9107b85..06b4bf2 100644 --- a/examples/TheRenegadeCoder_sample-programs_1000-snip/codelimit.json +++ b/examples/TheRenegadeCoder_sample-programs_1000-snip/codelimit.json @@ -1,8 +1,8 @@ { "version": "0.18.1", - "uuid": "5aae13b7-887c-419c-a501-fbaf1bdc6ebc", - "timestamp": "2025-01-16T09:25:52+00:00", - "root": "/private/var/folders/t2/s_70szzn7qn22cp4d2nmrjbc0000gn/T/tmpz2qbk_j7/sample-programs", + "uuid": "e0fa1097-1ec6-468e-b5fc-595e48a412f4", + "timestamp": "2025-01-17T21:59:44+00:00", + "root": "/private/var/folders/t2/s_70szzn7qn22cp4d2nmrjbc0000gn/T/tmpqjz0jke_/sample-programs", "codebase": { "totals": { "TypeScript": { @@ -33,6 +33,13 @@ "hard_to_maintain": 4, "unmaintainable": 0 }, + "C#": { + "files": 29, + "lines_of_code": 849, + "functions": 71, + "hard_to_maintain": 0, + "unmaintainable": 1 + }, "C": { "files": 26, "lines_of_code": 1248, @@ -54,7 +61,7 @@ "archive/", "scripts/" ], - "profile": [2162, 1886, 1246, 0] + "profile": [2588, 2243, 1246, 66] }, "archive/t/typescript/": { "entries": [ @@ -99,7 +106,7 @@ "c/", "p/" ], - "profile": [2096, 1815, 1210, 0] + "profile": [2522, 2172, 1210, 66] }, "archive/h/hack/": { "entries": [ @@ -200,6 +207,48 @@ ], "profile": [342, 200, 97, 0] }, + "archive/c/c-sharp/": { + "entries": [ + "LongestWord.cs", + "QuickSort.cs", + "FizzBuzz.cs", + "Baklava.cs", + "Rot13.cs", + "RemoveAllWhitespace.cs", + "JosephusProblem.cs", + "SelectionSort.cs", + "LinearSearch.cs", + "InsertionSort.cs", + "ReverseString.cs", + "SleepSort.cs", + "LongestCommonSubsequence.cs", + "HelloWorld.cs", + "PrimeNumber.cs", + "JobSequencing.cs", + "MergeSort.cs", + "Quine.cs", + "Fibonacci.cs", + "BubbleSort.cs", + "BinarySearch.cs", + "DuplicateCharacterCounter.cs", + "Capitalize.cs", + "EvenOdd.cs", + "RomanNumeral.cs", + "FractionMath.cs", + "FileInputOutput.cs", + "LongestPalindromicSubstring.cs", + "Factorial.cs" + ], + "profile": [426, 357, 0, 66] + }, + "archive/c/": { + "entries": [ + "c-sharp/", + "c-plus-plus/", + "c/" + ], + "profile": [1175, 1364, 967, 66] + }, "archive/c/c-plus-plus/": { "entries": [ "dijkstra.cpp", @@ -232,13 +281,6 @@ ], "profile": [269, 635, 571, 0] }, - "archive/c/": { - "entries": [ - "c-plus-plus/", - "c/" - ], - "profile": [749, 1007, 967, 0] - }, "archive/c/c/": { "entries": [ "hello-world.c", @@ -1127,6 +1169,309 @@ {"unit_name": "printString", "start": {"line": 22, "column": 1}, "end": {"line": 25, "column": 2}, "value": 4} ] }, + "archive/c/c-sharp/LongestWord.cs": { + "checksum": "6b2a4ac40a91bd74c2ed644a85006702", + "language": "C#", + "loc": 11, + "profile": [11, 0, 0, 0], + "measurements": [ + {"unit_name": "Main", "start": {"line": 6, "column": 24}, "end": {"line": 24, "column": 6}, "value": 11} + ] + }, + "archive/c/c-sharp/QuickSort.cs": { + "checksum": "afbda702f51f573168b7ecfd4ab00cbd", + "language": "C#", + "loc": 33, + "profile": [16, 17, 0, 0], + "measurements": [ + {"unit_name": "Sort", "start": {"line": 7, "column": 29}, "end": {"line": 18, "column": 6}, "value": 11}, + {"unit_name": "ErrorAndExit", "start": {"line": 20, "column": 24}, "end": {"line": 24, "column": 6}, "value": 5}, + {"unit_name": "Main", "start": {"line": 26, "column": 24}, "end": {"line": 42, "column": 6}, "value": 17} + ] + }, + "archive/c/c-sharp/FizzBuzz.cs": { + "checksum": "216fd4ec5593e0ce766935619b26fda6", + "language": "C#", + "loc": 25, + "profile": [8, 17, 0, 0], + "measurements": [ + {"unit_name": "FizzBuzz", "start": {"line": 5, "column": 30}, "end": {"line": 21, "column": 10}, "value": 17}, + {"unit_name": "Main", "start": {"line": 23, "column": 29}, "end": {"line": 30, "column": 10}, "value": 8} + ] + }, + "archive/c/c-sharp/Baklava.cs": { + "checksum": "fbc9ebc9734a3076519dc1e18b170afa", + "language": "C#", + "loc": 11, + "profile": [11, 0, 0, 0], + "measurements": [ + {"unit_name": "Main", "start": {"line": 6, "column": 17}, "end": {"line": 19, "column": 6}, "value": 11} + ] + }, + "archive/c/c-sharp/Rot13.cs": { + "checksum": "aca5d2355a2fe3fcf34da389f1110a72", + "language": "C#", + "loc": 32, + "profile": [32, 0, 0, 0], + "measurements": [ + {"unit_name": "Encrypt", "start": {"line": 15, "column": 28}, "end": {"line": 28, "column": 10}, "value": 12}, + {"unit_name": "ExitWithError", "start": {"line": 30, "column": 28}, "end": {"line": 34, "column": 10}, "value": 5}, + {"unit_name": "Main", "start": {"line": 36, "column": 28}, "end": {"line": 50, "column": 10}, "value": 15} + ] + }, + "archive/c/c-sharp/RemoveAllWhitespace.cs": { + "checksum": "697f556df1e6fd21790e7b8d59fb55a9", + "language": "C#", + "loc": 21, + "profile": [21, 0, 0, 0], + "measurements": [ + {"unit_name": "ExitWithError", "start": {"line": 7, "column": 24}, "end": {"line": 11, "column": 6}, "value": 5}, + {"unit_name": "RemoveAllWhitespace", "start": {"line": 13, "column": 24}, "end": {"line": 21, "column": 6}, "value": 9}, + {"unit_name": "Main", "start": {"line": 23, "column": 17}, "end": {"line": 29, "column": 6}, "value": 7} + ] + }, + "archive/c/c-sharp/JosephusProblem.cs": { + "checksum": "c4ecb4fbde17ebfdd3506e0b896eb853", + "language": "C#", + "loc": 24, + "profile": [24, 0, 0, 0], + "measurements": [ + {"unit_name": "Main", "start": {"line": 9, "column": 21}, "end": {"line": 26, "column": 10}, "value": 15}, + {"unit_name": "FindJosephusPosition", "start": {"line": 28, "column": 20}, "end": {"line": 38, "column": 10}, "value": 9} + ] + }, + "archive/c/c-sharp/SelectionSort.cs": { + "checksum": "063c2736caee2714105aaca22bc905b4", + "language": "C#", + "loc": 31, + "profile": [14, 17, 0, 0], + "measurements": [ + {"unit_name": "Selection", "start": {"line": 7, "column": 36}, "end": {"line": 15, "column": 6}, "value": 9}, + {"unit_name": "ErrorAndExit", "start": {"line": 17, "column": 24}, "end": {"line": 21, "column": 6}, "value": 5}, + {"unit_name": "Main", "start": {"line": 23, "column": 24}, "end": {"line": 39, "column": 6}, "value": 17} + ] + }, + "archive/c/c-sharp/LinearSearch.cs": { + "checksum": "8a88ab3dc22ea8b641e264745592fa27", + "language": "C#", + "loc": 29, + "profile": [29, 0, 0, 0], + "measurements": [ + {"unit_name": "Search", "start": {"line": 7, "column": 24}, "end": {"line": 17, "column": 6}, "value": 11}, + {"unit_name": "ErrorAndExit", "start": {"line": 19, "column": 24}, "end": {"line": 23, "column": 6}, "value": 5}, + {"unit_name": "Main", "start": {"line": 25, "column": 24}, "end": {"line": 37, "column": 6}, "value": 13} + ] + }, + "archive/c/c-sharp/InsertionSort.cs": { + "checksum": "9be7114c7e476a06e2023f2e8fe2af79", + "language": "C#", + "loc": 37, + "profile": [20, 17, 0, 0], + "measurements": [ + {"unit_name": "Insertion", "start": {"line": 7, "column": 29}, "end": {"line": 13, "column": 6}, "value": 7}, + {"unit_name": "Insert", "start": {"line": 15, "column": 29}, "end": {"line": 22, "column": 6}, "value": 8}, + {"unit_name": "ErrorAndExit", "start": {"line": 24, "column": 24}, "end": {"line": 28, "column": 6}, "value": 5}, + {"unit_name": "Main", "start": {"line": 30, "column": 24}, "end": {"line": 46, "column": 6}, "value": 17} + ] + }, + "archive/c/c-sharp/ReverseString.cs": { + "checksum": "1def91d277dddc3b8eba5993ed4e2f66", + "language": "C#", + "loc": 13, + "profile": [13, 0, 0, 0], + "measurements": [ + {"unit_name": "Reverse", "start": {"line": 7, "column": 30}, "end": {"line": 12, "column": 10}, "value": 6}, + {"unit_name": "Main", "start": {"line": 14, "column": 28}, "end": {"line": 20, "column": 10}, "value": 7} + ] + }, + "archive/c/c-sharp/SleepSort.cs": { + "checksum": "253dc4826051c6bcdf8ea20f14f61f18", + "language": "C#", + "loc": 25, + "profile": [5, 20, 0, 0], + "measurements": [ + {"unit_name": "ErrorAndExit", "start": {"line": 8, "column": 24}, "end": {"line": 12, "column": 6}, "value": 5}, + {"unit_name": "Main", "start": {"line": 14, "column": 24}, "end": {"line": 36, "column": 6}, "value": 20} + ] + }, + "archive/c/c-sharp/LongestCommonSubsequence.cs": { + "checksum": "d475685b9b021cb103182ef6fa76ece6", + "language": "C#", + "loc": 23, + "profile": [23, 0, 0, 0], + "measurements": [ + {"unit_name": "LCS", "start": {"line": 9, "column": 44}, "end": {"line": 18, "column": 10}, "value": 8}, + {"unit_name": "Main", "start": {"line": 23, "column": 28}, "end": {"line": 37, "column": 10}, "value": 15} + ] + }, + "archive/c/c-sharp/HelloWorld.cs": { + "checksum": "a6f9560b27afd4ffa6cc0ea4eefcce70", + "language": "C#", + "loc": 4, + "profile": [4, 0, 0, 0], + "measurements": [ + {"unit_name": "Main", "start": {"line": 5, "column": 21}, "end": {"line": 8, "column": 10}, "value": 4} + ] + }, + "archive/c/c-sharp/PrimeNumber.cs": { + "checksum": "467de4c534f30cdcafe06ce0df12cf30", + "language": "C#", + "loc": 32, + "profile": [13, 19, 0, 0], + "measurements": [ + {"unit_name": "IsPrime", "start": {"line": 8, "column": 28}, "end": {"line": 22, "column": 10}, "value": 13}, + {"unit_name": "Main", "start": {"line": 24, "column": 28}, "end": {"line": 42, "column": 10}, "value": 19} + ] + }, + "archive/c/c-sharp/JobSequencing.cs": { + "checksum": "0bf4e9eed47febfbd03425fee0f8d292", + "language": "C#", + "loc": 43, + "profile": [5, 38, 0, 0], + "measurements": [ + {"unit_name": "Job", "start": {"line": 12, "column": 12}, "end": {"line": 16, "column": 6}, "value": 5}, + {"unit_name": "Main", "start": {"line": 21, "column": 17}, "end": {"line": 51, "column": 6}, "value": 18}, + {"unit_name": "GetMaxProfitJobSequence", "start": {"line": 54, "column": 29}, "end": {"line": 85, "column": 6}, "value": 20} + ] + }, + "archive/c/c-sharp/MergeSort.cs": { + "checksum": "3ad95112093140b80766eccb7996bdac", + "language": "C#", + "loc": 51, + "profile": [18, 33, 0, 0], + "measurements": [ + {"unit_name": "Sort", "start": {"line": 8, "column": 35}, "end": {"line": 20, "column": 6}, "value": 13}, + {"unit_name": "Merge", "start": {"line": 22, "column": 29}, "end": {"line": 37, "column": 6}, "value": 16}, + {"unit_name": "ErrorAndExit", "start": {"line": 39, "column": 24}, "end": {"line": 43, "column": 6}, "value": 5}, + {"unit_name": "Main", "start": {"line": 45, "column": 24}, "end": {"line": 61, "column": 6}, "value": 17} + ] + }, + "archive/c/c-sharp/Quine.cs": { + "checksum": "cd19d1608d4e8826d6414f1de6fe1e7a", + "language": "C#", + "loc": 1, + "profile": [1, 0, 0, 0], + "measurements": [ + {"unit_name": "Main", "start": {"line": 1, "column": 27}, "end": {"line": 1, "column": 194}, "value": 1} + ] + }, + "archive/c/c-sharp/Fibonacci.cs": { + "checksum": "cb8fb19ae19fba2a16c1cc7357093bec", + "language": "C#", + "loc": 22, + "profile": [0, 22, 0, 0], + "measurements": [ + {"unit_name": "Main", "start": {"line": 7, "column": 28}, "end": {"line": 28, "column": 10}, "value": 22} + ] + }, + "archive/c/c-sharp/BubbleSort.cs": { + "checksum": "59a149aa622d07067f6875af50029366", + "language": "C#", + "loc": 51, + "profile": [17, 34, 0, 0], + "measurements": [ + {"unit_name": "BubbleSort", "start": {"line": 7, "column": 29}, "end": {"line": 18, "column": 6}, "value": 12}, + {"unit_name": "PassList", "start": {"line": 20, "column": 29}, "end": {"line": 36, "column": 6}, "value": 17}, + {"unit_name": "ErrorAndExit", "start": {"line": 38, "column": 24}, "end": {"line": 42, "column": 6}, "value": 5}, + {"unit_name": "Main", "start": {"line": 44, "column": 24}, "end": {"line": 60, "column": 6}, "value": 17} + ] + }, + "archive/c/c-sharp/BinarySearch.cs": { + "checksum": "51b6e4a3306aed06962f0a27d560c914", + "language": "C#", + "loc": 47, + "profile": [9, 38, 0, 0], + "measurements": [ + {"unit_name": "Search", "start": {"line": 7, "column": 24}, "end": {"line": 28, "column": 6}, "value": 18}, + {"unit_name": "if", "start": {"line": 18, "column": 18}, "end": {"line": 21, "column": 14}, "value": 4}, + {"unit_name": "ErrorAndExit", "start": {"line": 30, "column": 24}, "end": {"line": 34, "column": 6}, "value": 5}, + {"unit_name": "Main", "start": {"line": 36, "column": 24}, "end": {"line": 57, "column": 6}, "value": 20} + ] + }, + "archive/c/c-sharp/DuplicateCharacterCounter.cs": { + "checksum": "bebcab9263ea744cdbe7856b145d8f6a", + "language": "C#", + "loc": 27, + "profile": [0, 27, 0, 0], + "measurements": [ + {"unit_name": "Main", "start": {"line": 6, "column": 24}, "end": {"line": 38, "column": 6}, "value": 27} + ] + }, + "archive/c/c-sharp/Capitalize.cs": { + "checksum": "1e8897d54588dc9d0e21726e9a41cb11", + "language": "C#", + "loc": 11, + "profile": [11, 0, 0, 0], + "measurements": [ + {"unit_name": "Main", "start": {"line": 8, "column": 21}, "end": {"line": 18, "column": 10}, "value": 11} + ] + }, + "archive/c/c-sharp/EvenOdd.cs": { + "checksum": "fac6179595a37eeca66e525a3f4a7e81", + "language": "C#", + "loc": 14, + "profile": [14, 0, 0, 0], + "measurements": [ + {"unit_name": "Main", "start": {"line": 7, "column": 28}, "end": {"line": 20, "column": 10}, "value": 14} + ] + }, + "archive/c/c-sharp/RomanNumeral.cs": { + "checksum": "93c003bc1aab90c1204b43adad4f89cc", + "language": "C#", + "loc": 30, + "profile": [13, 17, 0, 0], + "measurements": [ + {"unit_name": "RomanToDecimal", "start": {"line": 19, "column": 28}, "end": {"line": 33, "column": 10}, "value": 13}, + {"unit_name": "Main", "start": {"line": 35, "column": 28}, "end": {"line": 51, "column": 10}, "value": 17} + ] + }, + "archive/c/c-sharp/FractionMath.cs": { + "checksum": "0540769e6e1445605d1be0313b0ed14a", + "language": "C#", + "loc": 112, + "profile": [46, 0, 0, 66], + "measurements": [ + {"unit_name": "FractionMath", "start": {"line": 10, "column": 16}, "end": {"line": 19, "column": 10}, "value": 9}, + {"unit_name": "GCD", "start": {"line": 24, "column": 21}, "end": {"line": 33, "column": 10}, "value": 10}, + {"unit_name": "Simplify", "start": {"line": 35, "column": 22}, "end": {"line": 46, "column": 10}, "value": 11}, + {"unit_name": "ToString", "start": {"line": 48, "column": 32}, "end": {"line": 52, "column": 10}, "value": 5}, + {"unit_name": "Parse", "start": {"line": 54, "column": 36}, "end": {"line": 66, "column": 10}, "value": 11}, + {"unit_name": "Main", "start": {"line": 126, "column": 28}, "end": {"line": 194, "column": 10}, "value": 66} + ] + }, + "archive/c/c-sharp/FileInputOutput.cs": { + "checksum": "b274b1157fcad20351ef612dc8c2b6a7", + "language": "C#", + "loc": 5, + "profile": [5, 0, 0, 0], + "measurements": [ + {"unit_name": "Main", "start": {"line": 14, "column": 28}, "end": {"line": 18, "column": 10}, "value": 5} + ] + }, + "archive/c/c-sharp/LongestPalindromicSubstring.cs": { + "checksum": "140d089fde3f7a1bcef679fdf4c0f0fb", + "language": "C#", + "loc": 54, + "profile": [33, 21, 0, 0], + "measurements": [ + {"unit_name": "Main", "start": {"line": 8, "column": 28}, "end": {"line": 12, "column": 10}, "value": 5}, + {"unit_name": "LongestPalindrome", "start": {"line": 14, "column": 30}, "end": {"line": 37, "column": 10}, "value": 21}, + {"unit_name": "ExpandAroundCenter", "start": {"line": 39, "column": 28}, "end": {"line": 47, "column": 10}, "value": 9}, + {"unit_name": "ContainsPalindrome", "start": {"line": 49, "column": 29}, "end": {"line": 62, "column": 10}, "value": 13}, + {"unit_name": "Reverse", "start": {"line": 64, "column": 31}, "end": {"line": 69, "column": 10}, "value": 6} + ] + }, + "archive/c/c-sharp/Factorial.cs": { + "checksum": "5d62f74dc37f768692cb53dd13d8a62e", + "language": "C#", + "loc": 30, + "profile": [10, 20, 0, 0], + "measurements": [ + {"unit_name": "Fact", "start": {"line": 8, "column": 34}, "end": {"line": 13, "column": 10}, "value": 6}, + {"unit_name": "Main", "start": {"line": 15, "column": 28}, "end": {"line": 37, "column": 10}, "value": 20}, + {"unit_name": "if", "start": {"line": 25, "column": 22}, "end": {"line": 28, "column": 18}, "value": 4} + ] + }, "archive/c/c-plus-plus/dijkstra.cpp": { "checksum": "a13debbf0ff15e104c1141e7fb0639bd", "language": "C++", diff --git a/tests/common/token_matching/test_TokenMatching.py b/tests/common/token_matching/test_TokenMatching.py index ba26fb2..ae09406 100644 --- a/tests/common/token_matching/test_TokenMatching.py +++ b/tests/common/token_matching/test_TokenMatching.py @@ -1,4 +1,6 @@ +import pytest from pygments.lexers import PythonLexer +from pygments.lexers import CSharpLexer from codelimit.common.gsm.matcher import find_all from codelimit.common.gsm.operator.OneOrMore import OneOrMore @@ -6,6 +8,7 @@ from codelimit.common.token_matching.predicate.Balanced import Balanced from codelimit.common.token_matching.predicate.Keyword import Keyword from codelimit.common.token_matching.predicate.Name import Name +from codelimit.common.token_matching.predicate.Symbol import Symbol from codelimit.common.token_matching.predicate.TokenValue import TokenValue @@ -70,3 +73,13 @@ def test_ignore_incomplete_match(): result = find_all([Keyword("def"), Name(), OneOrMore(Balanced("(", ")"))], tokens) assert len(result) == 1 + + +@pytest.mark.skip +def test_predicate_follows_operator(): + code = "Split(new[] {' '})" + tokens = lex(CSharpLexer(), code) + + result = find_all([Name(), OneOrMore(Balanced("(", ")")), Symbol('{')], tokens) + + assert len(result) == 1 diff --git a/tests/conftest.py b/tests/conftest.py index f6dd945..c3fed14 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -8,20 +8,20 @@ from codelimit.common.source_utils import filter_tokens -def assert_units(code: str, language: Language, units: dict[str, int]): +def assert_functions(code: str, language: Language, functions: dict[str, int]): code = dedent(code).strip("\n") lexer = get_lexer_by_name(language.name) tokens = lex(lexer, code, False) code_tokens = filter_tokens(tokens) scopes = build_scopes(tokens, language) scopes = unfold_scopes(scopes) - assert len(scopes) == len(units) + assert len(scopes) == len(functions) for idx, scope in enumerate(scopes): - assert scope.header.name() in units - assert count_lines(scope, code_tokens) == units[scope.header.name()] + assert scope.header.name() in functions + assert count_lines(scope, code_tokens) == functions[scope.header.name()] -def print_units(code: str, language: Language): +def print_scopes(code: str, language: Language): code = dedent(code).strip("\n") lexer = get_lexer_by_name(language.name) tokens = lex(lexer, code, False) @@ -30,4 +30,4 @@ def print_units(code: str, language: Language): scopes = unfold_scopes(scopes) print() for scope in scopes: - print(f"{scope.header.name}: {count_lines(scope, code_tokens)}") + print(f"{scope.header.name()}: {count_lines(scope, code_tokens)}") diff --git a/tests/languages/test_C.py b/tests/languages/test_C.py index b99199a..8f83a64 100644 --- a/tests/languages/test_C.py +++ b/tests/languages/test_C.py @@ -1,5 +1,5 @@ from codelimit.languages import Languages -from tests.conftest import assert_units +from tests.conftest import assert_functions def test_simple_main_function(): @@ -9,7 +9,7 @@ def test_simple_main_function(): } """ - assert_units(code, Languages.C, {"main": 3}) + assert_functions(code, Languages.C, {"main": 3}) def test_main_function_with_include(): @@ -21,7 +21,7 @@ def test_main_function_with_include(): } """ - assert_units(code, Languages.C, {"main": 4}) + assert_functions(code, Languages.C, {"main": 4}) def test_simple_function(): @@ -32,7 +32,7 @@ def test_simple_function(): } """ - assert_units(code, Languages.C, {"bar": 3}) + assert_functions(code, Languages.C, {"bar": 3}) def test_multiple_functions(): @@ -46,13 +46,13 @@ def test_multiple_functions(): } """ - assert_units(code, Languages.C, {"bar": 3, "foo": 3}) + assert_functions(code, Languages.C, {"bar": 3, "foo": 3}) def test_no_functions(): code = "" - assert_units(code, Languages.C, {}) + assert_functions(code, Languages.C, {}) def test_iteration_macro_is_not_a_function(): @@ -64,7 +64,7 @@ def test_iteration_macro_is_not_a_function(): } """ - assert_units(code, Languages.C, {"foo": 5}) + assert_functions(code, Languages.C, {"foo": 5}) def test_formatting(): @@ -78,7 +78,7 @@ def test_formatting(): } """ - assert_units(code, Languages.C, {"nfs_register_sysctl": 7}) + assert_functions(code, Languages.C, {"nfs_register_sysctl": 7}) def test_nested_header_but_no_body_inside_parent(): @@ -92,4 +92,4 @@ def test_nested_header_but_no_body_inside_parent(): }; """ - assert_units(code, Languages.C, {"foo": 5}) + assert_functions(code, Languages.C, {"foo": 5}) diff --git a/tests/languages/test_CSharp.py b/tests/languages/test_CSharp.py new file mode 100644 index 0000000..b73dd45 --- /dev/null +++ b/tests/languages/test_CSharp.py @@ -0,0 +1,31 @@ +from codelimit.languages import Languages +from tests.conftest import assert_functions + + +def test_hello_world(): + code = """ + namespace SamplePrograms + { + public class HelloWorld + { + static void Main() + { + System.Console.WriteLine("Hello, World!"); + } + } + } + """ + + assert_functions(code, Languages.CSharp, {"Main": 4}) + + +def test_longest_word(): + code = """ + public class Foo { + public void bar() { + string[] w = sentence.Split(new[] {' '}); + } + } + """ + + assert_functions(code, Languages.CSharp, {"bar": 3}) diff --git a/tests/languages/test_Cpp.py b/tests/languages/test_Cpp.py index 8b48b9f..b257a56 100644 --- a/tests/languages/test_Cpp.py +++ b/tests/languages/test_Cpp.py @@ -1,5 +1,5 @@ from codelimit.languages import Languages -from tests.conftest import assert_units +from tests.conftest import assert_functions def test_simple_main_function(): @@ -14,7 +14,7 @@ def test_simple_main_function(): } """ - assert_units(code, Languages.Cpp, {"main": 4}) + assert_functions(code, Languages.Cpp, {"main": 4}) def test_simple_class_function(): @@ -37,7 +37,7 @@ class Main { } """ - assert_units(code, Languages.Cpp, {"sayHello": 3, "main": 5}) + assert_functions(code, Languages.Cpp, {"sayHello": 3, "main": 5}) def test_function(): @@ -50,7 +50,7 @@ def test_function(): } """ - assert_units(code, Languages.Cpp, {"makeInscribedOctagon": 5}) + assert_functions(code, Languages.Cpp, {"makeInscribedOctagon": 5}) def test_namespace(): @@ -71,7 +71,7 @@ class Main { } """ - assert_units(code, Languages.Cpp, {"sayHello": 3}) + assert_functions(code, Languages.Cpp, {"sayHello": 3}) def test_skip_function_with_nocl_comment(): @@ -81,7 +81,7 @@ def test_skip_function_with_nocl_comment(): } """ - assert_units(code, Languages.Cpp, {"foo": 3}) + assert_functions(code, Languages.Cpp, {"foo": 3}) code = """ void foo(Bar bar) { // nocl @@ -89,4 +89,4 @@ def test_skip_function_with_nocl_comment(): } """ - assert_units(code, Languages.Cpp, {}) + assert_functions(code, Languages.Cpp, {}) diff --git a/tests/languages/test_Java.py b/tests/languages/test_Java.py index fb58456..0e5508e 100644 --- a/tests/languages/test_Java.py +++ b/tests/languages/test_Java.py @@ -1,5 +1,5 @@ from codelimit.languages import Languages -from tests.conftest import assert_units, print_units +from tests.conftest import assert_functions def test_simple_main_function(): @@ -11,7 +11,7 @@ def test_simple_main_function(): } """ - assert_units(code, Languages.Java, {"main": 3}) + assert_functions(code, Languages.Java, {"main": 3}) def test_function_with_throws(): @@ -23,7 +23,7 @@ def test_function_with_throws(): } """ - assert_units(code, Languages.Java, {"foo": 3}) + assert_functions(code, Languages.Java, {"foo": 3}) def test_two_functions(): @@ -38,7 +38,7 @@ def test_two_functions(): } """ - assert_units(code, Languages.Java, {"one": 3, "two": 3}) + assert_functions(code, Languages.Java, {"one": 3, "two": 3}) def test_nested_class(): @@ -55,7 +55,7 @@ class Foo { } """ - assert_units(code, Languages.Java, {"Foo": 3, "foobar": 3}) + assert_functions(code, Languages.Java, {"Foo": 3, "foobar": 3}) def test_anonymous_class(): @@ -74,9 +74,8 @@ class Foo { } } """ - print_units(code, Languages.Java) - assert_units(code, Languages.Java, {"Foo": 6, "preVisitDirectory": 6}) + assert_functions(code, Languages.Java, {"Foo": 6, "preVisitDirectory": 6}) def test_record_class(): @@ -95,7 +94,7 @@ def test_record_class(): } """ - assert_units(code, Languages.Java, {"main": 4}) + assert_functions(code, Languages.Java, {"main": 4}) def test_method_with_anonymous_class(): @@ -108,7 +107,7 @@ class Foo { } """ - assert_units(code, Languages.Java, {"foo": 4}) + assert_functions(code, Languages.Java, {"foo": 4}) def test_abstract_methods(): @@ -123,7 +122,7 @@ def test_abstract_methods(): } """ - assert_units(code, Languages.Java, {'foobar': 3}) + assert_functions(code, Languages.Java, {'foobar': 3}) def test_abstract_methods_with_inner_class(): @@ -139,4 +138,4 @@ class Bar { } """ - assert_units(code, Languages.Java, {'foobar': 3}) + assert_functions(code, Languages.Java, {'foobar': 3}) diff --git a/tests/languages/test_JavaScript.py b/tests/languages/test_JavaScript.py index cdd936a..63ec4ab 100644 --- a/tests/languages/test_JavaScript.py +++ b/tests/languages/test_JavaScript.py @@ -1,5 +1,5 @@ from codelimit.languages import Languages -from tests.conftest import assert_units +from tests.conftest import assert_functions def test_simple_function(): @@ -9,7 +9,7 @@ def test_simple_function(): } """ - assert_units(code, Languages.JavaScript, {"foo": 3}) + assert_functions(code, Languages.JavaScript, {"foo": 3}) def test_arrow_function(): @@ -19,7 +19,7 @@ def test_arrow_function(): } """ - assert_units(code, Languages.JavaScript, {"sayHello": 3}) + assert_functions(code, Languages.JavaScript, {"sayHello": 3}) def test_nested_functions(): @@ -38,7 +38,7 @@ def test_nested_functions(): sayHelloWorld(); """ - assert_units( + assert_functions( code, Languages.JavaScript, {"sayHelloWorld": 4, "sayHello": 3, "sayWorld": 3}, @@ -56,7 +56,8 @@ def test_top_level_anonymous_functions_are_skipped(): }); """ - assert_units(code, Languages.JavaScript, {"sayHelloWorld": 3}) + assert_functions(code, Languages.JavaScript, {"sayHelloWorld": 3}) + def test_nested_anonymous_functions_are_skipped(): code = """ @@ -71,4 +72,4 @@ def test_nested_anonymous_functions_are_skipped(): } """ - assert_units(code, Languages.JavaScript, {"say": 5, "helloWorld": 3}) \ No newline at end of file + assert_functions(code, Languages.JavaScript, {"say": 5, "helloWorld": 3}) diff --git a/tests/languages/test_Python.py b/tests/languages/test_Python.py index f3ef733..c17dfb9 100644 --- a/tests/languages/test_Python.py +++ b/tests/languages/test_Python.py @@ -3,7 +3,7 @@ from codelimit.common.lexer_utils import lex from codelimit.languages import Languages from codelimit.languages.Python import _get_indentation, _get_token_lines -from tests.conftest import assert_units +from tests.conftest import assert_functions def test_simple_function(): @@ -12,7 +12,7 @@ def foo(): pass """ - assert_units(code, Languages.Python, {"foo": 2}) + assert_functions(code, Languages.Python, {"foo": 2}) def test_simple_function_larger_block(): @@ -22,7 +22,7 @@ def foo(): spam = eggs """ - assert_units(code, Languages.Python, {"foo": 3}) + assert_functions(code, Languages.Python, {"foo": 3}) def test_two_functions(): @@ -34,7 +34,7 @@ def bar(): foo() """ - assert_units(code, Languages.Python, {"foo": 2, "bar": 2}) + assert_functions(code, Languages.Python, {"foo": 2, "bar": 2}) def test_return_type(): @@ -45,7 +45,7 @@ def bar( bar = foo """ - assert_units(code, Languages.Python, {"bar": 4}) + assert_functions(code, Languages.Python, {"bar": 4}) def test_two_functions_with_return_types(): @@ -61,7 +61,7 @@ def foo( foo = bar """ - assert_units(code, Languages.Python, {"bar": 4, "foo": 4}) + assert_functions(code, Languages.Python, {"bar": 4, "foo": 4}) def test_get_indentation(): @@ -78,7 +78,7 @@ def test_get_indentation(): def test_no_functions(): code = "" - assert_units(code, Languages.Python, {}) + assert_functions(code, Languages.Python, {}) def test_trailing_global_code(): @@ -91,7 +91,7 @@ def foo(): ] """ - assert_units(code, Languages.Python, {"foo": 2}) + assert_functions(code, Languages.Python, {"foo": 2}) def test_get_headers_multi_header_with_comment(): @@ -104,7 +104,7 @@ def bar(): foo() """ - assert_units(code, Languages.Python, {"foo": 2, "bar": 2}) + assert_functions(code, Languages.Python, {"foo": 2, "bar": 2}) def test_do_not_count_comment_lines(): @@ -115,7 +115,7 @@ def foo(): # This is also a comment """ - assert_units(code, Languages.Python, {"foo": 2}) + assert_functions(code, Languages.Python, {"foo": 2}) def test_header_with_defaults(): @@ -131,7 +131,7 @@ def foo( pass """ - assert_units(code, Languages.Python, {"foo": 9}) + assert_functions(code, Languages.Python, {"foo": 9}) def test_header_type_hints(): @@ -142,7 +142,7 @@ def foo( pass """ - assert_units(code, Languages.Python, {"foo": 4}) + assert_functions(code, Languages.Python, {"foo": 4}) def test_skip_function_with_nocl_comment_in_header(): @@ -159,7 +159,7 @@ def foo( bar = foo """ - assert_units(code, Languages.Python, {"foo": 5}) + assert_functions(code, Languages.Python, {"foo": 5}) def test_function_with_type_hints(): @@ -171,7 +171,7 @@ def foo( foo = bar """ - assert_units(code, Languages.Python, {"foo": 5}) + assert_functions(code, Languages.Python, {"foo": 5}) def test_line_continuation(): @@ -182,7 +182,7 @@ def say_hello(): "world") """ - assert_units(code, Languages.Python, {"say_hello": 4}) + assert_functions(code, Languages.Python, {"say_hello": 4}) def test_if_statement(): @@ -195,7 +195,7 @@ def foo(): bar = foo """ - assert_units(code, Languages.Python, {"foo": 6}) + assert_functions(code, Languages.Python, {"foo": 6}) def test_get_token_lines(): diff --git a/tests/languages/test_TypeScript.py b/tests/languages/test_TypeScript.py index fcdbad8..523cb90 100644 --- a/tests/languages/test_TypeScript.py +++ b/tests/languages/test_TypeScript.py @@ -1,5 +1,5 @@ from codelimit.languages import Languages -from tests.conftest import assert_units +from tests.conftest import assert_functions def test_simple_function(): @@ -9,7 +9,7 @@ def test_simple_function(): } """ - assert_units(code, Languages.TypeScript, {"foo": 3}) + assert_functions(code, Languages.TypeScript, {"foo": 3}) def test_arrow_function(): @@ -19,7 +19,7 @@ def test_arrow_function(): } """ - assert_units(code, Languages.TypeScript, {"sayHello": 3}) + assert_functions(code, Languages.TypeScript, {"sayHello": 3}) def test_nested_functions(): @@ -33,4 +33,4 @@ def test_nested_functions(): } """ - assert_units(code, Languages.TypeScript, {"Outer": 3, "sayHello": 3}) + assert_functions(code, Languages.TypeScript, {"Outer": 3, "sayHello": 3})