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'); +});