From 847964edb0e361e9ca2b30234541698dfa01d555 Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Fri, 12 Dec 2025 12:49:10 -0700 Subject: [PATCH 01/15] =?UTF-8?q?=F0=9F=94=90=20Add=20signing=20cert=20(ex?= =?UTF-8?q?p=202025-04-29);=20prepare=20release?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ancient.yml | 81 ------------------------------- .github/workflows/coverage.yml | 6 +-- .github/workflows/jruby.yml | 72 --------------------------- .github/workflows/legacy.yml | 76 ----------------------------- .github/workflows/main.yml | 29 ----------- .github/workflows/unsupported.yml | 76 ----------------------------- Appraisals | 28 ----------- CHANGELOG.md | 8 +-- README.md | 2 +- certs/pboling.pem | 27 +++++++++++ 10 files changed, 36 insertions(+), 369 deletions(-) delete mode 100644 .github/workflows/ancient.yml delete mode 100644 .github/workflows/jruby.yml delete mode 100644 .github/workflows/legacy.yml delete mode 100644 .github/workflows/main.yml delete mode 100644 .github/workflows/unsupported.yml create mode 100644 certs/pboling.pem diff --git a/.github/workflows/ancient.yml b/.github/workflows/ancient.yml deleted file mode 100644 index a28c90c..0000000 --- a/.github/workflows/ancient.yml +++ /dev/null @@ -1,81 +0,0 @@ -name: MRI 2.3, 2.4, 2.5 (EOL) - -permissions: - contents: read - -on: - push: - branches: - - 'main' - - '*-stable' - tags: - - '!*' # Do not execute on tags - pull_request: - branches: - - '*' - # Allow manually triggering the workflow. - workflow_dispatch: - -# Cancels all previous workflow runs for the same branch that have not yet completed. -concurrency: - # The concurrency group contains the workflow name and the branch name. - group: "${{ github.workflow }}-${{ github.ref }}" - cancel-in-progress: true - -jobs: - test: - if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" - name: Specs ${{ matrix.ruby }} ${{ matrix.appraisal }}${{ matrix.name_extra || '' }} - runs-on: ubuntu-22.04 - continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} - env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps - BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile - strategy: - fail-fast: false - matrix: - include: - # Ruby 2.3 - - ruby: "ruby-2.3" - appraisal: "ruby-2-3" - exec_cmd: "rake test" - gemfile: "Appraisal.root" - rubygems: "3.3.27" - bundler: "2.3.27" - - # Ruby 2.4 - - ruby: "ruby-2.4" - appraisal: "ruby-2-4" - exec_cmd: "rake test" - gemfile: "Appraisal.root" - rubygems: "3.3.27" - bundler: "2.3.27" - - # Ruby 2.5 - - ruby: "ruby-2.5" - appraisal: "ruby-2-5" - exec_cmd: "rake test" - gemfile: "Appraisal.root" - rubygems: "3.3.27" - bundler: "2.3.27" - - steps: - - name: Checkout - uses: actions/checkout@v6 - - - name: Setup Ruby & RubyGems - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{ matrix.ruby }} - rubygems: ${{ matrix.rubygems }} - bundler: ${{ matrix.bundler }} - bundler-cache: false - - # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) - # We need to do this first to get appraisal installed. - # NOTE: This does not use the primary Gemfile at all. - - name: Install Root Appraisal - run: bundle - - name: Appraisal for ${{ matrix.appraisal }} - run: bundle exec appraisal ${{ matrix.appraisal }} bundle - - name: Tests for ${{ matrix.ruby }} via ${{ matrix.exec_cmd }} - run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }} diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 85f70d8..73239f0 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -6,8 +6,8 @@ permissions: id-token: write env: - K_SOUP_COV_MIN_BRANCH: 100 - K_SOUP_COV_MIN_LINE: 100 + K_SOUP_COV_MIN_BRANCH: 85 + K_SOUP_COV_MIN_LINE: 96 K_SOUP_COV_MIN_HARD: true K_SOUP_COV_FORMATTERS: "xml,rcov,lcov,tty" K_SOUP_COV_DO: true @@ -115,7 +115,7 @@ jobs: hide_complexity: true indicators: true output: both - thresholds: '100 100' + thresholds: '96 85' continue-on-error: ${{ matrix.experimental != 'false' }} - name: Add Coverage PR Comment diff --git a/.github/workflows/jruby.yml b/.github/workflows/jruby.yml deleted file mode 100644 index 3ff8d8c..0000000 --- a/.github/workflows/jruby.yml +++ /dev/null @@ -1,72 +0,0 @@ -name: JRuby - -permissions: - contents: read - -env: - K_SOUP_COV_DO: false - -on: - push: - branches: - - 'main' - - '*-stable' - tags: - - '!*' # Do not execute on tags - pull_request: - branches: - - '*' - # Allow manually triggering the workflow. - workflow_dispatch: - -# Cancels all previous workflow runs for the same branch that have not yet completed. -concurrency: - # The concurrency group contains the workflow name and the branch name. - group: "${{ github.workflow }}-${{ github.ref }}" - cancel-in-progress: true - -jobs: - test: - if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" - name: Specs ${{ matrix.ruby }} ${{ matrix.appraisal }}${{ matrix.name_extra || '' }} - runs-on: ubuntu-22.04 - continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} - env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps - BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile - strategy: - matrix: - include: - # jruby-9.4 (targets Ruby 3.1 compatibility) - - ruby: "jruby-9.4" - appraisal: "ruby-3-1" - exec_cmd: "rake test" - gemfile: "Appraisal.root" - rubygems: default - bundler: default - - steps: - - name: Checkout - if: ${{ !env.ACT }} - uses: actions/checkout@v6 - - - name: Setup Ruby & RubyGems - if: ${{ !env.ACT }} - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{ matrix.ruby }} - rubygems: ${{ matrix.rubygems }} - bundler: ${{ matrix.bundler }} - bundler-cache: false - - # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) - # We need to do this first to get appraisal installed. - # NOTE: This does not use the primary Gemfile at all. - - name: Install Root Appraisal - if: ${{ !env.ACT }} - run: bundle - - name: Appraisal for ${{ matrix.appraisal }} - if: ${{ !env.ACT }} - run: bundle exec appraisal ${{ matrix.appraisal }} bundle - - name: Tests for ${{ matrix.ruby }} via ${{ matrix.exec_cmd }} - if: ${{ !env.ACT }} - run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }} diff --git a/.github/workflows/legacy.yml b/.github/workflows/legacy.yml deleted file mode 100644 index 9fe3c7c..0000000 --- a/.github/workflows/legacy.yml +++ /dev/null @@ -1,76 +0,0 @@ -name: MRI 3.0, 3.1 (EOL) - -permissions: - contents: read - -env: - K_SOUP_COV_DO: false - -on: - push: - branches: - - 'main' - - '*-stable' - tags: - - '!*' # Do not execute on tags - pull_request: - branches: - - '*' - # Allow manually triggering the workflow. - workflow_dispatch: - -# Cancels all previous workflow runs for the same branch that have not yet completed. -concurrency: - # The concurrency group contains the workflow name and the branch name. - group: "${{ github.workflow }}-${{ github.ref }}" - cancel-in-progress: true - -jobs: - test: - if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" - name: Specs ${{ matrix.ruby }} ${{ matrix.appraisal }}${{ matrix.name_extra || '' }} - runs-on: ubuntu-22.04 - continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} - env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps - BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile - strategy: - fail-fast: false - matrix: - include: - # Ruby 3.0 - - ruby: "ruby-3.0" - appraisal: "ruby-3-0" - exec_cmd: "rake test" - gemfile: "Appraisal.root" - rubygems: '3.5.23' - bundler: '2.5.23' - - # Ruby 3.1 - - ruby: "ruby-3.1" - appraisal: "ruby-3-1" - exec_cmd: "rake test" - gemfile: "Appraisal.root" - rubygems: '3.6.9' - bundler: '2.6.9' - - steps: - - name: Checkout - uses: actions/checkout@v6 - - - name: Setup Ruby & RubyGems - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{ matrix.ruby }} - rubygems: ${{ matrix.rubygems }} - bundler: ${{ matrix.bundler }} - bundler-cache: false - - # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) - # We need to do this first to get appraisal installed. - # NOTE: This does not use the primary Gemfile at all. - - name: Install Root Appraisal - run: bundle - - name: Appraisal for ${{ matrix.appraisal }} - run: bundle exec appraisal ${{ matrix.appraisal }} bundle - - name: Tests for ${{ matrix.ruby }} via ${{ matrix.exec_cmd }} - run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index c2be9d9..0000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Ruby - -on: - push: - branches: - - main - - pull_request: - -jobs: - build: - runs-on: ubuntu-latest - name: Ruby ${{ matrix.ruby }} - strategy: - matrix: - ruby: - - '3.4.7' - - steps: - - uses: actions/checkout@v6 - with: - persist-credentials: false - - name: Set up Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{ matrix.ruby }} - bundler-cache: true - - name: Run the default task - run: bundle exec rake diff --git a/.github/workflows/unsupported.yml b/.github/workflows/unsupported.yml deleted file mode 100644 index 4c938cb..0000000 --- a/.github/workflows/unsupported.yml +++ /dev/null @@ -1,76 +0,0 @@ -name: MRI 2.6 & 2.7 (EOL) - -permissions: - contents: read - -env: - K_SOUP_COV_DO: false - -on: - push: - branches: - - 'main' - - '*-stable' - tags: - - '!*' # Do not execute on tags - pull_request: - branches: - - '*' - # Allow manually triggering the workflow. - workflow_dispatch: - -# Cancels all previous workflow runs for the same branch that have not yet completed. -concurrency: - # The concurrency group contains the workflow name and the branch name. - group: "${{ github.workflow }}-${{ github.ref }}" - cancel-in-progress: true - -jobs: - test: - if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" - name: Specs ${{ matrix.ruby }} ${{ matrix.appraisal }}${{ matrix.name_extra || '' }} - runs-on: ubuntu-22.04 - continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} - env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps - BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile - strategy: - fail-fast: false - matrix: - include: - # Ruby 2.6 - - ruby: "ruby-2.6" - appraisal: "ruby-2-6" - exec_cmd: "rake test" - gemfile: "Appraisal.root" - rubygems: '3.4.22' - bundler: '2.4.22' - - # Ruby 2.7 - - ruby: "ruby-2.7" - appraisal: "ruby-2-7" - exec_cmd: "rake test" - gemfile: "Appraisal.root" - rubygems: '3.4.22' - bundler: '2.4.22' - - steps: - - name: Checkout - uses: actions/checkout@v6 - - - name: Setup Ruby & RubyGems - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{ matrix.ruby }} - rubygems: ${{ matrix.rubygems }} - bundler: ${{ matrix.bundler }} - bundler-cache: false - - # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) - # We need to do this first to get appraisal installed. - # NOTE: This does not use the primary Gemfile at all. - - name: Install Root Appraisal - run: bundle - - name: Appraisal for ${{ matrix.appraisal }} - run: bundle exec appraisal ${{ matrix.appraisal }} bundle - - name: Tests for ${{ matrix.ruby }} via ${{ matrix.exec_cmd }} - run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }} diff --git a/Appraisals b/Appraisals index 2d4cd44..dd9a504 100644 --- a/Appraisals +++ b/Appraisals @@ -47,34 +47,6 @@ appraise "dep-heads" do eval_gemfile "modular/runtime_heads.gemfile" end -appraise "ruby-2-3" do - eval_gemfile "modular/x_std_libs/r2.3/libs.gemfile" -end - -appraise "ruby-2-4" do - eval_gemfile "modular/x_std_libs/r2.4/libs.gemfile" -end - -appraise "ruby-2-5" do - eval_gemfile "modular/x_std_libs/r2.6/libs.gemfile" -end - -appraise "ruby-2-6" do - eval_gemfile "modular/x_std_libs/r2.6/libs.gemfile" -end - -appraise "ruby-2-7" do - eval_gemfile "modular/x_std_libs/r2/libs.gemfile" -end - -appraise "ruby-3-0" do - eval_gemfile "modular/x_std_libs/r3.1/libs.gemfile" -end - -appraise "ruby-3-1" do - eval_gemfile "modular/x_std_libs/r3.1/libs.gemfile" -end - appraise "ruby-3-2" do eval_gemfile "modular/x_std_libs/r3/libs.gemfile" end diff --git a/CHANGELOG.md b/CHANGELOG.md index fe9cba2..e907215 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,8 @@ Please file a bug if you notice a violation of semantic versioning. ### Added +- Initial release + ### Changed ### Deprecated @@ -30,6 +32,6 @@ Please file a bug if you notice a violation of semantic versioning. ### Security -## [0.1.0] - 2025-12-05 - -- Initial release +[Unreleased]: https://github.com/kettle-rb/bash-merge/compare/v1.0.0...HEAD +[1.0.0]: https://github.com/kettle-rb/bash-merge/compare/db525d5aedd0c895422a067629118f3fa9c3c22d...v1.0.0 +[1.0.0t]: https://github.com/kettle-rb/bash-merge/tags/v1.0.0 diff --git a/README.md b/README.md index 0373cfa..1935aae 100644 --- a/README.md +++ b/README.md @@ -640,7 +640,7 @@ See [LICENSE.txt][📄license] for the official [Copyright Notice][📄copyright @@ -581,6 +596,24 @@

 
 
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
 62
 63
 64
@@ -588,36 +621,20 @@ 

66 67 68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86

+69 -
# File 'lib/bash/merge/file_analysis.rb', line 62
+      
# File 'lib/bash/merge/file_analysis.rb', line 44
 
-def initialize(source, freeze_token: DEFAULT_FREEZE_TOKEN, signature_generator: nil, parser_path: nil)
+def initialize(source, freeze_token: DEFAULT_FREEZE_TOKEN, signature_generator: nil, parser_path: nil, **options)
   @source = source
   @lines = source.lines.map(&:chomp)
   @freeze_token = freeze_token
   @signature_generator = signature_generator
   @parser_path = parser_path || self.class.find_parser_path
   @errors = []
-
+  # **options captures any additional parameters (e.g., node_typing) for forward compatibility
+
   # Initialize comment tracking
   @comment_tracker = CommentTracker.new(source)
 
@@ -840,10 +857,7 @@ 

-

Find the parser library path

- -

Uses TreeHaver::GrammarFinder if available, otherwise
-searches common paths directly.

+

Find the parser library path using TreeHaver::GrammarFinder

@@ -874,47 +888,15 @@

 
 
-35
-36
-37
-38
-39
-40
-41
-42
-43
-44
-45
-46
-47
-48
-49
-50
-51
-52
-53
+32 +33 +34

-
# File 'lib/bash/merge/file_analysis.rb', line 35
+      
# File 'lib/bash/merge/file_analysis.rb', line 32
 
 def find_parser_path
-  # Use TreeHaver's GrammarFinder if available
-  if defined?(TreeHaver::GrammarFinder)
-    TreeHaver::GrammarFinder.new(:bash).find_library_path
-  else
-    # Fallback: check environment variable first
-    env_path = ENV["TREE_SITTER_BASH_PATH"]
-    return env_path if env_path && File.exist?(env_path)
-
-    # Search common paths
-    [
-      "/usr/lib/libtree-sitter-bash.so",
-      "/usr/lib64/libtree-sitter-bash.so",
-      "/usr/local/lib/libtree-sitter-bash.so",
-      "/opt/homebrew/lib/libtree-sitter-bash.dylib",
-      "/usr/local/lib/libtree-sitter-bash.dylib",
-    ].find { |path| File.exist?(path) }
-  end
+  TreeHaver::GrammarFinder.new(:bash).find_library_path
 end
@@ -988,12 +970,12 @@

 
 
-122
-123
-124
+105 +106 +107

-
# File 'lib/bash/merge/file_analysis.rb', line 122
+      
# File 'lib/bash/merge/file_analysis.rb', line 105
 
 def fallthrough_node?(value)
   value.is_a?(NodeWrapper) || value.is_a?(FreezeNode) || super
@@ -1060,12 +1042,12 @@ 

 
 
-115
-116
-117
+98 +99 +100

-
# File 'lib/bash/merge/file_analysis.rb', line 115
+      
# File 'lib/bash/merge/file_analysis.rb', line 98
 
 def freeze_block_at(line_num)
   @freeze_blocks.find { |fb| fb.location.cover?(line_num) }
@@ -1132,12 +1114,12 @@ 

 
 
-107
-108
-109
+90 +91 +92

-
# File 'lib/bash/merge/file_analysis.rb', line 107
+      
# File 'lib/bash/merge/file_analysis.rb', line 90
 
 def in_freeze_block?(line_num)
   @freeze_blocks.any? { |fb| fb.location.cover?(line_num) }
@@ -1185,14 +1167,14 @@ 

 
 
-128
-129
-130
-131
-132
+111 +112 +113 +114 +115

-
# File 'lib/bash/merge/file_analysis.rb', line 128
+      
# File 'lib/bash/merge/file_analysis.rb', line 111
 
 def root_node
   return unless valid?
@@ -1246,12 +1228,12 @@ 

 
 
-96
-97
-98
+79 +80 +81

-
# File 'lib/bash/merge/file_analysis.rb', line 96
+      
# File 'lib/bash/merge/file_analysis.rb', line 79
 
 def statements
   @nodes ||= []
@@ -1299,23 +1281,23 @@ 

 
 
-136
-137
-138
-139
-140
-141
-142
-143
-144
-145
-146
-147
-148
-149
+119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132

-
# File 'lib/bash/merge/file_analysis.rb', line 136
+      
# File 'lib/bash/merge/file_analysis.rb', line 119
 
 def top_level_statements
   return [] unless valid?
@@ -1374,12 +1356,12 @@ 

 
 
-90
-91
-92
+73 +74 +75

-
# File 'lib/bash/merge/file_analysis.rb', line 90
+      
# File 'lib/bash/merge/file_analysis.rb', line 73
 
 def valid?
   @errors.empty? && !@ast.nil?
@@ -1394,9 +1376,9 @@ 

diff --git a/docs/Bash/Merge/FreezeNode.html b/docs/Bash/Merge/FreezeNode.html index 68ff3d6..d7971fa 100644 --- a/docs/Bash/Merge/FreezeNode.html +++ b/docs/Bash/Merge/FreezeNode.html @@ -785,9 +785,9 @@

diff --git a/docs/Bash/Merge/MergeResult.html b/docs/Bash/Merge/MergeResult.html index e69de29..f1c131f 100644 --- a/docs/Bash/Merge/MergeResult.html +++ b/docs/Bash/Merge/MergeResult.html @@ -0,0 +1,1188 @@ + + + + + + + Class: Bash::Merge::MergeResult + + — Documentation by YARD 0.9.38 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: Bash::Merge::MergeResult + + + +

+
+ +
+
Inherits:
+
+ Ast::Merge::MergeResultBase + +
    +
  • Object
  • + + + + + +
+ show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/bash/merge/merge_result.rb
+
+ +
+ +

Overview

+
+

Tracks the result of a merge operation, including the merged content,
+decisions made, and statistics.

+ +

Inherits decision constants and base functionality from Ast::Merge::MergeResultBase.

+ + +
+
+
+ +
+

Examples:

+ + +

Basic usage

+
+ +
result = MergeResult.new
+result.add_line("echo 'hello'", decision: :kept_template, source: :template)
+result.to_bash # => "echo 'hello'\n"
+ +
+ + +
+ +

+ Constant Summary + collapse +

+ +
+ +
DECISION_KEPT_TEMPLATE = +
+
+

Inherit decision constants from base class

+ + +
+
+
+ + +
+
+
Ast::Merge::MergeResultBase::DECISION_KEPT_TEMPLATE
+ +
DECISION_KEPT_DEST = + +
+
Ast::Merge::MergeResultBase::DECISION_KEPT_DEST
+ +
DECISION_MERGED = + +
+
Ast::Merge::MergeResultBase::DECISION_MERGED
+ +
DECISION_ADDED = + +
+
Ast::Merge::MergeResultBase::DECISION_ADDED
+ +
DECISION_FREEZE_BLOCK = + +
+
Ast::Merge::MergeResultBase::DECISION_FREEZE_BLOCK
+ +
+ + + + + +

Instance Attribute Summary collapse

+
    + +
  • + + + #statistics ⇒ Hash + + + + + + + + + readonly + + + + + + + + + +

    Statistics about the merge.

    +
    + +
  • + + +
+ + + + + +

+ Instance Method Summary + collapse +

+ + + + + +
+

Constructor Details

+ +
+

+ + #initialize(**options) ⇒ MergeResult + + + + + +

+
+

Initialize a new merge result

+ + +
+
+
+

Parameters:

+
    + +
  • + + options + + + (Hash) + + + + — +

    Additional options for forward compatibility

    +
    + +
  • + +
+ + +
+ + + + +
+
+
+
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+
+
# File 'lib/bash/merge/merge_result.rb', line 27
+
+def initialize(**options)
+  super(**options)
+  @statistics = {
+    template_lines: 0,
+    dest_lines: 0,
+    merged_lines: 0,
+    freeze_preserved_lines: 0,
+    total_decisions: 0,
+  }
+end
+
+
+ +
+ +
+

Instance Attribute Details

+ + + +
+

+ + #statisticsHash (readonly) + + + + + +

+
+

Returns Statistics about the merge.

+ + +
+
+
+ +

Returns:

+
    + +
  • + + + (Hash) + + + + — +

    Statistics about the merge

    +
    + +
  • + +
+ +
+ + + + +
+
+
+
+23
+24
+25
+
+
# File 'lib/bash/merge/merge_result.rb', line 23
+
+def statistics
+  @statistics
+end
+
+
+ +
+ + +
+

Instance Method Details

+ + +
+

+ + #add_blank_line(decision: DECISION_MERGED, source: :merged) ⇒ Object + + + + + +

+
+

Add a blank line

+ + +
+
+
+

Parameters:

+
    + +
  • + + decision + + + (Symbol) + + + (defaults to: DECISION_MERGED) + + + — +

    Decision for the blank line

    +
    + +
  • + +
  • + + source + + + (Symbol) + + + (defaults to: :merged) + + + — +

    Source

    +
    + +
  • + +
+ + +
+ + + + +
+
+
+
+73
+74
+75
+
+
# File 'lib/bash/merge/merge_result.rb', line 73
+
+def add_blank_line(decision: DECISION_MERGED, source: :merged)
+  add_line("", decision: decision, source: source)
+end
+
+
+ +
+

+ + #add_freeze_block(freeze_node) ⇒ Object + + + + + +

+
+

Add content from a freeze block

+ + +
+
+
+

Parameters:

+
    + +
  • + + freeze_node + + + (FreezeNode) + + + + — +

    Freeze block to add

    +
    + +
  • + +
+ + +
+ + + + +
+
+
+
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+
+
# File 'lib/bash/merge/merge_result.rb', line 80
+
+def add_freeze_block(freeze_node)
+  freeze_node.lines.each_with_index do |line, idx|
+    add_line(
+      line.chomp,
+      decision: DECISION_FREEZE_BLOCK,
+      source: :destination,
+      original_line: freeze_node.start_line + idx,
+    )
+  end
+end
+
+
+ +
+

+ + #add_line(line, decision:, source:, original_line: nil) ⇒ Object + + + + + +

+
+

Add a single line to the result

+ + +
+
+
+

Parameters:

+
    + +
  • + + line + + + (String) + + + + — +

    Line content

    +
    + +
  • + +
  • + + decision + + + (Symbol) + + + + — +

    Decision that led to this line

    +
    + +
  • + +
  • + + source + + + (Symbol) + + + + — +

    Source of the line (:template, :destination, :merged)

    +
    + +
  • + +
  • + + original_line + + + (Integer, nil) + + + (defaults to: nil) + + + — +

    Original line number

    +
    + +
  • + +
+ + +
+ + + + +
+
+
+
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+
+
# File 'lib/bash/merge/merge_result.rb', line 44
+
+def add_line(line, decision:, source:, original_line: nil)
+  @lines << {
+    content: line,
+    decision: decision,
+    source: source,
+    original_line: original_line,
+  }
+
+  track_statistics(decision, source)
+  track_decision(decision, source, line: original_line)
+end
+
+
+ +
+

+ + #add_lines(lines, decision:, source:, start_line: nil) ⇒ Object + + + + + +

+
+

Add multiple lines to the result

+ + +
+
+
+

Parameters:

+
    + +
  • + + lines + + + (Array<String>) + + + + — +

    Lines to add

    +
    + +
  • + +
  • + + decision + + + (Symbol) + + + + — +

    Decision for all lines

    +
    + +
  • + +
  • + + source + + + (Symbol) + + + + — +

    Source of the lines

    +
    + +
  • + +
  • + + start_line + + + (Integer, nil) + + + (defaults to: nil) + + + — +

    Starting original line number

    +
    + +
  • + +
+ + +
+ + + + +
+
+
+
+62
+63
+64
+65
+66
+67
+
+
# File 'lib/bash/merge/merge_result.rb', line 62
+
+def add_lines(lines, decision:, source:, start_line: nil)
+  lines.each_with_index do |line, idx|
+    original_line = start_line ? start_line + idx : nil
+    add_line(line, decision: decision, source: source, original_line: original_line)
+  end
+end
+
+
+ +
+

+ + #add_node(node, decision:, source:, analysis:) ⇒ Object + + + + + +

+
+

Add content from a node wrapper

+ + +
+
+
+

Parameters:

+
    + +
  • + + node + + + (NodeWrapper) + + + + — +

    Node to add

    +
    + +
  • + +
  • + + decision + + + (Symbol) + + + + — +

    Decision that led to keeping this node

    +
    + +
  • + +
  • + + source + + + (Symbol) + + + + — +

    Source of the node

    +
    + +
  • + +
  • + + analysis + + + (FileAnalysis) + + + + — +

    Analysis for accessing source lines

    +
    + +
  • + +
+ + +
+ + + + +
+
+
+
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+
+
# File 'lib/bash/merge/merge_result.rb', line 97
+
+def add_node(node, decision:, source:, analysis:)
+  return unless node.start_line && node.end_line
+
+  (node.start_line..node.end_line).each do |line_num|
+    line = analysis.line_at(line_num)
+    next unless line
+
+    add_line(line.chomp, decision: decision, source: source, original_line: line_num)
+  end
+end
+
+
+ +
+

+ + #contentString + + + + + +

+
+

Alias for to_bash

+ + +
+
+
+ +

Returns:

+
    + +
  • + + + (String) + + + +
  • + +
+ +
+ + + + +
+
+
+
+120
+121
+122
+
+
# File 'lib/bash/merge/merge_result.rb', line 120
+
+def content
+  to_bash
+end
+
+
+ +
+

+ + #to_bashString + + + + + +

+
+

Get the merged content as a Bash string

+ + +
+
+
+ +

Returns:

+
    + +
  • + + + (String) + + + +
  • + +
+ +
+ + + + +
+
+
+
+111
+112
+113
+114
+115
+116
+
+
# File 'lib/bash/merge/merge_result.rb', line 111
+
+def to_bash
+  content = @lines.map { |l| l[:content] }.join("\n")
+  # Ensure trailing newline
+  content += "\n" unless content.end_with?("\n") || content.empty?
+  content
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/docs/Bash/Merge/NodeWrapper.html b/docs/Bash/Merge/NodeWrapper.html index c5c51f7..e69de29 100644 --- a/docs/Bash/Merge/NodeWrapper.html +++ b/docs/Bash/Merge/NodeWrapper.html @@ -1,2866 +0,0 @@ - - - - - - - Class: Bash::Merge::NodeWrapper - - — Documentation by YARD 0.9.38 - - - - - - - - - - - - - - - - - - - -
- - -

Class: Bash::Merge::NodeWrapper - - - -

-
- -
-
Inherits:
-
- Object - -
    -
  • Object
  • - - - -
- show all - -
-
- - - - - - - - - - - -
-
Defined in:
-
lib/bash/merge/node_wrapper.rb
-
- -
- -

Overview

-
-

Wraps TreeHaver nodes with comment associations, line information, and signatures.
-This provides a unified interface for working with Bash AST nodes during merging.

- - -
-
-
- -
-

Examples:

- - -

Basic usage

-
- -
parser = TreeHaver::Parser.new
-parser.language = TreeHaver::Language.bash
-tree = parser.parse(source)
-wrapper = NodeWrapper.new(tree.root_node, lines: source.lines, source: source)
-wrapper.signature # => [:program, ...]
- -
- - -
- - - -

Instance Attribute Summary collapse

- - - - - - -

- Instance Method Summary - collapse -

- - - - -
-

Constructor Details

- -
-

- - #initialize(node, lines:, source: nil, leading_comments: [], inline_comment: nil) ⇒ NodeWrapper - - - - - -

-
-

Returns a new instance of NodeWrapper.

- - -
-
-
-

Parameters:

-
    - -
  • - - node - - - (TreeHaver::Node) - - - - — -

    tree-haver node to wrap

    -
    - -
  • - -
  • - - lines - - - (Array<String>) - - - - — -

    Source lines for content extraction

    -
    - -
  • - -
  • - - source - - - (String) - - - (defaults to: nil) - - - — -

    Original source string for byte-based text extraction

    -
    - -
  • - -
  • - - leading_comments - - - (Array<Hash>) - - - (defaults to: []) - - - — -

    Comments before this node

    -
    - -
  • - -
  • - - inline_comment - - - (Hash, nil) - - - (defaults to: nil) - - - — -

    Inline comment on the node’s line

    -
    - -
  • - -
- - -
- - - - -
-
-
-
-41
-42
-43
-44
-45
-46
-47
-48
-49
-50
-51
-52
-53
-54
-55
-56
-57
-58
-59
-60
-
-
# File 'lib/bash/merge/node_wrapper.rb', line 41
-
-def initialize(node, lines:, source: nil, leading_comments: [], inline_comment: nil)
-  @node = node
-  @lines = lines
-  @source = source || lines.join("\n")
-  @leading_comments = leading_comments
-  @inline_comment = inline_comment
-
-  # Extract line information from the tree-haver node (0-indexed to 1-indexed)
-  if node.respond_to?(:start_point)
-    point = node.start_point
-    @start_line = (point.respond_to?(:row) ? point.row : point[:row]) + 1
-  end
-  if node.respond_to?(:end_point)
-    point = node.end_point
-    @end_line = (point.respond_to?(:row) ? point.row : point[:row]) + 1
-  end
-
-  # Handle edge case where end_line might be before start_line
-  @end_line = @start_line if @start_line && @end_line && @end_line < @start_line
-end
-
-
- -
- -
-

Instance Attribute Details

- - - -
-

- - #end_lineInteger (readonly) - - - - - -

-
-

Returns End line (1-based).

- - -
-
-
- -

Returns:

-
    - -
  • - - - (Integer) - - - - — -

    End line (1-based)

    -
    - -
  • - -
- -
- - - - -
-
-
-
-28
-29
-30
-
-
# File 'lib/bash/merge/node_wrapper.rb', line 28
-
-def end_line
-  @end_line
-end
-
-
- - - -
-

- - #inline_commentHash? (readonly) - - - - - -

-
-

Returns Inline/trailing comment on the same line.

- - -
-
-
- -

Returns:

-
    - -
  • - - - (Hash, nil) - - - - — -

    Inline/trailing comment on the same line

    -
    - -
  • - -
- -
- - - - -
-
-
-
-22
-23
-24
-
-
# File 'lib/bash/merge/node_wrapper.rb', line 22
-
-def inline_comment
-  @inline_comment
-end
-
-
- - - -
-

- - #leading_commentsArray<Hash> (readonly) - - - - - -

-
-

Returns Leading comments associated with this node.

- - -
-
-
- -

Returns:

-
    - -
  • - - - (Array<Hash>) - - - - — -

    Leading comments associated with this node

    -
    - -
  • - -
- -
- - - - -
-
-
-
-19
-20
-21
-
-
# File 'lib/bash/merge/node_wrapper.rb', line 19
-
-def leading_comments
-  @leading_comments
-end
-
-
- - - -
-

- - #linesArray<String> (readonly) - - - - - -

-
-

Returns Source lines.

- - -
-
-
- -

Returns:

-
    - -
  • - - - (Array<String>) - - - - — -

    Source lines

    -
    - -
  • - -
- -
- - - - -
-
-
-
-31
-32
-33
-
-
# File 'lib/bash/merge/node_wrapper.rb', line 31
-
-def lines
-  @lines
-end
-
-
- - - -
-

- - #nodeTreeHaver::Node (readonly) - - - - - -

-
-

Returns The wrapped tree-haver node.

- - -
-
-
- -

Returns:

-
    - -
  • - - - (TreeHaver::Node) - - - - — -

    The wrapped tree-haver node

    -
    - -
  • - -
- -
- - - - -
-
-
-
-16
-17
-18
-
-
# File 'lib/bash/merge/node_wrapper.rb', line 16
-
-def node
-  @node
-end
-
-
- - - -
-

- - #sourceString (readonly) - - - - - -

-
-

Returns The original source string.

- - -
-
-
- -

Returns:

-
    - -
  • - - - (String) - - - - — -

    The original source string

    -
    - -
  • - -
- -
- - - - -
-
-
-
-34
-35
-36
-
-
# File 'lib/bash/merge/node_wrapper.rb', line 34
-
-def source
-  @source
-end
-
-
- - - -
-

- - #start_lineInteger (readonly) - - - - - -

-
-

Returns Start line (1-based).

- - -
-
-
- -

Returns:

-
    - -
  • - - - (Integer) - - - - — -

    Start line (1-based)

    -
    - -
  • - -
- -
- - - - -
-
-
-
-25
-26
-27
-
-
# File 'lib/bash/merge/node_wrapper.rb', line 25
-
-def start_line
-  @start_line
-end
-
-
- -
- - -
-

Instance Method Details

- - -
-

- - #case_statement?Boolean - - - - - -

-
-

Check if this is a case statement

- - -
-
-
- -

Returns:

-
    - -
  • - - - (Boolean) - - - -
  • - -
- -
- - - - -
-
-
-
-121
-122
-123
-
-
# File 'lib/bash/merge/node_wrapper.rb', line 121
-
-def case_statement?
-  @node.type.to_s == "case_statement"
-end
-
-
- -
-

- - #childrenArray<NodeWrapper> - - - - - -

-
-

Get children wrapped as NodeWrappers

- - -
-
-
- -

Returns:

- - -
- - - - -
-
-
-
-179
-180
-181
-182
-183
-184
-185
-186
-187
-
-
# File 'lib/bash/merge/node_wrapper.rb', line 179
-
-def children
-  return [] unless @node.respond_to?(:each)
-
-  result = []
-  @node.each do |child|
-    result << NodeWrapper.new(child, lines: @lines, source: @source)
-  end
-  result
-end
-
-
- -
-

- - #command?Boolean - - - - - -

-
-

Check if this is a command

- - -
-
-
- -

Returns:

-
    - -
  • - - - (Boolean) - - - -
  • - -
- -
- - - - -
-
-
-
-127
-128
-129
-
-
# File 'lib/bash/merge/node_wrapper.rb', line 127
-
-def command?
-  @node.type.to_s == "command"
-end
-
-
- -
-

- - #command_nameString? - - - - - -

-
-

Get the command name if this is a command

- - -
-
-
- -

Returns:

-
    - -
  • - - - (String, nil) - - - -
  • - -
- -
- - - - -
-
-
-
-165
-166
-167
-168
-169
-170
-171
-172
-173
-174
-175
-
-
# File 'lib/bash/merge/node_wrapper.rb', line 165
-
-def command_name
-  return unless command?
-
-  # First child that is a word or simple_expansion
-  @node.each do |child|
-    next if %w[comment file_redirect heredoc_redirect].include?(child.type.to_s)
-
-    return node_text(child) if %w[word command_name].include?(child.type.to_s)
-  end
-  nil
-end
-
-
- -
-

- - #comment?Boolean - - - - - -

-
-

Check if this is a comment

- - -
-
-
- -

Returns:

-
    - -
  • - - - (Boolean) - - - -
  • - -
- -
- - - - -
-
-
-
-139
-140
-141
-
-
# File 'lib/bash/merge/node_wrapper.rb', line 139
-
-def comment?
-  @node.type.to_s == "comment"
-end
-
-
- -
-

- - #contentString - - - - - -

-
-

Get the content for this node from source lines

- - -
-
-
- -

Returns:

-
    - -
  • - - - (String) - - - -
  • - -
- -
- - - - -
-
-
-
-227
-228
-229
-230
-231
-
-
# File 'lib/bash/merge/node_wrapper.rb', line 227
-
-def content
-  return "" unless @start_line && @end_line
-
-  (@start_line..@end_line).map { |ln| @lines[ln - 1] }.compact.join("\n")
-end
-
-
- -
-

- - #find_child_by_field(field_name) ⇒ TreeSitter::Node? - - - - - -

-
-

Find a child by field name

- - -
-
-
-

Parameters:

-
    - -
  • - - field_name - - - (String) - - - - — -

    Field name to look for

    -
    - -
  • - -
- -

Returns:

-
    - -
  • - - - (TreeSitter::Node, nil) - - - -
  • - -
- -
- - - - -
-
-
-
-192
-193
-194
-195
-196
-
-
# File 'lib/bash/merge/node_wrapper.rb', line 192
-
-def find_child_by_field(field_name)
-  return unless @node.respond_to?(:child_by_field_name)
-
-  @node.child_by_field_name(field_name)
-end
-
-
- -
-

- - #find_child_by_type(type_name) ⇒ TreeSitter::Node? - - - - - -

-
-

Find a child by type

- - -
-
-
-

Parameters:

-
    - -
  • - - type_name - - - (String) - - - - — -

    Type name to look for

    -
    - -
  • - -
- -

Returns:

-
    - -
  • - - - (TreeSitter::Node, nil) - - - -
  • - -
- -
- - - - -
-
-
-
-201
-202
-203
-204
-205
-206
-207
-208
-
-
# File 'lib/bash/merge/node_wrapper.rb', line 201
-
-def find_child_by_type(type_name)
-  return unless @node.respond_to?(:each)
-
-  @node.each do |child|
-    return child if child.type.to_s == type_name
-  end
-  nil
-end
-
-
- -
-

- - #for_statement?Boolean - - - - - -

-
-

Check if this is a for loop

- - -
-
-
- -

Returns:

-
    - -
  • - - - (Boolean) - - - -
  • - -
- -
- - - - -
-
-
-
-109
-110
-111
-
-
# File 'lib/bash/merge/node_wrapper.rb', line 109
-
-def for_statement?
-  %w[for_statement c_style_for_statement].include?(@node.type.to_s)
-end
-
-
- -
-

- - #freeze_node?Boolean - - - - - -

-
-

Check if this is a freeze node

- - -
-
-
- -

Returns:

-
    - -
  • - - - (Boolean) - - - -
  • - -
- -
- - - - -
-
-
-
-72
-73
-74
-
-
# File 'lib/bash/merge/node_wrapper.rb', line 72
-
-def freeze_node?
-  false
-end
-
-
- -
-

- - #function_definition?Boolean - - - - - -

-
-

Check if this is a function definition

- - -
-
-
- -

Returns:

-
    - -
  • - - - (Boolean) - - - -
  • - -
- -
- - - - -
-
-
-
-91
-92
-93
-
-
# File 'lib/bash/merge/node_wrapper.rb', line 91
-
-def function_definition?
-  @node.type.to_s == "function_definition"
-end
-
-
- -
-

- - #function_nameString? - - - - - -

-
-

Get the function name if this is a function definition

- - -
-
-
- -

Returns:

-
    - -
  • - - - (String, nil) - - - -
  • - -
- -
- - - - -
-
-
-
-145
-146
-147
-148
-149
-150
-151
-
-
# File 'lib/bash/merge/node_wrapper.rb', line 145
-
-def function_name
-  return unless function_definition?
-
-  # In bash tree-sitter, function name is in a 'name' or 'word' child
-  name_node = find_child_by_type("word") || find_child_by_field("name")
-  node_text(name_node) if name_node
-end
-
-
- -
-

- - #if_statement?Boolean - - - - - -

-
-

Check if this is an if statement

- - -
-
-
- -

Returns:

-
    - -
  • - - - (Boolean) - - - -
  • - -
- -
- - - - -
-
-
-
-103
-104
-105
-
-
# File 'lib/bash/merge/node_wrapper.rb', line 103
-
-def if_statement?
-  @node.type.to_s == "if_statement"
-end
-
-
- -
-

- - #inspectString - - - - - -

-
-

String representation for debugging

- - -
-
-
- -

Returns:

-
    - -
  • - - - (String) - - - -
  • - -
- -
- - - - -
-
-
-
-235
-236
-237
-
-
# File 'lib/bash/merge/node_wrapper.rb', line 235
-
-def inspect
-  "#<#{self.class.name} type=#{@node.type} lines=#{@start_line}..#{@end_line}>"
-end
-
-
- -
-

- - #node_text(ts_node) ⇒ String - - - - - -

-
-

Extract text from a tree-sitter node using byte positions

- - -
-
-
-

Parameters:

-
    - -
  • - - ts_node - - - (TreeSitter::Node) - - - - — -

    The tree-sitter node

    -
    - -
  • - -
- -

Returns:

-
    - -
  • - - - (String) - - - -
  • - -
- -
- - - - -
-
-
-
-219
-220
-221
-222
-223
-
-
# File 'lib/bash/merge/node_wrapper.rb', line 219
-
-def node_text(ts_node)
-  return "" unless ts_node.respond_to?(:start_byte) && ts_node.respond_to?(:end_byte)
-
-  @source[ts_node.start_byte...ts_node.end_byte] || ""
-end
-
-
- -
-

- - #pipeline?Boolean - - - - - -

-
-

Check if this is a pipeline

- - -
-
-
- -

Returns:

-
    - -
  • - - - (Boolean) - - - -
  • - -
- -
- - - - -
-
-
-
-133
-134
-135
-
-
# File 'lib/bash/merge/node_wrapper.rb', line 133
-
-def pipeline?
-  @node.type.to_s == "pipeline"
-end
-
-
- -
-

- - #signatureArray? - - - - - -

-
-

Generate a signature for this node for matching purposes.
-Signatures are used to identify corresponding nodes between template and destination.

- - -
-
-
- -

Returns:

-
    - -
  • - - - (Array, nil) - - - - — -

    Signature array or nil if not signaturable

    -
    - -
  • - -
- -
- - - - -
-
-
-
-66
-67
-68
-
-
# File 'lib/bash/merge/node_wrapper.rb', line 66
-
-def signature
-  compute_signature(@node)
-end
-
-
- -
-

- - #textString - - - - - -

-
-

Get the text content for this node by extracting from source using byte positions

- - -
-
-
- -

Returns:

-
    - -
  • - - - (String) - - - -
  • - -
- -
- - - - -
-
-
-
-212
-213
-214
-
-
# File 'lib/bash/merge/node_wrapper.rb', line 212
-
-def text
-  node_text(@node)
-end
-
-
- -
-

- - #typeSymbol - - - - - -

-
-

Get the node type as a symbol

- - -
-
-
- -

Returns:

-
    - -
  • - - - (Symbol) - - - -
  • - -
- -
- - - - -
-
-
-
-78
-79
-80
-
-
# File 'lib/bash/merge/node_wrapper.rb', line 78
-
-def type
-  @node.type.to_sym
-end
-
-
- -
-

- - #type?(type_name) ⇒ Boolean - - - - - -

-
-

Check if this node has a specific type

- - -
-
-
-

Parameters:

-
    - -
  • - - type_name - - - (Symbol, String) - - - - — -

    Type to check

    -
    - -
  • - -
- -

Returns:

-
    - -
  • - - - (Boolean) - - - -
  • - -
- -
- - - - -
-
-
-
-85
-86
-87
-
-
# File 'lib/bash/merge/node_wrapper.rb', line 85
-
-def type?(type_name)
-  @node.type.to_s == type_name.to_s
-end
-
-
- -
-

- - #variable_assignment?Boolean - - - - - -

-
-

Check if this is a variable assignment

- - -
-
-
- -

Returns:

-
    - -
  • - - - (Boolean) - - - -
  • - -
- -
- - - - -
-
-
-
-97
-98
-99
-
-
# File 'lib/bash/merge/node_wrapper.rb', line 97
-
-def variable_assignment?
-  @node.type.to_s == "variable_assignment"
-end
-
-
- -
-

- - #variable_nameString? - - - - - -

-
-

Get the variable name if this is a variable assignment

- - -
-
-
- -

Returns:

-
    - -
  • - - - (String, nil) - - - -
  • - -
- -
- - - - -
-
-
-
-155
-156
-157
-158
-159
-160
-161
-
-
# File 'lib/bash/merge/node_wrapper.rb', line 155
-
-def variable_name
-  return unless variable_assignment?
-
-  # Get the variable name from the left side of assignment
-  name_node = find_child_by_field("name")
-  node_text(name_node) if name_node
-end
-
-
- -
-

- - #while_statement?Boolean - - - - - -

-
-

Check if this is a while loop

- - -
-
-
- -

Returns:

-
    - -
  • - - - (Boolean) - - - -
  • - -
- -
- - - - -
-
-
-
-115
-116
-117
-
-
# File 'lib/bash/merge/node_wrapper.rb', line 115
-
-def while_statement?
-  @node.type.to_s == "while_statement"
-end
-
-
- -
- -
- - - -
- - \ No newline at end of file diff --git a/docs/Bash/Merge/ParseError.html b/docs/Bash/Merge/ParseError.html index 7d03647..e69de29 100644 --- a/docs/Bash/Merge/ParseError.html +++ b/docs/Bash/Merge/ParseError.html @@ -1,290 +0,0 @@ - - - - - - - Class: Bash::Merge::ParseError - - — Documentation by YARD 0.9.38 - - - - - - - - - - - - - - - - - - - -
- - -

Class: Bash::Merge::ParseError - - - -

-
- -
-
Inherits:
-
- Ast::Merge::ParseError - -
    -
  • Object
  • - - - - - -
- show all - -
-
- - - - - - - - - - - -
-
Defined in:
-
lib/bash/merge.rb
-
- -
- -

Overview

-
-

Raised when a Bash script has parsing errors.
-Inherits from Ast::Merge::ParseError for consistency across merge gems.

- - -
-
-
- -
-

Examples:

- - -

Handling parse errors

-
- -
begin
-  analysis = FileAnalysis.new(bash_content)
-rescue ParseError => e
-  puts "Bash syntax error: #{e.message}"
-  e.errors.each { |error| puts "  #{error}" }
-end
- -
- - -
-

Direct Known Subclasses

-

DestinationParseError, TemplateParseError

-
- - - - - - - - -

- Instance Method Summary - collapse -

- - - - - -
-

Constructor Details

- -
-

- - #initialize(message = nil, content: nil, errors: []) ⇒ ParseError - - - - - -

-
-

Returns a new instance of ParseError.

- - -
-
-
-

Parameters:

-
    - -
  • - - message - - - (String, nil) - - - (defaults to: nil) - - - — -

    Error message (auto-generated if nil)

    -
    - -
  • - -
  • - - content - - - (String, nil) - - - (defaults to: nil) - - - — -

    The Bash source that failed to parse

    -
    - -
  • - -
  • - - errors - - - (Array) - - - (defaults to: []) - - - — -

    Parse errors from tree-sitter

    -
    - -
  • - -
- - -
- - - - -
-
-
-
-68
-69
-70
-
-
# File 'lib/bash/merge.rb', line 68
-
-def initialize(message = nil, content: nil, errors: [])
-  super(message, errors: errors, content: content)
-end
-
-
- -
- - -
- - - -
- - \ No newline at end of file diff --git a/docs/Bash/Merge/SmartMerger.html b/docs/Bash/Merge/SmartMerger.html index d8c535e..80a47b1 100644 --- a/docs/Bash/Merge/SmartMerger.html +++ b/docs/Bash/Merge/SmartMerger.html @@ -69,11 +69,13 @@
Inherits:
- Object + Ast::Merge::SmartMergerBase
  • Object
  • + +
@@ -146,6 +148,14 @@

With custom signature generator } merger = SmartMerger.new(template, dest, signature_generator: sig_gen)

+ +

With node_typing for per-node-type preferences

+
+ +
merger = SmartMerger.new(template, dest,
+  node_typing: { "function_definition" => ->(n) { NodeTyping.with_merge_type(n, :func) } },
+  preference: { default: :destination, func: :template })
+ @@ -153,207 +163,6 @@

With custom signature generator -

Instance Attribute Summary collapse

- - @@ -391,7 +200,7 @@

  • - #initialize(template_content, dest_content, signature_generator: nil, preference: :destination, add_template_only_nodes: false, freeze_token: FileAnalysis::DEFAULT_FREEZE_TOKEN, parser_path: nil) ⇒ SmartMerger + #initialize(template_content, dest_content, signature_generator: nil, preference: :destination, add_template_only_nodes: false, freeze_token: nil, match_refiner: nil, regions: nil, region_placeholder: nil, node_typing: nil, **options) ⇒ SmartMerger @@ -433,29 +242,6 @@

    Perform the merge and return the result as a Bash string.

    -

  • - - -
  • - - - #merge_result ⇒ MergeResult - - - - - - - - - - - - - -

    Perform the merge and return the full result object.

    -
    -
  • @@ -508,13 +294,14 @@

    -
    + +

    Constructor Details

    - #initialize(template_content, dest_content, signature_generator: nil, preference: :destination, add_template_only_nodes: false, freeze_token: FileAnalysis::DEFAULT_FREEZE_TOKEN, parser_path: nil) ⇒ SmartMerger + #initialize(template_content, dest_content, signature_generator: nil, preference: :destination, add_template_only_nodes: false, freeze_token: nil, match_refiner: nil, regions: nil, region_placeholder: nil, node_typing: nil, **options) ⇒ SmartMerger @@ -522,7 +309,15 @@

    -

    Creates a new SmartMerger for intelligent Bash script merging.

    + +
    + Note: +

    To specify a custom parser path, use the TREE_SITTER_BASH_PATH environment
    +variable. This is handled by tree_haver’s GrammarFinder.

    +
    +
    + +

    Creates a new SmartMerger for intelligent Bash script merging.

    @@ -583,19 +378,14 @@

    preference - (Symbol) + (Symbol, Hash) (defaults to: :destination) — -

    Which version to prefer when
    -nodes have matching signatures:

    -
      -
    • :destination (default) - Keep destination version (customizations)
    • -
    • :template - Use template version (updates)
    • -
    +

    :destination, :template, or per-type Hash

    @@ -625,7 +415,7 @@

    (String) - (defaults to: FileAnalysis::DEFAULT_FREEZE_TOKEN) + (defaults to: nil) — @@ -636,499 +426,114 @@

  • - parser_path + match_refiner - (String, nil) + (#call, nil) (defaults to: nil) — -

    Path to tree-sitter-bash parser library

    +

    Match refiner for fuzzy matching

  • - - -

    Raises:

    -
      +
    • + + regions + + + (Array<Hash>, nil) + + + (defaults to: nil) + + + — +

      Region configurations for nested merging

      +
      + +
    • + region_placeholder - (TemplateParseError) + + (String, nil) + (defaults to: nil) + — -

      If template has syntax errors

      +

      Custom placeholder for regions

    • + node_typing - (DestinationParseError) + (Hash{Symbol,String => #call}, nil) + + + (defaults to: nil) — -

      If destination has syntax errors

      +

      Node typing configuration

      +
      + +
    • + +
    • + + options + + + (Hash) + + + + — +

      Additional options for forward compatibility

    -

    - - - - -
    -
    -
    -
    -69
    -70
    -71
    -72
    -73
    -74
    -75
    -76
    -77
    -78
    -79
    -80
    -81
    -82
    -83
    -84
    -85
    -86
    -87
    -88
    -89
    -90
    -91
    -92
    -93
    -94
    -95
    -96
    -97
    -98
    -99
    -100
    -101
    -102
    -103
    -104
    -105
    -106
    -107
    -108
    -109
    -110
    -111
    -112
    -113
    -114
    -115
    -116
    -117
    -
    -
    # File 'lib/bash/merge/smart_merger.rb', line 69
    -
    -def initialize(
    -  template_content,
    -  dest_content,
    -  signature_generator: nil,
    -  preference: :destination,
    -  add_template_only_nodes: false,
    -  freeze_token: FileAnalysis::DEFAULT_FREEZE_TOKEN,
    -  parser_path: nil
    -)
    -  @preference = preference
    -  @add_template_only_nodes = add_template_only_nodes
    -  @freeze_token = freeze_token
    -
    -  # Analyze both files
    -  @template_analysis = FileAnalysis.new(
    -    template_content,
    -    freeze_token: freeze_token,
    -    signature_generator: signature_generator,
    -    parser_path: parser_path,
    -  )
    -
    -  @dest_analysis = FileAnalysis.new(
    -    dest_content,
    -    freeze_token: freeze_token,
    -    signature_generator: signature_generator,
    -    parser_path: parser_path,
    -  )
    -
    -  # Validate parsing
    -  validate_parsing!
    -
    -  # Create resolver
    -  @resolver = ConflictResolver.new(
    -    @template_analysis,
    -    @dest_analysis,
    -    preference: preference,
    -    add_template_only_nodes: add_template_only_nodes,
    -  )
    -
    -  # Initialize result
    -  @result = MergeResult.new
    -
    -  DebugLogger.debug("SmartMerger initialized", {
    -    template_valid: @template_analysis.valid?,
    -    dest_valid: @dest_analysis.valid?,
    -    preference: preference,
    -    add_template_only: add_template_only_nodes,
    -  })
    -end
    -
    -

    - -
    - -
    -

    Instance Attribute Details

    - - - -
    -

    - - #add_template_only_nodesBoolean (readonly) - - - - - -

    -
    -

    Returns Whether to add template-only nodes.

    - - -
    -
    -
    - -

    Returns:

    -
      - -
    • - - - (Boolean) - - - - — -

      Whether to add template-only nodes

      -
      - -
    • - -
    - -
    - - - - -
    -
    -
    -
    -49
    -50
    -51
    -
    -
    # File 'lib/bash/merge/smart_merger.rb', line 49
    -
    -def add_template_only_nodes
    -  @add_template_only_nodes
    -end
    -
    -
    - - - -
    -

    - - #dest_analysisFileAnalysis (readonly) - - - - - -

    -
    -

    Returns Analysis of the destination file.

    - - -
    -
    -
    - -

    Returns:

    -
      - -
    • - - - (FileAnalysis) - - - - — -

      Analysis of the destination file

      -
      - -
    • - -
    - -
    - - - - -
    -
    -
    -
    -37
    -38
    -39
    -
    -
    # File 'lib/bash/merge/smart_merger.rb', line 37
    -
    -def dest_analysis
    -  @dest_analysis
    -end
    -
    -
    - - - -
    -

    - - #freeze_tokenString (readonly) - - - - - -

    -
    -

    Returns Token used for freeze blocks.

    - - -
    -
    -
    - -

    Returns:

    -
      - -
    • - - - (String) - - - - — -

      Token used for freeze blocks

      -
      - -
    • - -
    - -
    - - - - -
    -
    -
    -
    -52
    -53
    -54
    -
    -
    # File 'lib/bash/merge/smart_merger.rb', line 52
    -
    -def freeze_token
    -  @freeze_token
    -end
    -
    -
    - - - -
    -

    - - #preferenceSymbol (readonly) - - - - - -

    -
    -

    Returns Preference for signature matches.

    - - -
    -
    -
    - -

    Returns:

    -
      - -
    • - - - (Symbol) - - - - — -

      Preference for signature matches

      -
      - -
    • - -
    - -
    - - - - -
    -
    -
    -
    -46
    -47
    -48
    -
    -
    # File 'lib/bash/merge/smart_merger.rb', line 46
    -
    -def preference
    -  @preference
    -end
    -
    -
    - - - -
    -

    - - #resolverConflictResolver (readonly) - - - - - -

    -
    -

    Returns Resolver for handling conflicts.

    - - -
    -
    -
    - -

    Returns:

    -
    - - - - -
    -
    -
    -
    -40
    -41
    -42
    -
    -
    # File 'lib/bash/merge/smart_merger.rb', line 40
    -
    -def resolver
    -  @resolver
    -end
    -
    -
    - - - -
    -

    - - #resultMergeResult (readonly) - - - - - -

    -
    -

    Returns Result of the merge operation.

    - - -
    -
    -
    - -

    Returns:

    -
      -
    • - (MergeResult) + (DestinationParseError) — -

      Result of the merge operation

      +

      If destination has syntax errors

    • @@ -1141,81 +546,70 @@

       
       
      -43
      -44
      -45
      +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83

    -
    # File 'lib/bash/merge/smart_merger.rb', line 43
    +      
    # File 'lib/bash/merge/smart_merger.rb', line 57
     
    -def result
    -  @result
    +def initialize(
    +  template_content,
    +  dest_content,
    +  signature_generator: nil,
    +  preference: :destination,
    +  add_template_only_nodes: false,
    +  freeze_token: nil,
    +  match_refiner: nil,
    +  regions: nil,
    +  region_placeholder: nil,
    +  node_typing: nil,
    +  **options
    +)
    +  super(
    +    template_content,
    +    dest_content,
    +    signature_generator: signature_generator,
    +    preference: preference,
    +    add_template_only_nodes: add_template_only_nodes,
    +    freeze_token: freeze_token,
    +    match_refiner: match_refiner,
    +    regions: regions,
    +    region_placeholder: region_placeholder,
    +    node_typing: node_typing,
    +    **options
    +  )
     end
    - - - -
    -

    - - #template_analysisFileAnalysis (readonly) - - - - - -

    -
    -

    Returns Analysis of the template file.

    - - -
    -
    -
    - -

    Returns:

    -
      - -
    • - - - (FileAnalysis) - - - - — -

      Analysis of the template file

      -
      - -
    • -
    - -
    - - - - -
    -
    -
    -
    -34
    -35
    -36
    -
    -
    # File 'lib/bash/merge/smart_merger.rb', line 34
    -
    -def template_analysis
    -  @template_analysis
    -end
    -
    - -
    @@ -1264,15 +658,15 @@

     
     
    -171
    -172
    -173
    -174
    -175
    -176
    +125 +126 +127 +128 +129 +130

    -
    # File 'lib/bash/merge/smart_merger.rb', line 171
    +      
    # File 'lib/bash/merge/smart_merger.rb', line 125
     
     def errors
       errors = []
    @@ -1327,12 +721,12 @@ 

     
     
    -122
    -123
    -124
    +88 +89 +90

    -
    # File 'lib/bash/merge/smart_merger.rb', line 122
    +      
    # File 'lib/bash/merge/smart_merger.rb', line 88
     
     def merge
       merge_result.to_bash
    @@ -1340,73 +734,6 @@ 

    - - -
    -

    - - #merge_resultMergeResult - - - - - -

    -
    -

    Perform the merge and return the full result object.

    - - -
    -
    -
    - -

    Returns:

    -
      - -
    • - - - (MergeResult) - - - - — -

      The merge result containing merged content and metadata

      -
      - -
    • - -
    - -
    - - - - -
    -
    -
    -
    -129
    -130
    -131
    -132
    -133
    -134
    -135
    -136
    -
    -
    # File 'lib/bash/merge/smart_merger.rb', line 129
    -
    -def merge_result
    -  return @merge_result if @merge_result
    -
    -  @merge_result = DebugLogger.time("SmartMerger#merge") do
    -    @resolver.resolve(@result)
    -    @result
    -  end
    -end
    -
    @@ -1451,28 +778,28 @@

     
     
    -141
    -142
    -143
    -144
    -145
    -146
    -147
    -148
    -149
    -150
    -151
    -152
    -153
    -154
    -155
    -156
    -157
    -158
    -159
    +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113

    -
    # File 'lib/bash/merge/smart_merger.rb', line 141
    +      
    # File 'lib/bash/merge/smart_merger.rb', line 95
     
     def merge_with_debug
       content = merge
    @@ -1536,12 +863,12 @@ 

     
     
    -164
    -165
    -166
    +118 +119 +120

    -
    # File 'lib/bash/merge/smart_merger.rb', line 164
    +      
    # File 'lib/bash/merge/smart_merger.rb', line 118
     
     def valid?
       @template_analysis.valid? && @dest_analysis.valid?
    @@ -1556,9 +883,9 @@ 

    diff --git a/docs/Bash/Merge/TemplateParseError.html b/docs/Bash/Merge/TemplateParseError.html index e69de29..72d639e 100644 --- a/docs/Bash/Merge/TemplateParseError.html +++ b/docs/Bash/Merge/TemplateParseError.html @@ -0,0 +1,172 @@ + + + + + + + Class: Bash::Merge::TemplateParseError + + — Documentation by YARD 0.9.38 + + + + + + + + + + + + + + + + + + + +
    + + +

    Class: Bash::Merge::TemplateParseError + + + +

    +
    + +
    +
    Inherits:
    +
    + ParseError + +
      +
    • Object
    • + + + + + + + +
    + show all + +
    +
    + + + + + + + + + + + +
    +
    Defined in:
    +
    lib/bash/merge.rb
    +
    + +
    + +

    Overview

    +
    +

    Raised when the template file has syntax errors.

    + + +
    +
    +
    + +
    +

    Examples:

    + + +

    Handling template parse errors

    +
    + +
    begin
    +  merger = SmartMerger.new(template, destination)
    +  result = merger.merge
    +rescue TemplateParseError => e
    +  puts "Template syntax error: #{e.message}"
    +  e.errors.each do |error|
    +    puts "  #{error.message}"
    +  end
    +end
    + +
    + + +
    + + + + + + + + + + + + + +

    Method Summary

    + +

    Methods inherited from ParseError

    +

    #initialize

    + +
    +

    Constructor Details

    + +

    This class inherits a constructor from Bash::Merge::ParseError

    + +
    + + +
    + + + +
    + + \ No newline at end of file diff --git a/docs/Bash/Merge/Version.html b/docs/Bash/Merge/Version.html index f42a13c..3f5871c 100644 --- a/docs/Bash/Merge/Version.html +++ b/docs/Bash/Merge/Version.html @@ -131,9 +131,9 @@

    diff --git a/docs/_index.html b/docs/_index.html index 8b25805..e69de29 100644 --- a/docs/_index.html +++ b/docs/_index.html @@ -1,349 +0,0 @@ - - - - - - - Documentation by YARD 0.9.38 - - - - - - - - - - - - - - - - - - - -
    - - -

    Documentation by YARD 0.9.38

    -
    -

    Alphabetic Index

    - -

    File Listing

    - - -
    -

    Namespace Listing A-Z

    - - - - - - - - -
    - - -
      -
    • B
    • -
        - -
      • - Bash - -
      • - -
      -
    - - - - - - - - -
      -
    • E
    • -
        - -
      • - Emitter - - (Bash::Merge) - -
      • - -
      • - Error - - (Bash::Merge) - -
      • - -
      -
    - - - - - -
      -
    • M
    • - -
    - - - - - -
    - - -
      -
    • P
    • - -
    - - - - - - - - -
      -
    • V
    • -
        - -
      • - Version - - (Bash::Merge) - -
      • - -
      -
    - -
    - -
    - -
    - - - -
    - - \ No newline at end of file diff --git a/docs/class_list.html b/docs/class_list.html index c710e53..e69de29 100644 --- a/docs/class_list.html +++ b/docs/class_list.html @@ -1,54 +0,0 @@ - - - - - - - - - - - - - - - - - - Class List - - - -
    -
    -

    Class List

    - - - -
    - - -
    - - diff --git a/docs/file.CHANGELOG.html b/docs/file.CHANGELOG.html index 001bc10..f4261a0 100644 --- a/docs/file.CHANGELOG.html +++ b/docs/file.CHANGELOG.html @@ -78,10 +78,6 @@

    Added

    Changed

    -
      -
    • Migrated from ruby_tree_sitter to tree_haver for unified cross-platform tree-sitter support
    • -
    -

    Deprecated

    Removed

    @@ -93,9 +89,9 @@

    Security

    diff --git a/docs/file.CITATION.html b/docs/file.CITATION.html index 7182078..c4cbf22 100644 --- a/docs/file.CITATION.html +++ b/docs/file.CITATION.html @@ -82,9 +82,9 @@ diff --git a/docs/file.CODE_OF_CONDUCT.html b/docs/file.CODE_OF_CONDUCT.html index d0f8c45..e69de29 100644 --- a/docs/file.CODE_OF_CONDUCT.html +++ b/docs/file.CODE_OF_CONDUCT.html @@ -1,201 +0,0 @@ - - - - - - - File: CODE_OF_CONDUCT - - — Documentation by YARD 0.9.38 - - - - - - - - - - - - - - - - - - - -
    - - -

    Contributor Covenant Code of Conduct

    - -

    Our Pledge

    - -

    We as members, contributors, and leaders pledge to make participation in our
    -community a harassment-free experience for everyone, regardless of age, body
    -size, visible or invisible disability, ethnicity, sex characteristics, gender
    -identity and expression, level of experience, education, socio-economic status,
    -nationality, personal appearance, race, caste, color, religion, or sexual
    -identity and orientation.

    - -

    We pledge to act and interact in ways that contribute to an open, welcoming,
    -diverse, inclusive, and healthy community.

    - -

    Our Standards

    - -

    Examples of behavior that contributes to a positive environment for our
    -community include:

    - -
      -
    • Demonstrating empathy and kindness toward other people
    • -
    • Being respectful of differing opinions, viewpoints, and experiences
    • -
    • Giving and gracefully accepting constructive feedback
    • -
    • Accepting responsibility and apologizing to those affected by our mistakes,
      -and learning from the experience
    • -
    • Focusing on what is best not just for us as individuals, but for the overall
      -community
    • -
    - -

    Examples of unacceptable behavior include:

    - -
      -
    • The use of sexualized language or imagery, and sexual attention or advances of
      -any kind
    • -
    • Trolling, insulting or derogatory comments, and personal or political attacks
    • -
    • Public or private harassment
    • -
    • Publishing others’ private information, such as a physical or email address,
      -without their explicit permission
    • -
    • Other conduct which could reasonably be considered inappropriate in a
      -professional setting
    • -
    - -

    Enforcement Responsibilities

    - -

    Community leaders are responsible for clarifying and enforcing our standards of
    -acceptable behavior and will take appropriate and fair corrective action in
    -response to any behavior that they deem inappropriate, threatening, offensive,
    -or harmful.

    - -

    Community leaders have the right and responsibility to remove, edit, or reject
    -comments, commits, code, wiki edits, issues, and other contributions that are
    -not aligned to this Code of Conduct, and will communicate reasons for moderation
    -decisions when appropriate.

    - -

    Scope

    - -

    This Code of Conduct applies within all community spaces, and also applies when
    -an individual is officially representing the community in public spaces.
    -Examples of representing our community include using an official email address,
    -posting via an official social media account, or acting as an appointed
    -representative at an online or offline event.

    - -

    Enforcement

    - -

    Instances of abusive, harassing, or otherwise unacceptable behavior may be
    -reported to the community leaders responsible for enforcement at
    -Contact Maintainer.
    -All complaints will be reviewed and investigated promptly and fairly.

    - -

    All community leaders are obligated to respect the privacy and security of the
    -reporter of any incident.

    - -

    Enforcement Guidelines

    - -

    Community leaders will follow these Community Impact Guidelines in determining
    -the consequences for any action they deem in violation of this Code of Conduct:

    - -

    1. Correction

    - -

    Community Impact: Use of inappropriate language or other behavior deemed
    -unprofessional or unwelcome in the community.

    - -

    Consequence: A private, written warning from community leaders, providing
    -clarity around the nature of the violation and an explanation of why the
    -behavior was inappropriate. A public apology may be requested.

    - -

    2. Warning

    - -

    Community Impact: A violation through a single incident or series of
    -actions.

    - -

    Consequence: A warning with consequences for continued behavior. No
    -interaction with the people involved, including unsolicited interaction with
    -those enforcing the Code of Conduct, for a specified period of time. This
    -includes avoiding interactions in community spaces as well as external channels
    -like social media. Violating these terms may lead to a temporary or permanent
    -ban.

    - -

    3. Temporary Ban

    - -

    Community Impact: A serious violation of community standards, including
    -sustained inappropriate behavior.

    - -

    Consequence: A temporary ban from any sort of interaction or public
    -communication with the community for a specified period of time. No public or
    -private interaction with the people involved, including unsolicited interaction
    -with those enforcing the Code of Conduct, is allowed during this period.
    -Violating these terms may lead to a permanent ban.

    - -

    4. Permanent Ban

    - -

    Community Impact: Demonstrating a pattern of violation of community
    -standards, including sustained inappropriate behavior, harassment of an
    -individual, or aggression toward or disparagement of classes of individuals.

    - -

    Consequence: A permanent ban from any sort of public interaction within the
    -community.

    - -

    Attribution

    - -

    This Code of Conduct is adapted from the Contributor Covenant,
    -version 2.1, available at
    -https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.

    - -

    Community Impact Guidelines were inspired by
    -Mozilla’s code of conduct enforcement ladder.

    - -

    For answers to common questions about this code of conduct, see the FAQ at
    -https://www.contributor-covenant.org/faq. Translations are available at
    -https://www.contributor-covenant.org/translations.

    - -
    - - - -
    - - \ No newline at end of file diff --git a/docs/file.CONTRIBUTING.html b/docs/file.CONTRIBUTING.html index fdf8bc8..ecefe30 100644 --- a/docs/file.CONTRIBUTING.html +++ b/docs/file.CONTRIBUTING.html @@ -309,9 +309,9 @@

    Manual process

    diff --git a/docs/file.FUNDING.html b/docs/file.FUNDING.html index cac2fe6..e69de29 100644 --- a/docs/file.FUNDING.html +++ b/docs/file.FUNDING.html @@ -1,109 +0,0 @@ - - - - - - - File: FUNDING - - — Documentation by YARD 0.9.38 - - - - - - - - - - - - - - - - - - - -
    - - -
    - -

    Official Discord 👉️ Live Chat on Discord

    - -

    Many paths lead to being a sponsor or a backer of this project. Are you on such a path?

    - -

    OpenCollective Backers OpenCollective Sponsors Sponsor Me on Github Liberapay Goal Progress Donate on PayPal

    - -

    Buy me a coffee Donate on Polar Donate to my FLOSS efforts at ko-fi.com Donate to my FLOSS efforts using Patreon

    - - - -

    🤑 A request for help

    - -

    Maintainers have teeth and need to pay their dentists.
    -After getting laid off in an RIF in March, and encountering difficulty finding a new one,
    -I began spending most of my time building open source tools.
    -I’m hoping to be able to pay for my kids’ health insurance this month,
    -so if you value the work I am doing, I need your support.
    -Please consider sponsoring me or the project.

    - -

    To join the community or get help 👇️ Join the Discord.

    - -

    Live Chat on Discord

    - -

    To say “thanks!” ☝️ Join the Discord or 👇️ send money.

    - -

    Sponsor kettle-rb/bash-merge on Open Source Collective 💌 Sponsor me on GitHub Sponsors 💌 Sponsor me on Liberapay 💌 Donate on PayPal

    - -

    Another Way to Support Open Source Software

    - -

    I’m driven by a passion to foster a thriving open-source community – a space where people can tackle complex problems, no matter how small. Revitalizing libraries that have fallen into disrepair, and building new libraries focused on solving real-world challenges, are my passions. I was recently affected by layoffs, and the tech jobs market is unwelcoming. I’m reaching out here because your support would significantly aid my efforts to provide for my family, and my farm (11 🐔 chickens, 2 🐶 dogs, 3 🐰 rabbits, 8 🐈‍ cats).

    - -

    If you work at a company that uses my work, please encourage them to support me as a corporate sponsor. My work on gems you use might show up in bundle fund.

    - -

    I’m developing a new library, floss_funding, designed to empower open-source developers like myself to get paid for the work we do, in a sustainable way. Please give it a look.

    - -

    Floss-Funding.dev: 👉️ No network calls. 👉️ No tracking. 👉️ No oversight. 👉️ Minimal crypto hashing. 💡 Easily disabled nags

    - -
    - - - -
    - - \ No newline at end of file diff --git a/docs/file.LICENSE.html b/docs/file.LICENSE.html index be9ee35..f2b9e39 100644 --- a/docs/file.LICENSE.html +++ b/docs/file.LICENSE.html @@ -60,9 +60,9 @@
    The MIT License (MIT)

    Copyright (c) 2025 Peter H. Boling

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    THE SOFTWARE.
    diff --git a/docs/file.README.html b/docs/file.README.html index ef7335f..e69de29 100644 --- a/docs/file.README.html +++ b/docs/file.README.html @@ -1,377 +0,0 @@ - - - - - - - File: README - - — Documentation by YARD 0.9.38 - - - - - - - - - - - - - - - - - - - -
    - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    📍 NOTE
    RubyGems (the GitHub org, not the website) suffered a hostile takeover in September 2025.
    Ultimately 4 maintainers were hard removed and a reason has been given for only 1 of those, while 2 others resigned in protest.
    It is a complicated story which is difficult to parse quickly.
    Simply put - there was active policy for adding or removing maintainers/owners of rubygems and bundler, and those policies were not followed.
    I’m adding notes like this to gems because I don’t condone theft of repositories or gems from their rightful owners.
    If a similar theft happened with my repos/gems, I’d hope some would stand up for me.
    Disenfranchised former-maintainers have started gem.coop.
    Once available I will publish there exclusively; unless RubyCentral makes amends with the community.
    The “Technology for Humans: Joel Draper” podcast episode by reinteractive is the most cogent summary I’m aware of.
    See here, here and here for more info on what comes next.
    What I’m doing: A (WIP) proposal for bundler/gem scopes, and a (WIP) proposal for a federated gem server.
    - -

    Galtzo FLOSS Logo by Aboling0, CC BY-SA 4.0 ruby-lang Logo, Yukihiro Matsumoto, Ruby Visual Identity Team, CC BY-SA 2.5 kettle-rb Logo by Aboling0, CC BY-SA 4.0

    - -

    ☯️ Bash::Merge

    - -

    Version GitHub tag (latest SemVer) License: MIT Downloads Rank Open Source Helpers CodeCov Test Coverage Coveralls Test Coverage QLTY Test Coverage QLTY Maintainability CI Heads CI Runtime Dependencies @ HEAD CI Current CI Truffle Ruby Deps Locked Deps Unlocked CI Supported CI Test Coverage CI Style CodeQL Apache SkyWalking Eyes License Compatibility Check

    - -

    if ci_badges.map(&:color).detect { it != "green"} ☝️ let me know, as I may have missed the discord notification.

    - -
    - -

    if ci_badges.map(&:color).all? { it == "green"} 👇️ send money so I can do more of this. FLOSS maintenance is now my full-time job.

    - -

    OpenCollective Backers OpenCollective Sponsors Sponsor Me on Github Liberapay Goal Progress Donate on PayPal Buy me a coffee Donate on Polar Donate at ko-fi.com

    - -

    🌻 Synopsis

    - -

    Bash::Merge is a standalone Ruby module that intelligently merges two versions of a Bash script using tree-sitter AST analysis. It’s like a smart “git merge” specifically designed for shell scripts. Built on top of ast-merge, it shares the same architecture as prism-merge for Ruby source files.

    - -

    Key Features

    - -
      -
    • -Tree-Sitter Powered: Uses tree-sitter-bash for accurate AST parsing
    • -
    • -Script-Aware: Understands Bash syntax including functions, variables, and commands
    • -
    • -Intelligent: Matches functions and variable assignments by name
    • -
    • -Comment-Preserving: Comments are preserved in their context
    • -
    • -Shebang Handling: Properly handles #!/bin/bash and similar shebangs
    • -
    • -Freeze Block Support: Respects freeze markers (default: bash-merge:freeze / bash-merge:unfreeze) for merge control - customizable to match your project’s conventions
    • -
    • -Full Provenance: Tracks origin of every node
    • -
    • -Standalone: Minimal dependencies - just ast-merge and ruby_tree_sitter -
    • -
    • -Customizable: -
        -
      • -signature_generator - callable custom signature generators
      • -
      • -preference - setting of :template, :destination, or a Hash for per-node-type preferences
      • -
      • -node_splitter - Hash mapping node types to callables for per-node-type merge customization (see ast-merge docs)
      • -
      • -add_template_only_nodes - setting to retain nodes that do not exist in destination
      • -
      • -freeze_token - customize freeze block markers (default: "bash-merge")
      • -
      -
    • -
    - -

    Supported Node Types

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Node TypeSignature FormatMatching Behavior
    Function Definition[:function, name]Functions match by name
    Variable Assignment[:assignment, name]Variables match by name
    Command[:command, name, args...]Commands match by name and arguments
    Comment[:comment, text]Comments preserved in context
    Pipeline[:pipeline, commands...]Pipelines match by command sequence
    If Statement[:if, condition_sig]Conditionals match by condition signature
    For Loop[:for, variable]For loops match by loop variable
    While Loop[:while, condition_sig]While loops match by condition signature
    - -

    Example

    - -
    require "bash/merge"
    -
    -template = File.read("template.sh")
    -destination = File.read("destination.sh")
    -
    -merger = Bash::Merge::SmartMerger.new(template, destination)
    -result = merger.merge
    -
    -File.write("merged.sh", result.to_bash)
    -
    - -

    The *-merge Gem Family

    - -

    This gem is part of a family of gems that provide intelligent merging for various file formats:

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    GemFormatParser Backend(s)Description
    tree_haverMultiMRI C, Rust, FFI, Java, Prism, Psych, Commonmarker, Markly, Citrus -Foundation: Cross-Ruby adapter for parsing libraries (like Faraday for HTTP)
    ast-mergeTextinternal -Infrastructure: Shared base classes and merge logic for all *-merge gems
    prism-mergeRuby -Prism (via tree_haver)Smart merge for Ruby source files
    psych-mergeYAML -Psych (via tree_haver)Smart merge for YAML files
    json-mergeJSON -tree-sitter-json (via tree_haver)Smart merge for JSON files
    jsonc-mergeJSONC -tree-sitter-jsonc (via tree_haver)⚠️ Proof of concept; Smart merge for JSON with Comments
    bash-mergeBash -tree-sitter-bash (via tree_haver)Smart merge for Bash scripts
    rbs-mergeRBSRBSSmart merge for Ruby type signatures
    dotenv-mergeDotenvinternal (dotenv)Smart merge for .env files
    toml-mergeTOML -tree-sitter-toml (via tree_haver)Smart merge for TOML files
    markdown-mergeMarkdown -Commonmarker / Markly (via tree_haver) -Foundation: Shared base for Markdown mergers with inner code block merging
    markly-mergeMarkdown -Markly (via tree_haver)Smart merge for Markdown (CommonMark via cmark-gfm C)
    commonmarker-mergeMarkdown -Commonmarker (via tree_haver)Smart merge for Markdown (CommonMark via comrak Rust)
    - -

    Example implementations for the gem templating use case:

    - - - - - - - - - - - - - - - - - - - - - -
    GemPurposeDescription
    kettle-devGem DevelopmentGem templating tool using *-merge gems
    kettle-jemGem TemplatingGem template library with smart merge support
    - -

    💡 Info you can shake a stick at

    - - - - - - - - - - - - - - - -
    Tokens to Remember -Gem name Gem namespace -
    Works with JRuby -JRuby 10.0 Compat JRuby HEAD Compat -
    Works with Truffle Ruby - - - - File: REEK - - — Documentation by YARD 0.9.38 - - - - - - - - - - - - - - - - - - - -
    - - -
    -
    - - - -
    - - \ No newline at end of file diff --git a/docs/file.RUBOCOP.html b/docs/file.RUBOCOP.html index 9bdec88..10344de 100644 --- a/docs/file.RUBOCOP.html +++ b/docs/file.RUBOCOP.html @@ -161,9 +161,9 @@

    Benefits of rubocop_gradual

    diff --git a/docs/file.SECURITY.html b/docs/file.SECURITY.html index e69de29..0642c84 100644 --- a/docs/file.SECURITY.html +++ b/docs/file.SECURITY.html @@ -0,0 +1,101 @@ + + + + + + + File: SECURITY + + — Documentation by YARD 0.9.38 + + + + + + + + + + + + + + + + + + + +
    + + +

    Security Policy

    + +

    Supported Versions

    + + + + + + + + + + + + + + +
    VersionSupported
    1.latest
    + +

    Security contact information

    + +

    To report a security vulnerability, please use the
    +Tidelift security contact.
    +Tidelift will coordinate the fix and disclosure.

    + +

    Additional Support

    + +

    If you are interested in support for versions older than the latest release,
    +please consider sponsoring the project / maintainer @ https://liberapay.com/pboling/donate,
    +or find other sponsorship links in the README.

    + +
    + + + +
    + + \ No newline at end of file diff --git a/docs/file.merge.html b/docs/file.merge.html index e69de29..5575c8c 100644 --- a/docs/file.merge.html +++ b/docs/file.merge.html @@ -0,0 +1,329 @@ + + + + + + + File: merge + + — Documentation by YARD 0.9.38 + + + + + + + + + + + + + + + + + + + +
    + + +

    Type signatures for bash-merge gem

    +

    Smart merge for Bash scripts using tree-sitter AST analysis

    + +

    module Bash
    + module Merge
    + VERSION: String

    + +
    # Base error class for bash-merge errors
    +class Error < StandardError
    +end
    +
    +# Error raised when template Bash file has syntax errors
    +class TemplateParseError < Error
    +  attr_reader errors: Array[untyped]
    +  attr_reader content: String?
    +
    +  def initialize: (?String? message, ?errors: Array[untyped], ?content: String?) -> void
    +end
    +
    +# Error raised when destination Bash file has syntax errors
    +class DestinationParseError < Error
    +  attr_reader errors: Array[untyped]
    +  attr_reader content: String?
    +
    +  def initialize: (?String? message, ?errors: Array[untyped], ?content: String?) -> void
    +end
    +
    +# Debug logging utility for Bash::Merge
    +module DebugLogger
    +  extend Ast::Merge::DebugLogger
    +
    +  def self.env_var_name: () -> String
    +  def self.env_var_name=: (String name) -> String
    +  def self.log_prefix: () -> String
    +  def self.log_prefix=: (String prefix) -> String
    +  def self.enabled?: () -> bool
    +  def self.debug: (String message, ?Hash[Symbol, untyped] context) -> void
    +  def self.info: (String message) -> void
    +  def self.warning: (String message) -> void
    +  def self.time: [T] (String operation) { () -> T } -> T
    +  def self.extract_node_info: (untyped node) -> Hash[Symbol, untyped]
    +end
    +
    +# Freeze block node for Bash scripts
    +class FreezeNode < Ast::Merge::FreezeNode
    +  InvalidStructureError: singleton(Ast::Merge::FreezeNode::InvalidStructureError)
    +  Location: singleton(Ast::Merge::FreezeNode::Location)
    +
    +  attr_reader lines: Array[String]
    +
    +  def initialize: (
    +    start_line: Integer,
    +    end_line: Integer,
    +    lines: Array[String],
    +    ?start_marker: String?,
    +    ?end_marker: String?,
    +    ?pattern_type: Symbol
    +  ) -> void
    +
    +  def signature: () -> Array[Symbol | String]
    +  def function_definition?: () -> bool
    +  def variable_assignment?: () -> bool
    +  def command?: () -> bool
    +  def slice: () -> String?
    +  def inspect: () -> String
    +
    +  private
    +
    +  def validate_structure!: () -> void
    +end
    +
    +# Wrapper for tree-sitter Bash nodes
    +class NodeWrapper
    +  attr_reader node: untyped
    +  attr_reader start_line: Integer
    +  attr_reader end_line: Integer
    +  attr_reader source: String
    +  attr_reader analysis: FileAnalysis?
    +
    +  def initialize: (
    +    untyped node,
    +    String source,
    +    ?analysis: FileAnalysis?
    +  ) -> void
    +
    +  def location: () -> Ast::Merge::FreezeNode::Location
    +  def signature: () -> Array[untyped]
    +  def freeze_node?: () -> bool
    +  def function_definition?: () -> bool
    +  def variable_assignment?: () -> bool
    +  def command?: () -> bool
    +  def comment?: () -> bool
    +  def name: () -> String?
    +  def value: () -> String?
    +  def slice: () -> String?
    +  def text: () -> String
    +  def type: () -> Symbol
    +  def inspect: () -> String
    +end
    +
    +# Comment tracker for Bash files
    +class CommentTracker
    +  attr_reader comments: Array[Hash[Symbol, untyped]]
    +  attr_reader source: String
    +  attr_reader lines: Array[String]
    +
    +  def initialize: (String source) -> void
    +
    +  def extract_comments: () -> Array[Hash[Symbol, untyped]]
    +  def comments_for_line: (Integer line) -> Array[Hash[Symbol, untyped]]
    +  def leading_comments_for: (Integer line) -> Array[Hash[Symbol, untyped]]
    +  def trailing_comment_for: (Integer line) -> Hash[Symbol, untyped]?
    +  def shebang?: (String line) -> bool
    +end
    +
    +# File analysis for Bash scripts
    +class FileAnalysis
    +  include Ast::Merge::FileAnalysisBase
    +
    +  DEFAULT_FREEZE_TOKEN: String
    +  PARSER_SEARCH_PATHS: Array[String]
    +
    +  attr_reader source: String
    +  attr_reader lines: Array[String]
    +  attr_reader ast: untyped
    +  attr_reader statements: Array[NodeWrapper | FreezeNode]
    +  attr_reader freeze_blocks: Array[FreezeNode]
    +  attr_reader freeze_token: String
    +  attr_reader signature_generator: (^(untyped) -> Array[untyped]?)?
    +  attr_reader comment_tracker: CommentTracker
    +  attr_reader errors: Array[untyped]
    +
    +  def self.find_parser_path: () -> String?
    +
    +  def initialize: (
    +    String source,
    +    ?freeze_token: String,
    +    ?signature_generator: (^(untyped) -> Array[untyped]?)?,
    +    ?parser_path: String?
    +  ) -> void
    +
    +  def valid?: () -> bool
    +  def nodes: () -> Array[NodeWrapper | FreezeNode]
    +  def line_at: (Integer line_num) -> String?
    +  def normalized_line: (Integer line_num) -> String?
    +  def in_freeze_block?: (Integer line_num) -> bool
    +  def freeze_block_at: (Integer line_num) -> FreezeNode?
    +  def signature_at: (Integer index) -> Array[untyped]?
    +  def generate_signature: (untyped node) -> Array[untyped]?
    +  def compute_node_signature: (untyped node) -> Array[untyped]?
    +
    +  private
    +
    +  def parse_bash: () -> void
    +  def extract_nodes: (untyped tree_node) -> Array[NodeWrapper]
    +  def extract_freeze_blocks: () -> Array[FreezeNode]
    +  def integrate_nodes_and_freeze_blocks: () -> Array[NodeWrapper | FreezeNode]
    +end
    +
    +# Result of a Bash merge operation
    +class MergeResult < Ast::Merge::MergeResult
    +  DECISION_KEPT_TEMPLATE: Symbol
    +  DECISION_KEPT_DEST: Symbol
    +  DECISION_MERGED: Symbol
    +  DECISION_ADDED: Symbol
    +  DECISION_FREEZE_BLOCK: Symbol
    +
    +  attr_reader lines: Array[Hash[Symbol, untyped]]
    +  attr_reader decisions: Array[Hash[Symbol, untyped]]
    +  attr_reader statistics: Hash[Symbol, Integer]
    +
    +  def initialize: (
    +    ?template_analysis: FileAnalysis?,
    +    ?dest_analysis: FileAnalysis?,
    +    ?conflicts: Array[Hash[Symbol, untyped]],
    +    ?frozen_blocks: Array[FreezeNode],
    +    ?stats: Hash[Symbol, untyped]
    +  ) -> void
    +
    +  def add_line: (
    +    String line,
    +    decision: Symbol,
    +    source: Symbol,
    +    ?original_line: Integer?
    +  ) -> void
    +
    +  def add_lines: (
    +    Array[String] lines,
    +    decision: Symbol,
    +    source: Symbol,
    +    ?start_line: Integer?
    +  ) -> void
    +
    +  def add_blank_line: (?decision: Symbol, ?source: Symbol) -> void
    +  def add_freeze_block: (FreezeNode freeze_node) -> void
    +  def to_bash: () -> String
    +  def content: () -> Array[Hash[Symbol, untyped]]
    +  def content_string: () -> String
    +  def empty?: () -> bool
    +
    +  private
    +
    +  def track_statistics: (Symbol decision, Symbol source) -> void
    +end
    +
    +# Smart merger for Bash scripts
    +class SmartMerger
    +  include Ast::Merge::MergerConfig
    +
    +  attr_reader template_analysis: FileAnalysis
    +  attr_reader dest_analysis: FileAnalysis
    +  attr_reader signature_match_preference: (Symbol | Hash[Symbol, Symbol])
    +  attr_reader add_template_only_nodes: bool
    +
    +  def initialize: (
    +    String template_content,
    +    String dest_content,
    +    ?signature_match_preference: (Symbol | Hash[Symbol, Symbol]),
    +    ?add_template_only_nodes: bool,
    +    ?freeze_token: String,
    +    ?signature_generator: (^(untyped) -> Array[untyped]?)?,
    +    ?node_splitter: Hash[Symbol, untyped]?,
    +    ?parser_path: String?
    +  ) -> void
    +
    +  def merge: () -> MergeResult
    +
    +  private
    +
    +  def perform_merge: () -> MergeResult
    +  def merge_nodes: (MergeResult result) -> void
    +end
    +
    +# Conflict resolver for Bash merges
    +class ConflictResolver
    +  attr_reader template_analysis: FileAnalysis
    +  attr_reader dest_analysis: FileAnalysis
    +  attr_reader signature_match_preference: (Symbol | Hash[Symbol, Symbol])
    +  attr_reader add_template_only_nodes: bool
    +
    +  def initialize: (
    +    FileAnalysis template_analysis,
    +    FileAnalysis dest_analysis,
    +    ?signature_match_preference: (Symbol | Hash[Symbol, Symbol]),
    +    ?add_template_only_nodes: bool
    +  ) -> void
    +
    +  def resolve: (untyped boundary, MergeResult result) -> void
    +end
    +
    +# Emitter for reconstructing Bash output
    +class Emitter
    +  attr_reader result: MergeResult
    +
    +  def initialize: (MergeResult result) -> void
    +
    +  def emit: () -> String
    +end   end end
    +
    +
    + + + +
    + + \ No newline at end of file diff --git a/docs/frames.html b/docs/frames.html index e69de29..2febcdb 100644 --- a/docs/frames.html +++ b/docs/frames.html @@ -0,0 +1,22 @@ + + + + + Documentation by YARD 0.9.38 + + + + diff --git a/docs/index.html b/docs/index.html index 4ea97f9..e69de29 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1,1007 +0,0 @@ - - - - - - - File: README - - — Documentation by YARD 0.9.38 - - - - - - - - - - - - - - - - - - - -
    - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    📍 NOTE
    RubyGems (the GitHub org, not the website) suffered a hostile takeover in September 2025.
    Ultimately 4 maintainers were hard removed and a reason has been given for only 1 of those, while 2 others resigned in protest.
    It is a complicated story which is difficult to parse quickly.
    Simply put - there was active policy for adding or removing maintainers/owners of rubygems and bundler, and those policies were not followed.
    I’m adding notes like this to gems because I don’t condone theft of repositories or gems from their rightful owners.
    If a similar theft happened with my repos/gems, I’d hope some would stand up for me.
    Disenfranchised former-maintainers have started gem.coop.
    Once available I will publish there exclusively; unless RubyCentral makes amends with the community.
    The “Technology for Humans: Joel Draper” podcast episode by reinteractive is the most cogent summary I’m aware of.
    See here, here and here for more info on what comes next.
    What I’m doing: A (WIP) proposal for bundler/gem scopes, and a (WIP) proposal for a federated gem server.
    - -

    Galtzo FLOSS Logo by Aboling0, CC BY-SA 4.0 ruby-lang Logo, Yukihiro Matsumoto, Ruby Visual Identity Team, CC BY-SA 2.5 kettle-rb Logo by Aboling0, CC BY-SA 4.0

    - -

    ☯️ Bash::Merge

    - -

    Version GitHub tag (latest SemVer) License: MIT Downloads Rank Open Source Helpers CodeCov Test Coverage Coveralls Test Coverage QLTY Test Coverage QLTY Maintainability CI Heads CI Runtime Dependencies @ HEAD CI Current CI Truffle Ruby Deps Locked Deps Unlocked CI Supported CI Test Coverage CI Style CodeQL Apache SkyWalking Eyes License Compatibility Check

    - -

    if ci_badges.map(&:color).detect { it != "green"} ☝️ let me know, as I may have missed the discord notification.

    - -
    - -

    if ci_badges.map(&:color).all? { it == "green"} 👇️ send money so I can do more of this. FLOSS maintenance is now my full-time job.

    - -

    OpenCollective Backers OpenCollective Sponsors Sponsor Me on Github Liberapay Goal Progress Donate on PayPal Buy me a coffee Donate on Polar Donate at ko-fi.com

    - -

    🌻 Synopsis

    - -

    Bash::Merge is a standalone Ruby module that intelligently merges two versions of a Bash script using tree-sitter AST analysis. It’s like a smart “git merge” specifically designed for shell scripts. Built on top of ast-merge, it shares the same architecture as prism-merge for Ruby source files.

    - -

    Key Features

    - -
      -
    • -Tree-Sitter Powered: Uses tree-sitter-bash for accurate AST parsing
    • -
    • -Script-Aware: Understands Bash syntax including functions, variables, and commands
    • -
    • -Intelligent: Matches functions and variable assignments by name
    • -
    • -Comment-Preserving: Comments are preserved in their context
    • -
    • -Shebang Handling: Properly handles #!/bin/bash and similar shebangs
    • -
    • -Freeze Block Support: Respects freeze markers (default: bash-merge:freeze / bash-merge:unfreeze) for merge control - customizable to match your project’s conventions
    • -
    • -Full Provenance: Tracks origin of every node
    • -
    • -Standalone: Minimal dependencies - just ast-merge and ruby_tree_sitter -
    • -
    • -Customizable: -
        -
      • -signature_generator - callable custom signature generators
      • -
      • -preference - setting of :template, :destination, or a Hash for per-node-type preferences
      • -
      • -node_splitter - Hash mapping node types to callables for per-node-type merge customization (see ast-merge docs)
      • -
      • -add_template_only_nodes - setting to retain nodes that do not exist in destination
      • -
      • -freeze_token - customize freeze block markers (default: "bash-merge")
      • -
      -
    • -
    - -

    Supported Node Types

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Node TypeSignature FormatMatching Behavior
    Function Definition[:function, name]Functions match by name
    Variable Assignment[:assignment, name]Variables match by name
    Command[:command, name, args...]Commands match by name and arguments
    Comment[:comment, text]Comments preserved in context
    Pipeline[:pipeline, commands...]Pipelines match by command sequence
    If Statement[:if, condition_sig]Conditionals match by condition signature
    For Loop[:for, variable]For loops match by loop variable
    While Loop[:while, condition_sig]While loops match by condition signature
    - -

    Example

    - -
    require "bash/merge"
    -
    -template = File.read("template.sh")
    -destination = File.read("destination.sh")
    -
    -merger = Bash::Merge::SmartMerger.new(template, destination)
    -result = merger.merge
    -
    -File.write("merged.sh", result.to_bash)
    -
    - -

    The *-merge Gem Family

    - -

    This gem is part of a family of gems that provide intelligent merging for various file formats:

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    GemFormatParser Backend(s)Description
    tree_haverMultiMRI C, Rust, FFI, Java, Prism, Psych, Commonmarker, Markly, Citrus -Foundation: Cross-Ruby adapter for parsing libraries (like Faraday for HTTP)
    ast-mergeTextinternal -Infrastructure: Shared base classes and merge logic for all *-merge gems
    prism-mergeRuby -Prism (via tree_haver)Smart merge for Ruby source files
    psych-mergeYAML -Psych (via tree_haver)Smart merge for YAML files
    json-mergeJSON -tree-sitter-json (via tree_haver)Smart merge for JSON files
    jsonc-mergeJSONC -tree-sitter-jsonc (via tree_haver)⚠️ Proof of concept; Smart merge for JSON with Comments
    bash-mergeBash -tree-sitter-bash (via tree_haver)Smart merge for Bash scripts
    rbs-mergeRBSRBSSmart merge for Ruby type signatures
    dotenv-mergeDotenvinternal (dotenv)Smart merge for .env files
    toml-mergeTOML -tree-sitter-toml (via tree_haver)Smart merge for TOML files
    markdown-mergeMarkdown -Commonmarker / Markly (via tree_haver) -Foundation: Shared base for Markdown mergers with inner code block merging
    markly-mergeMarkdown -Markly (via tree_haver)Smart merge for Markdown (CommonMark via cmark-gfm C)
    commonmarker-mergeMarkdown -Commonmarker (via tree_haver)Smart merge for Markdown (CommonMark via comrak Rust)
    - -

    Example implementations for the gem templating use case:

    - - - - - - - - - - - - - - - - - - - - - -
    GemPurposeDescription
    kettle-devGem DevelopmentGem templating tool using *-merge gems
    kettle-jemGem TemplatingGem template library with smart merge support
    - -

    💡 Info you can shake a stick at

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Tokens to Remember -Gem name Gem namespace -
    Works with JRuby -JRuby 10.0 Compat JRuby HEAD Compat -
    Works with Truffle Ruby -Truffle Ruby 23.1 Compat Truffle Ruby 24.1 Compat -
    Works with MRI Ruby 3 -Ruby 3.2 Compat Ruby 3.3 Compat Ruby 3.4 Compat Ruby HEAD Compat -
    Support & Community -Join Me on Daily.dev's RubyFriends Live Chat on Discord Get help from me on Upwork Get help from me on Codementor -
    Source -Source on GitLab.com Source on CodeBerg.org Source on Github.com The best SHA: dQw4w9WgXcQ! -
    Documentation -Current release on RubyDoc.info YARD on Galtzo.com Maintainer Blog GitLab Wiki GitHub Wiki -
    Compliance -License: MIT Compatible with Apache Software Projects: Verified by SkyWalking Eyes 📄ilo-declaration-img Security Policy Contributor Covenant 2.1 SemVer 2.0.0 -
    Style -Enforced Code Style Linter Keep-A-Changelog 1.0.0 Gitmoji Commits Compatibility appraised by: appraisal2 -
    Maintainer 🎖️ -Follow Me on LinkedIn Follow Me on Ruby.Social Follow Me on Bluesky Contact Maintainer My technical writing -
    -... 💖 -Find Me on WellFound: Find Me on CrunchBase My LinkTree More About Me 🧊 🐙 🛖 🧪 -
    - -

    Compatibility

    - -

    Compatible with MRI Ruby 3.2.0+, and concordant releases of JRuby, and TruffleRuby.

    - - - - - - - - - - - - - - -
    🚚 Amazing test matrix was brought to you by🔎 appraisal2 🔎 and the color 💚 green 💚
    👟 Check it out!github.com/appraisal-rb/appraisal2
    - -

    Federated DVCS

    - -
    - Find this repo on federated forges (Coming soon!) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Federated DVCS RepositoryStatusIssuesPRsWikiCIDiscussions
    🧪 kettle-rb/bash-merge on GitLab -The Truth💚💚💚🐭 Tiny Matrix
    🧊 kettle-rb/bash-merge on CodeBerg -An Ethical Mirror (Donate)💚💚⭕️ No Matrix
    🐙 kettle-rb/bash-merge on GitHub -Another Mirror💚💚💚💯 Full Matrix💚
    🎮️ Discord Server -Live Chat on DiscordLet’stalkaboutthislibrary!
    - -
    - -

    Enterprise Support Tidelift -

    - -

    Available as part of the Tidelift Subscription.

    - -
    - Need enterprise-level guarantees? - -

    The maintainers of this and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use.

    - -

    Get help from me on Tidelift

    - -
      -
    • 💡Subscribe for support guarantees covering all your FLOSS dependencies
    • -
    • 💡Tidelift is part of Sonar -
    • -
    • 💡Tidelift pays maintainers to maintain the software you depend on!
      📊@Pointy Haired Boss: An enterprise support subscription is “never gonna let you down”, and supports open source maintainers
    • -
    - -

    Alternatively:

    - -
      -
    • Live Chat on Discord
    • -
    • Get help from me on Upwork
    • -
    • Get help from me on Codementor
    • -
    - -
    - -

    ✨ Installation

    - -

    Install the gem and add to the application’s Gemfile by executing:

    - -
    bundle add bash-merge
    -
    - -

    If bundler is not being used to manage dependencies, install the gem by executing:

    - -
    gem install bash-merge
    -
    - -

    🔒 Secure Installation

    - -
    - For Medium or High Security Installations - -

    This gem is cryptographically signed, and has verifiable SHA-256 and SHA-512 checksums by -stone_checksums. Be sure the gem you install hasn’t been tampered with -by following the instructions below.

    - -

    Add my public key (if you haven’t already, expires 2045-04-29) as a trusted certificate:

    - -
    gem cert --add <(curl -Ls https://raw.github.com/galtzo-floss/certs/main/pboling.pem)
    -
    - -

    You only need to do that once. Then proceed to install with:

    - -
    gem install bash-merge -P HighSecurity
    -
    - -

    The HighSecurity trust profile will verify signed gems, and not allow the installation of unsigned dependencies.

    - -

    If you want to up your security game full-time:

    - -
    bundle config set --global trust-policy MediumSecurity
    -
    - -

    MediumSecurity instead of HighSecurity is necessary if not all the gems you use are signed.

    - -

    NOTE: Be prepared to track down certs for signed gems and add them the same way you added mine.

    - -
    - -

    🌳 Tree-Sitter Parser Installation

    - -

    This gem requires the tree-sitter-bash parser library to be installed on your system.
    -The parser is a native shared library (.so on Linux, .dylib on macOS) that provides
    -Bash syntax parsing capabilities.

    - - - -

    Download pre-built parsers from Faveod/tree-sitter-parsers:

    - -

    Linux (x64):

    -
    # Download and extract
    -curl -Lo parsers.tar.gz https://github.com/Faveod/tree-sitter-parsers/releases/download/v4.10/tree-sitter-parsers-4.10-linux-x64.tar.gz
    -tar -xzf parsers.tar.gz
    -
    -# Install system-wide (requires sudo)
    -sudo cp libtree-sitter-bash.so /usr/lib/
    -sudo ldconfig
    -
    -# Or install locally and set environment variable
    -mkdir -p ~/.local/lib/tree-sitter
    -cp libtree-sitter-bash.so ~/.local/lib/tree-sitter/
    -export TREE_SITTER_BASH_PATH="$HOME/.local/lib/tree-sitter/libtree-sitter-bash.so"
    -
    - -

    Debian/Ubuntu (amd64):

    -
    curl -Lo parsers.deb https://github.com/Faveod/tree-sitter-parsers/releases/download/v4.10/tree-sitter-parsers-4.10-amd64.deb
    -sudo dpkg -i parsers.deb
    -
    - -

    macOS (Apple Silicon):

    -
    curl -Lo parsers.tar.gz https://github.com/Faveod/tree-sitter-parsers/releases/download/v4.10/tree-sitter-parsers-4.10-macos-arm64.tar.gz
    -tar -xzf parsers.tar.gz
    -
    -# Install to a location and set environment variable
    -mkdir -p ~/.local/lib/tree-sitter
    -cp libtree-sitter-bash.dylib ~/.local/lib/tree-sitter/
    -export TREE_SITTER_BASH_PATH="$HOME/.local/lib/tree-sitter/libtree-sitter-bash.dylib"
    -
    - -

    Option 2: Build from Source

    - -
    git clone https://github.com/tree-sitter/tree-sitter-bash.git
    -cd tree-sitter-bash
    -make
    -sudo cp libtree-sitter-bash.so /usr/lib/  # Linux
    -# or
    -sudo cp libtree-sitter-bash.dylib /usr/local/lib/  # macOS
    -
    - -

    Option 3: Package Manager

    - -

    Some package managers provide tree-sitter parsers:

    - -

    Fedora Atomic (Silverblue, Kinoite, Bazzite, Aurora, etc.):

    -
    # Install via rpm-ostree (requires reboot)
    -rpm-ostree install libtree-sitter-bash
    -
    -# The library will be installed to /usr/lib64/libtree-sitter-bash.so
    -# You may need to set the environment variable:
    -export TREE_SITTER_BASH_PATH="/usr/lib64/libtree-sitter-bash.so"
    -
    - -

    Fedora (traditional):

    -
    sudo dnf install libtree-sitter-bash
    -
    - -

    Arch Linux:

    -
    # Check AUR for tree-sitter-bash
    -yay -S tree-sitter-bash
    -
    - -

    Environment Variable

    - -

    If the parser is not in a standard location (/usr/lib/, /usr/lib64/, /usr/local/lib/),
    -set the TREE_SITTER_BASH_PATH environment variable to point to the parser library:

    - -
    export TREE_SITTER_BASH_PATH="/path/to/libtree-sitter-bash.so"
    -
    - -

    Note: Some distributions install the library with a version number suffix
    -(e.g., libtree-sitter-bash.so.14 instead of libtree-sitter-bash.so).
    -If the gem can’t find the parser, check for versioned files and either:

    -
      -
    • Set TREE_SITTER_BASH_PATH to the full versioned path, or
    • -
    • Create a symlink: sudo ln -s /usr/lib64/libtree-sitter-bash.so.14 /usr/lib64/libtree-sitter-bash.so -
    • -
    - -

    Add this to your shell profile (.bashrc, .zshrc, etc.) for persistence.

    - -

    ⚙️ Configuration

    - -
    merger = Bash::Merge::SmartMerger.new(
    -  template_content,
    -  dest_content,
    -  # Which version to prefer when nodes match
    -  # :destination (default) - keep destination code
    -  # :template - use template code
    -  preference: :destination,
    -
    -  # Whether to add template-only nodes to the result
    -  # false (default) - only include nodes that exist in destination
    -  # true - include all template nodes (functions, variables, etc.)
    -  add_template_only_nodes: false,
    -
    -  # Token for freeze block markers
    -  # Default: "bash-merge"
    -  # Looks for: # bash-merge:freeze / # bash-merge:unfreeze
    -  freeze_token: "bash-merge",
    -
    -  # Custom signature generator (optional)
    -  # Receives a node, returns a signature array or nil
    -  signature_generator: ->(node) { [:function, node.name] if node.type == :function_definition },
    -)
    -
    - -

    🔧 Basic Usage

    - -

    Simple Merge

    - -
    require "bash/merge"
    -
    -# Template defines the structure
    -template = <<~BASH
    -  #!/bin/bash
    -  
    -  # Configuration
    -  APP_NAME="myapp"
    -  DEBUG=false
    -  
    -  # Main function
    -  main() {
    -    echo "Starting $APP_NAME"
    -    setup
    -    run
    -  }
    -  
    -  setup() {
    -    echo "Setting up..."
    -  }
    -  
    -  run() {
    -    echo "Running..."
    -  }
    -  
    -  main "$@"
    -BASH
    -
    -# Destination has customizations
    -destination = <<~BASH
    -  #!/bin/bash
    -  
    -  # Configuration
    -  APP_NAME="myapp-custom"
    -  DEBUG=true
    -  LOG_FILE="/var/log/myapp.log"
    -  
    -  # Main function with custom logging
    -  main() {
    -    echo "Starting $APP_NAME" | tee -a "$LOG_FILE"
    -    setup
    -    run
    -  }
    -  
    -  setup() {
    -    echo "Custom setup..."
    -  }
    -  
    -  main "$@"
    -BASH
    -
    -merger = Bash::Merge::SmartMerger.new(template, destination)
    -result = merger.merge
    -puts result.to_bash
    -
    - -

    Using Freeze Blocks

    - -

    Freeze blocks protect sections from being overwritten during merge:

    - -
    #!/bin/bash
    -
    -# Configuration
    -APP_NAME="myapp"
    -
    -# bash-merge:freeze Custom credentials
    -DB_USER="production_user"
    -DB_PASS="super_secret_password"
    -API_KEY="my_production_api_key"
    -# bash-merge:unfreeze
    -
    -# Standard functions
    -main() {
    -  echo "Starting $APP_NAME"
    -}
    -
    -main "$@"
    -
    - -

    Content between # bash-merge:freeze and # bash-merge:unfreeze markers is preserved from the destination file, regardless of what the template contains.

    - -

    Adding Template-Only Nodes

    - -
    merger = Bash::Merge::SmartMerger.new(
    -  template,
    -  destination,
    -  add_template_only_nodes: true,
    -)
    -result = merger.merge
    -# Result includes functions/variables from template that don't exist in destination
    -
    - -

    🦷 FLOSS Funding

    - -

    While kettle-rb tools are free software and will always be, the project would benefit immensely from some funding.
    -Raising a monthly budget of… “dollars” would make the project more sustainable.

    - -

    We welcome both individual and corporate sponsors! We also offer a
    -wide array of funding channels to account for your preferences
    -(although currently Open Collective is our preferred funding platform).

    - -

    If you’re working in a company that’s making significant use of kettle-rb tools we’d
    -appreciate it if you suggest to your company to become a kettle-rb sponsor.

    - -

    You can support the development of kettle-rb tools via
    -GitHub Sponsors,
    -Liberapay,
    -PayPal,
    -Open Collective
    -and Tidelift.

    - - - - - - - - - - - - -
    📍 NOTE
    If doing a sponsorship in the form of donation is problematic for your company
    from an accounting standpoint, we’d recommend the use of Tidelift,
    where you can get a support-like subscription instead.
    - -

    Open Collective for Individuals

    - -

    Support us with a monthly donation and help us continue our activities. [Become a backer]

    - -

    NOTE: kettle-readme-backers updates this list every day, automatically.

    - - -

    No backers yet. Be the first!
    -

    - -

    Open Collective for Organizations

    - -

    Become a sponsor and get your logo on our README on GitHub with a link to your site. [Become a sponsor]

    - -

    NOTE: kettle-readme-backers updates this list every day, automatically.

    - - -

    No sponsors yet. Be the first!
    -

    - -

    Another way to support open-source

    - -

    I’m driven by a passion to foster a thriving open-source community – a space where people can tackle complex problems, no matter how small. Revitalizing libraries that have fallen into disrepair, and building new libraries focused on solving real-world challenges, are my passions. I was recently affected by layoffs, and the tech jobs market is unwelcoming. I’m reaching out here because your support would significantly aid my efforts to provide for my family, and my farm (11 🐔 chickens, 2 🐶 dogs, 3 🐰 rabbits, 8 🐈‍ cats).

    - -

    If you work at a company that uses my work, please encourage them to support me as a corporate sponsor. My work on gems you use might show up in bundle fund.

    - -

    I’m developing a new library, floss_funding, designed to empower open-source developers like myself to get paid for the work we do, in a sustainable way. Please give it a look.

    - -

    Floss-Funding.dev: 👉️ No network calls. 👉️ No tracking. 👉️ No oversight. 👉️ Minimal crypto hashing. 💡 Easily disabled nags

    - -

    OpenCollective Backers OpenCollective Sponsors Sponsor Me on Github Liberapay Goal Progress Donate on PayPal Buy me a coffee Donate on Polar Donate to my FLOSS efforts at ko-fi.com Donate to my FLOSS efforts using Patreon

    - -

    🔐 Security

    - -

    See SECURITY.md.

    - -

    🤝 Contributing

    - -

    If you need some ideas of where to help, you could work on adding more code coverage,
    -or if it is already 💯 (see below) check reek, issues, or PRs,
    -or use the gem and think about how it could be better.

    - -

    We Keep A Changelog so if you make changes, remember to update it.

    - -

    See CONTRIBUTING.md for more detailed instructions.

    - -

    🚀 Release Instructions

    - -

    See CONTRIBUTING.md.

    - -

    Code Coverage

    - -

    Coverage Graph

    - -

    Coveralls Test Coverage

    - -

    QLTY Test Coverage

    - -

    🪇 Code of Conduct

    - -

    Everyone interacting with this project’s codebases, issue trackers,
    -chat rooms and mailing lists agrees to follow the Contributor Covenant 2.1.

    - -

    🌈 Contributors

    - -

    Contributors

    - -

    Made with contributors-img.

    - -

    Also see GitLab Contributors: https://gitlab.com/kettle-rb/bash-merge/-/graphs/main

    - -
    - ⭐️ Star History - - - - - - Star History Chart - - - -
    - -

    📌 Versioning

    - -

    This Library adheres to Semantic Versioning 2.0.0.
    -Violations of this scheme should be reported as bugs.
    -Specifically, if a minor or patch version is released that breaks backward compatibility,
    -a new version should be immediately released that restores compatibility.
    -Breaking changes to the public API will only be introduced with new major versions.

    - -
    -

    dropping support for a platform is both obviously and objectively a breaking change

    -—Jordan Harband (@ljharb, maintainer of SemVer) in SemVer issue 716

    -
    - -

    I understand that policy doesn’t work universally (“exceptions to every rule!”),
    -but it is the policy here.
    -As such, in many cases it is good to specify a dependency on this library using
    -the Pessimistic Version Constraint with two digits of precision.

    - -

    For example:

    - -
    spec.add_dependency("bash-merge", "~> 1.0")
    -
    - -
    - 📌 Is "Platform Support" part of the public API? More details inside. - -

    SemVer should, IMO, but doesn’t explicitly, say that dropping support for specific Platforms -is a breaking change to an API, and for that reason the bike shedding is endless.

    - -

    To get a better understanding of how SemVer is intended to work over a project’s lifetime, -read this article from the creator of SemVer:

    - - - -
    - -

    See CHANGELOG.md for a list of releases.

    - -

    📄 License

    - -

    The gem is available as open source under the terms of
    -the MIT License License: MIT.
    -See LICENSE.txt for the official Copyright Notice.

    - - - - - -

    🤑 A request for help

    - -

    Maintainers have teeth and need to pay their dentists.
    -After getting laid off in an RIF in March, and encountering difficulty finding a new one,
    -I began spending most of my time building open source tools.
    -I’m hoping to be able to pay for my kids’ health insurance this month,
    -so if you value the work I am doing, I need your support.
    -Please consider sponsoring me or the project.

    - -

    To join the community or get help 👇️ Join the Discord.

    - -

    Live Chat on Discord

    - -

    To say “thanks!” ☝️ Join the Discord or 👇️ send money.

    - -

    Sponsor kettle-rb/bash-merge on Open Source Collective 💌 Sponsor me on GitHub Sponsors 💌 Sponsor me on Liberapay 💌 Donate on PayPal

    - -

    Please give the project a star ⭐ ♥.

    - -

    Thanks for RTFM. ☺️

    - -
    - - - -
    - - \ No newline at end of file diff --git a/docs/method_list.html b/docs/method_list.html index 25a14e3..332af36 100644 --- a/docs/method_list.html +++ b/docs/method_list.html @@ -88,14 +88,6 @@

    Method List

  • -
    - #add_template_only_nodes - Bash::Merge::SmartMerger -
    -
  • - - -
  • #ast Bash::Merge::FileAnalysis @@ -103,7 +95,7 @@

    Method List

  • -
  • +
  • #blank_line? Bash::Merge::CommentTracker @@ -111,7 +103,7 @@

    Method List

  • -
  • +
  • #case_statement? Bash::Merge::NodeWrapper @@ -119,7 +111,7 @@

    Method List

  • -
  • +
  • #children Bash::Merge::NodeWrapper @@ -127,7 +119,7 @@

    Method List

  • -
  • +
  • #clear Bash::Merge::Emitter @@ -135,7 +127,7 @@

    Method List

  • -
  • +
  • #command? Bash::Merge::FreezeNode @@ -143,7 +135,7 @@

    Method List

  • -
  • +
  • #command? Bash::Merge::NodeWrapper @@ -151,7 +143,7 @@

    Method List

  • -
  • +
  • #command_name Bash::Merge::NodeWrapper @@ -159,7 +151,7 @@

    Method List

  • -
  • +
  • #comment? Bash::Merge::NodeWrapper @@ -167,7 +159,7 @@

    Method List

  • -
  • +
  • #comment_at Bash::Merge::CommentTracker @@ -175,7 +167,7 @@

    Method List

  • -
  • +
  • #comment_tracker Bash::Merge::FileAnalysis @@ -183,7 +175,7 @@

    Method List

  • -
  • +
  • #comments Bash::Merge::CommentTracker @@ -191,7 +183,7 @@

    Method List

  • -
  • +
  • #comments_in_range Bash::Merge::CommentTracker @@ -199,7 +191,7 @@

    Method List

  • -
  • +
  • #content Bash::Merge::MergeResult @@ -207,7 +199,7 @@

    Method List

  • -
  • +
  • #content Bash::Merge::NodeWrapper @@ -215,14 +207,6 @@

    Method List

  • -
  • -
    - #dest_analysis - Bash::Merge::SmartMerger -
    -
  • - -
  • #emit_blank_line @@ -464,14 +448,6 @@

    Method List

  • -
    - #freeze_token - Bash::Merge::SmartMerger -
    -
  • - - -
  • #full_line_comment? Bash::Merge::CommentTracker @@ -479,7 +455,7 @@

    Method List

  • -
  • +
  • #function_definition? Bash::Merge::FreezeNode @@ -487,7 +463,7 @@

    Method List

  • -
  • +
  • #function_definition? Bash::Merge::NodeWrapper @@ -495,7 +471,7 @@

    Method List

  • -
  • +
  • #function_name Bash::Merge::NodeWrapper @@ -503,7 +479,7 @@

    Method List

  • -
  • +
  • #if_statement? Bash::Merge::NodeWrapper @@ -511,7 +487,7 @@

    Method List

  • -
  • +
  • #in_freeze_block? Bash::Merge::FileAnalysis @@ -519,7 +495,7 @@

    Method List

  • -
  • +
  • #indent_level Bash::Merge::Emitter @@ -527,7 +503,7 @@

    Method List

  • -
  • +
  • #indent_size Bash::Merge::Emitter @@ -535,7 +511,7 @@

    Method List

  • -
  • +
  • #initialize Bash::Merge::ParseError @@ -543,7 +519,7 @@

    Method List

  • -
  • +
  • #initialize Bash::Merge::Emitter @@ -551,7 +527,7 @@

    Method List

  • -
  • +
  • #initialize Bash::Merge::FreezeNode @@ -559,7 +535,7 @@

    Method List

  • -
  • +
  • #initialize Bash::Merge::MergeResult @@ -567,7 +543,7 @@

    Method List

  • -
  • +
  • #initialize Bash::Merge::NodeWrapper @@ -575,7 +551,7 @@

    Method List

  • -
  • +
  • #initialize Bash::Merge::SmartMerger @@ -583,7 +559,7 @@

    Method List

  • -
  • +
  • #initialize Bash::Merge::FileAnalysis @@ -591,7 +567,7 @@

    Method List

  • -
  • +
  • #initialize Bash::Merge::CommentTracker @@ -599,7 +575,7 @@

    Method List

  • -
  • +
  • #initialize Bash::Merge::ConflictResolver @@ -607,7 +583,7 @@

    Method List

  • -
  • +
  • #inline_comment Bash::Merge::NodeWrapper @@ -615,7 +591,7 @@

    Method List

  • -
  • +
  • #inline_comment_at Bash::Merge::CommentTracker @@ -623,7 +599,7 @@

    Method List

  • -
  • +
  • #inspect Bash::Merge::FreezeNode @@ -631,7 +607,7 @@

    Method List

  • -
  • +
  • #inspect Bash::Merge::NodeWrapper @@ -639,7 +615,7 @@

    Method List

  • -
  • +
  • #leading_comments Bash::Merge::NodeWrapper @@ -647,7 +623,7 @@

    Method List

  • -
  • +
  • #leading_comments_before Bash::Merge::CommentTracker @@ -655,7 +631,7 @@

    Method List

  • -
  • +
  • #lines Bash::Merge::Emitter @@ -663,7 +639,7 @@

    Method List

  • -
  • +
  • #lines Bash::Merge::NodeWrapper @@ -671,7 +647,7 @@

    Method List

  • -
  • +
  • #lines Bash::Merge::CommentTracker @@ -679,7 +655,7 @@

    Method List

  • -
  • +
  • log_node Bash::Merge::DebugLogger @@ -687,17 +663,9 @@

    Method List

  • -
  • -
    - #merge - Bash::Merge::SmartMerger -
    -
  • - -
  • - #merge_result + #merge Bash::Merge::SmartMerger
  • @@ -736,14 +704,6 @@

    Method List

  • -
    - #preference - Bash::Merge::SmartMerger -
    -
  • - - -
  • #resolve Bash::Merge::ConflictResolver @@ -751,23 +711,7 @@

    Method List

  • -
  • -
    - #resolver - Bash::Merge::SmartMerger -
    -
  • - -
  • -
    - #result - Bash::Merge::SmartMerger -
    -
  • - - -
  • #root_node Bash::Merge::FileAnalysis @@ -775,7 +719,7 @@

    Method List

  • -
  • +
  • #shebang? Bash::Merge::CommentTracker @@ -783,7 +727,7 @@

    Method List

  • -
  • +
  • #signature Bash::Merge::FreezeNode @@ -791,7 +735,7 @@

    Method List

  • -
  • +
  • #signature Bash::Merge::NodeWrapper @@ -799,7 +743,7 @@

    Method List

  • -
  • +
  • #source Bash::Merge::NodeWrapper @@ -807,7 +751,7 @@

    Method List

  • -
  • +
  • #start_line Bash::Merge::NodeWrapper @@ -815,7 +759,7 @@

    Method List

  • -
  • +
  • #statements Bash::Merge::FileAnalysis @@ -823,7 +767,7 @@

    Method List

  • -
  • +
  • #statistics Bash::Merge::MergeResult @@ -831,14 +775,6 @@

    Method List

  • -
  • -
    - #template_analysis - Bash::Merge::SmartMerger -
    -
  • - -
  • #text diff --git a/docs/top-level-namespace.html b/docs/top-level-namespace.html index b816efa..e69de29 100644 --- a/docs/top-level-namespace.html +++ b/docs/top-level-namespace.html @@ -1,110 +0,0 @@ - - - - - - - Top Level Namespace - - — Documentation by YARD 0.9.38 - - - - - - - - - - - - - - - - - - - -
    - - -

    Top Level Namespace - - - -

    -
    - - - - - - - - - - - -
    - -

    Defined Under Namespace

    -

    - - - Modules: Bash - - - - -

    - - - - - - - - - -
    - - - -
    - - \ No newline at end of file diff --git a/gemfiles/modular/templating.gemfile b/gemfiles/modular/templating.gemfile index d4ad41c..9d56247 100644 --- a/gemfiles/modular/templating.gemfile +++ b/gemfiles/modular/templating.gemfile @@ -10,7 +10,7 @@ if ENV.fetch("KETTLE_RB_DEV", "false").casecmp?("true") # Inner-merge dependencies for CodeBlockMerger # These are loaded on-demand when merging fenced code blocks - # gem "bash-merge", path: "../../../bash-merge" # dotenv code blocks (this gem) + # gem "bash-merge", path: "../../../bash-merge" # bash code blocks (this gem) gem "dotenv-merge", path: "../../../dotenv-merge" # dotenv code blocks gem "json-merge", path: "../../../json-merge" # JSON code blocks gem "jsonc-merge", path: "../../../jsonc-merge" # JSONC code blocks @@ -21,7 +21,7 @@ if ENV.fetch("KETTLE_RB_DEV", "false").casecmp?("true") gem "rbs-merge", path: "../../../rbs-merge" # RBS code blocks gem "toml-merge", path: "../../../toml-merge" # TOML code blocks else - # gem "bash-merge" # dotenv code blocks (this gem) + # gem "bash-merge" # bash code blocks (this gem) gem "dotenv-merge" # dotenv code blocks gem "json-merge" # JSON code blocks gem "jsonc-merge" # JSONC code blocks diff --git a/gemfiles/modular/tree_sitter.gemfile b/gemfiles/modular/tree_sitter.gemfile index 26a40b3..6dbc9a3 100644 --- a/gemfiles/modular/tree_sitter.gemfile +++ b/gemfiles/modular/tree_sitter.gemfile @@ -21,7 +21,7 @@ platform :mri do gem "ruby_tree_sitter", require: false # DO NOT LOAD, because conflicts with FFI # Rust Backend gem "tree_stump", - # path: "../../vendor/tree_stump" - github: "pboling/tree_stump", - branch: "tree_haver" + # path: "../../vendor/tree_stump" + github: "joker1007/tree_stump", + branch: "main" end diff --git a/lib/bash/merge.rb b/lib/bash/merge.rb index ab6e97b..056ab1f 100644 --- a/lib/bash/merge.rb +++ b/lib/bash/merge.rb @@ -18,9 +18,14 @@ bash_available = bash_finder.available? bash_finder.register! if bash_available -# Ensure grammar is available +# Only warn if the grammar file is actually missing (not just runtime unavailable) +# When the runtime isn't available, tree-sitter backends just won't be used, +# which is expected behavior - no need to warn the user. unless bash_available - warn "WARNING: Bash grammar not available. #{bash_finder.not_found_message}" + grammar_path = bash_finder.find_library_path + unless grammar_path + warn "WARNING: Bash grammar not available. #{bash_finder.not_found_message}" + end end require "version_gem" diff --git a/lib/bash/merge/debug_logger.rb b/lib/bash/merge/debug_logger.rb index c86b78b..144c6f8 100644 --- a/lib/bash/merge/debug_logger.rb +++ b/lib/bash/merge/debug_logger.rb @@ -18,23 +18,25 @@ module DebugLogger self.env_var_name = "BASH_MERGE_DEBUG" self.log_prefix = "[Bash::Merge]" - # Override log_node to handle Bash-specific node types. - # - # @param node [Object] Node to log information about - # @param label [String] Label for the node - def self.log_node(node, label: "Node") - return unless enabled? + class << self + # Override log_node to handle Bash-specific node types. + # + # @param node [Object] Node to log information about + # @param label [String] Label for the node + def log_node(node, label: "Node") + return unless enabled? - info = case node - when Bash::Merge::FreezeNode - {type: "FreezeNode", lines: "#{node.start_line}..#{node.end_line}"} - when Bash::Merge::NodeWrapper - {type: node.type.to_s, lines: "#{node.start_line}..#{node.end_line}"} - else - extract_node_info(node) - end + info = case node + when Bash::Merge::FreezeNode + {type: "FreezeNode", lines: "#{node.start_line}..#{node.end_line}"} + when Bash::Merge::NodeWrapper + {type: node.type.to_s, lines: "#{node.start_line}..#{node.end_line}"} + else + extract_node_info(node) + end - debug(label, info) + debug(label, info) + end end end end diff --git a/spec/bash/merge/node_wrapper_spec.rb b/spec/bash/merge/node_wrapper_spec.rb index f51d94c..0a160f5 100644 --- a/spec/bash/merge/node_wrapper_spec.rb +++ b/spec/bash/merge/node_wrapper_spec.rb @@ -455,7 +455,7 @@ end it "handles heredocs" do - source = <<~'BASH' + source = <<~BASH cat <