From 05dc8a09cfe96596699ba5c4291c1999da44cf37 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 3 Sep 2026 16:24:56 +0000 Subject: [PATCH] Include README-advertised language extensions in analysis The Supported Languages table already lists C++ .cxx/.hh/.hxx, Scala scripts, Groovy .gvy, Erlang headers, literate Haskell, F#/OCaml signatures, ClojureScript, and PowerShell data files. Those paths were dropped from folder, GitHub, and ZIP analysis because they were neither code nor text, even though the CLI already watched them for live diffs. Co-authored-by: Braedon Saunders --- index.html | 6 +- tests/advertised-extensions.test.mjs | 93 ++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 tests/advertised-extensions.test.mjs diff --git a/index.html b/index.html index 2a8aceb1..46cf37ee 100644 --- a/index.html +++ b/index.html @@ -989,11 +989,11 @@ ruby:{grammar:'ruby',exts:['.rb'],coverage:'available'}, php:{grammar:'php',exts:['.php'],coverage:'available'}, c:{grammar:'c',exts:['.c','.h'],coverage:'available'}, - cpp:{grammar:'cpp',exts:['.cpp','.cc','.hpp'],coverage:'available'}, + cpp:{grammar:'cpp',exts:['.cpp','.cc','.cxx','.hpp','.hh','.hxx'],coverage:'available'}, csharp:{grammar:'c_sharp',exts:['.cs'],coverage:'available'}, swift:{grammar:'swift',exts:['.swift'],coverage:'available'}, kotlin:{grammar:'kotlin',exts:['.kt','.kts'],coverage:'available'}, - scala:{grammar:'scala',exts:['.scala'],coverage:'available'}, + scala:{grammar:'scala',exts:['.scala','.sc'],coverage:'available'}, elixir:{grammar:'elixir',exts:['.ex','.exs'],coverage:'available'}, lua:{grammar:'lua',exts:['.lua'],coverage:'available'}, bash:{grammar:'bash',exts:['.sh','.bash','.zsh','.fish'],coverage:'available'} @@ -1073,7 +1073,7 @@ if(Parser.isCode(filename))return 'heuristic-regex'; return 'text'; }, - codeExts:['.js','.jsx','.ts','.tsx','.mjs','.cjs','.py','.pyw','.pyi','.java','.go','.rb','.php','.rs','.c','.cpp','.cc','.h','.hpp','.cs','.swift','.kt','.kts','.scala','.clj','.ex','.exs','.erl','.hs','.lua','.r','.R','.jl','.dart','.elm','.fs','.fsx','.ml','.pl','.pm','.sh','.bash','.zsh','.fish','.ps1','.psm1','.groovy','.gradle','.vba','.bas','.cls','.xlsm','.xlam','.xlsb','.xla','.xlw','.pas','.pp','.dpr','.dpk','.lpr','.inc'], + codeExts:['.js','.jsx','.ts','.tsx','.mjs','.cjs','.py','.pyw','.pyi','.java','.go','.rb','.php','.rs','.c','.cpp','.cc','.cxx','.h','.hpp','.hh','.hxx','.cs','.swift','.kt','.kts','.scala','.sc','.clj','.cljs','.cljc','.ex','.exs','.erl','.hrl','.hs','.lhs','.lua','.r','.R','.jl','.dart','.elm','.fs','.fsi','.fsx','.ml','.mli','.pl','.pm','.sh','.bash','.zsh','.fish','.ps1','.psm1','.psd1','.groovy','.gvy','.gradle','.vba','.bas','.cls','.xlsm','.xlam','.xlsb','.xla','.xlw','.pas','.pp','.dpr','.dpk','.lpr','.inc'], scriptContainerExts:['.html','.htm','.xhtml','.vue','.svelte'], textExts:['.md','.markdown','.txt','.json','.jsonl','.yaml','.yml','.toml','.xml','.html','.htm','.css','.scss','.sass','.less','.svg','.graphql','.gql','.sql','.prisma','.proto','.tf','.tfvars','.env','.env.example','.gitignore','.gitattributes','.gitmodules','.eslintrc','.prettierrc','.babelrc','.editorconfig','.ini','.cfg','.conf','.properties','.lock','.csv','.tsv','.rst','.tex','.cmake','.rake','.vba','.bas','.cls','.xlsm','.xlam','.xlsb','.xla','.xlw','.mod','.sum'], textNames:['dockerfile','containerfile','makefile','rakefile','gemfile','podfile','pipfile','procfile','brewfile','justfile','taskfile','cmakelists.txt','license','copying','notice','readme','changelog','authors','contributors','owners','codeowners','go.mod','go.sum'], diff --git a/tests/advertised-extensions.test.mjs b/tests/advertised-extensions.test.mjs new file mode 100644 index 00000000..56addd5b --- /dev/null +++ b/tests/advertised-extensions.test.mjs @@ -0,0 +1,93 @@ +import assert from 'node:assert/strict'; +import { readFile } from 'node:fs/promises'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import test from 'node:test'; +import vm from 'node:vm'; +import { isWatchableFile } from '../cli/codeflow.mjs'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const repoRoot = join(__dirname, '..'); +const html = await readFile(join(repoRoot, 'index.html'), 'utf8'); +const start = html.indexOf('// ===== CODEFLOW_ANALYZER_START ====='); +const end = html.indexOf('// ===== CODEFLOW_ANALYZER_END =====', start); +const context = { + console, + TreeSitter: undefined, + Babel: undefined, + acorn: undefined, + getSecurityScanContent(file) { return file && file.content ? file.content : ''; }, + isSanitizedPreviewRenderer() { return false; }, +}; + +vm.createContext(context); +vm.runInContext( + html.slice(start, end) + '\nthis.Parser = Parser; this.buildAnalysisData = buildAnalysisData;', + context +); + +const { Parser } = context; + +// README Supported Languages lists these extensions. Until they are in +// codeExts they are neither code nor text, so folder/GitHub/ZIP analysis +// drops them even though the CLI already watches them for live diffs. +const advertisedCodeExts = [ + 'util.cxx', + 'types.hh', + 'types.hxx', + 'scratch.sc', + 'build.gvy', + 'records.hrl', + 'Guide.lhs', + 'Api.fsi', + 'api.mli', + 'app.cljs', + 'shared.cljc', + 'Config.psd1', +]; + +test('README-advertised language extensions are analyzed as code', () => { + for (const name of advertisedCodeExts) { + assert.equal(Parser.isCode(name), true, name); + assert.equal(Parser.isIncluded(name), true, name); + } +}); + +test('CLI already watches the advertised extensions the analyzer used to drop', () => { + for (const name of advertisedCodeExts) { + assert.equal(isWatchableFile(name), true, name); + } +}); + +test('C++ helpers on .cxx/.hh extract the same way as .cpp/.hpp', () => { + const cxx = [ + 'int add_pair(int a, int b) {', + ' return a + b;', + '}', + ].join('\n'); + const names = Parser.extract(cxx, 'math.cxx').map((fn) => fn.name); + assert.ok(names.includes('add_pair'), names.join(',')); + + const header = [ + 'inline int times_two(int n) {', + ' return n * 2;', + '}', + ].join('\n'); + const headerNames = Parser.extract(header, 'math.hh').map((fn) => fn.name); + assert.ok(headerNames.includes('times_two'), headerNames.join(',')); +}); + +test('Scala scripts and Groovy sources extract advertised routines', () => { + const scala = Parser.extract('def greet(name: String) = s"hi $name"\n', 'scratch.sc').map((fn) => fn.name); + assert.ok(scala.includes('greet'), scala.join(',')); + + const groovy = Parser.extract('public String shout(String word) {\n return word.toUpperCase()\n}\n', 'build.gvy').map((fn) => fn.name); + assert.ok(groovy.includes('shout'), groovy.join(',')); +}); + +test('tree-sitter C++/Scala configs include the advertised extra extensions', () => { + assert.equal(Parser.getTreeSitterConfig('math.cxx').grammar, 'cpp'); + assert.equal(Parser.getTreeSitterConfig('types.hh').grammar, 'cpp'); + assert.equal(Parser.getTreeSitterConfig('types.hxx').grammar, 'cpp'); + assert.equal(Parser.getTreeSitterConfig('scratch.sc').grammar, 'scala'); +});