Ragex.LanguageSupport (Ragex v0.38.0)

View Source

Shared language detection, adapter resolution, and parsing utilities.

Consolidates duplicated detect_language/1, get_adapter/1, parse_document/3, and find_source_files/2 helpers that were previously copy-pasted across Security, Smells, BusinessLogic, MetastaticBridge, Semantic, and other modules.

Supported Languages

  • :elixir -- .ex, .exs
  • :erlang -- .erl, .hrl
  • :python -- .py
  • :ruby -- .rb
  • :haskell -- .hs
  • :javascript -- .js, .jsx, .mjs, .cjs
  • :typescript -- .ts, .tsx
  • :cure -- .cure
  • :agda -- .agda, .lagda

Usage

alias Ragex.LanguageSupport

language = LanguageSupport.detect_language("lib/my_module.ex")
# => :elixir

{:ok, adapter} = LanguageSupport.get_adapter(:elixir)
# => {:ok, Metastatic.Adapters.Elixir}

{:ok, doc} = LanguageSupport.parse_document(source, :elixir)

Summary

Functions

Returns default directory exclusion patterns.

Detects language from a file path extension.

Ensures Metastatic's semantic operation patterns are registered.

Finds supported source files in a directory.

Returns the Metastatic adapter module for a language.

Returns whether a language has a Metastatic adapter.

Returns the list of file extensions with Metastatic adapter support.

Parses source code into a Metastatic.Document via the appropriate adapter.

Clears the ensure_semantic_patterns/0 guard so the next call re-registers.

Returns the list of all supported file extensions.

Types

language()

@type language() ::
  :elixir
  | :erlang
  | :python
  | :ruby
  | :haskell
  | :javascript
  | :typescript
  | :cure
  | :agda
  | :march
  | :unknown

Functions

default_exclude_patterns()

@spec default_exclude_patterns() :: [String.t()]

Returns default directory exclusion patterns.

detect_language(path)

@spec detect_language(String.t()) :: language()

Detects language from a file path extension.

Examples

iex> Ragex.LanguageSupport.detect_language("lib/my_module.ex")
:elixir

iex> Ragex.LanguageSupport.detect_language("script.py")
:python

iex> Ragex.LanguageSupport.detect_language("unknown.xyz")
:unknown

ensure_semantic_patterns()

@spec ensure_semantic_patterns() :: :ok

Ensures Metastatic's semantic operation patterns are registered.

Metastatic registers its OpKind pattern tables from module-level register_all/0 calls at the bottom of each Metastatic.Semantic.Domains.* module. Elixir only evaluates that kind of top-level side effect while the module is being compiled, so in any build where the beams are loaded from disk (a release, a mix compile from a clean _build, a dependency compiled ahead of time, ...) the tables stay empty and Enricher.enrich_tree/2 silently attaches no op_kind metadata at all.

Empty pattern tables mean semantic analysis degrades to "no operations detected" with no error anywhere, so Ragex calls this once during application start (see Ragex.Application) and again from every code path that reads op_kind.

The call is idempotent -- Metastatic.Semantic.Patterns.register/3 appends to the existing list -- so it is guarded by a :persistent_term flag to keep repeated calls cheap and to avoid growing the tables on every invocation.

Returns :ok even when the domain modules are unavailable, so a Metastatic version that reorganises them cannot break startup.

find_source_files(path, opts \\ [])

@spec find_source_files(String.t(), keyword()) ::
  {:ok, [String.t()]} | {:error, term()}

Finds supported source files in a directory.

Options

  • :recursive -- recursively search subdirectories (default: true)
  • :metastatic_only -- only include languages with Metastatic adapters (default: false)
  • :exclude_patterns -- patterns/directories to exclude (default: common build/deps dirs)

Examples

{:ok, files} = LanguageSupport.find_source_files("lib/")
{:ok, files} = LanguageSupport.find_source_files("lib/", metastatic_only: true)

get_adapter(language)

@spec get_adapter(language()) ::
  {:ok, module()} | {:error, {:unsupported_language, language()}}

Returns the Metastatic adapter module for a language.

Examples

iex> Ragex.LanguageSupport.get_adapter(:elixir)
{:ok, Metastatic.Adapters.Elixir}

iex> Ragex.LanguageSupport.get_adapter(:javascript)
{:ok, Metastatic.Adapters.JavaScript}

has_adapter?(language)

@spec has_adapter?(language()) :: boolean()

Returns whether a language has a Metastatic adapter.

metastatic_extensions()

@spec metastatic_extensions() :: [String.t()]

Returns the list of file extensions with Metastatic adapter support.

parse_document(content, language, opts \\ [])

@spec parse_document(String.t(), language(), keyword()) ::
  {:ok, Metastatic.Document.t()} | {:error, term()}

Parses source code into a Metastatic.Document via the appropriate adapter.

Parameters

  • content -- source code string
  • language -- language atom (or auto-detect via :path option)
  • opts -- keyword options
    • :path -- file path for auto-detection (used only if language is not provided)

Examples

{:ok, doc} = LanguageSupport.parse_document(source, :elixir)

parse_file(path, opts \\ [])

@spec parse_file(String.t(), keyword()) ::
  {:ok, Metastatic.Document.t()} | {:error, term()}

Parses a file into a Metastatic.Document.

Reads the file, detects the language, and parses.

Options

  • :language -- override language detection (default: auto-detect from extension)

Examples

{:ok, doc} = LanguageSupport.parse_file("lib/my_module.ex")

reset_semantic_patterns()

@spec reset_semantic_patterns() :: :ok

Clears the ensure_semantic_patterns/0 guard so the next call re-registers.

Intended for tests that clear Metastatic's pattern tables.

should_exclude?(file_path, root_dir, exclude_patterns \\ ["node_modules", ".git", ".hg", ".svn", "_build", "deps", "target", "dist", "build", "coverage", ".elixir_ls", "__pycache__", ".pytest_cache", ".mypy_cache", ".bundle", "vendor"])

@spec should_exclude?(String.t(), String.t(), [String.t()]) :: boolean()

Checks if a file path should be excluded based on relative path components from the root directory and configured exclusion patterns.

supported_extensions()

@spec supported_extensions() :: [String.t()]

Returns the list of all supported file extensions.