smorg-rb 7.1.1 → 7.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c450da4a626c5f983cd9b487e0e42714ad674003f3b779e99716ce9237268df9
4
- data.tar.gz: e21ef620c696b6cd36b4fd8bd0c7619cb788992f1a3685ca3e01e8c578633412
3
+ metadata.gz: 12f587107d9a170feee761ed0191d98959c214d4507ea7d7212256947760ccea
4
+ data.tar.gz: deb44e4aaa799376879ec3d0f1183a55707529f5ac01420344733e6836e0c9ad
5
5
  SHA512:
6
- metadata.gz: befc7f7bd499c36ebfeaa1b04aea6fe1672db736a2a40081b759ccdedc364427c22088112e5d0849b395da1f21cf406e6c73f0aaa2d4a4730fcecdc1ed7169dc
7
- data.tar.gz: 8f3c51cd057fef2dadb1553c29b7eff34587afcd105bcdde4c78332baff7ac6b9c90f8ecb5b2313b48a7fd5943696130e4560eae497dc66dd15beba0766c5b14
6
+ metadata.gz: a0b5b8de7fad58ef0489c375fd2010f32f2031a25363c1ee28ee0a77a0827cf87d2852212cd57f91a4bd552b04ce001297fd4a67defe770b2481528a1f4c534d
7
+ data.tar.gz: 448d2f9ad40cb86205f60f9d00e938b001c0595d6d12f654a51fab5cd02aabd07c75d2ad4412d50a69bc9a604fa5dd6f57a7d73e48dae7283d9183edc6fa204c
checksums.yaml.gz.sig CHANGED
Binary file
data/README.md CHANGED
@@ -45,7 +45,7 @@ I've summarized my thoughts in [this blog post](https://dev.to/galtzo/hostile-ta
45
45
 
46
46
  ### Compatibility
47
47
 
48
- Compatible with MRI Ruby 4.0.0+, and concordant releases of JRuby, and TruffleRuby.
48
+ Compatible with MRI Ruby 4.0.0+, JRuby, and TruffleRuby.
49
49
  CI workflows and Appraisals are generated for MRI Ruby 4.0.0+.
50
50
  This test floor is configured by `ruby.test_minimum` in `.kettle-jem.yml` and
51
51
  may be higher than the gem's runtime compatibility floor when legacy Rubies are
@@ -108,20 +108,12 @@ See [SECURITY.md][🔐security].
108
108
  ## 🤝 Contributing
109
109
 
110
110
  If you need some ideas of where to help, you could work on adding more code coverage,
111
- or if it is already 💯 (see [below](#code-coverage)) check [issues][🤝gh-issues] or [PRs][🤝gh-pulls],
112
- or use the gem and think about how it could be better.
111
+ check [issues][🤝gh-issues] or [PRs][🤝gh-pulls], or use the gem and think about how it could be better.
113
112
 
114
113
  We [![Keep A Changelog][📗keep-changelog-img]][📗keep-changelog] so if you make changes, remember to update it.
115
114
 
116
115
  See [CONTRIBUTING.md][🤝contributing] for more detailed instructions.
117
116
 
118
- ### Code Coverage
119
-
120
- <details markdown="1">
121
- <summary>Coverage service badges</summary>
122
-
123
- </details>
124
-
125
117
  ## 📌 Versioning
126
118
 
127
119
  This library follows [![Semantic Versioning 2.0.0][📌semver-img]][📌semver] for its public API where practical.
@@ -261,7 +253,7 @@ If none of the available licenses suit your use case, please [contact us](mailto
261
253
  [📌gitmoji]: https://gitmoji.dev
262
254
  [📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
263
255
  [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
264
- [🧮kloc-img]: https://img.shields.io/badge/KLOC-0.494-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
256
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-0.460-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
265
257
  [🔐security]: https://github.com/structuredmerge/structuredmerge-ruby/blob/main/SECURITY.md
266
258
  [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
267
259
  [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
@@ -5,7 +5,7 @@ module Smorg
5
5
  # Version namespace for this gem.
6
6
  module Version
7
7
  # Current gem version.
8
- VERSION = '7.1.1'
8
+ VERSION = '7.1.4'
9
9
  end
10
10
  # Current gem version exposed at the traditional constant location.
11
11
  VERSION = Version::VERSION # Traditional Constant Location
data/lib/smorg/rb.rb CHANGED
@@ -1,21 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'English'
4
+ require 'bash/merge'
4
5
  require 'go-merge'
6
+ require 'html/merge'
5
7
  require 'ast/merge'
6
8
  require 'ast-merge-git'
7
9
  require 'diff/lcs'
8
10
  require 'diff/lcs/hunk'
11
+ require 'dotenv/merge'
9
12
  require 'json'
10
13
  require 'json-merge'
11
14
  require 'kettle/jem'
12
15
  require 'kettle/jem/tasks/install_task'
13
- require 'markly/merge'
16
+ require 'markdown/merge'
14
17
  require 'plain-merge'
18
+ require 'rbs/merge'
19
+ require 'toml/merge'
20
+ require 'typescript/merge'
21
+ require 'prism/merge'
22
+ require 'psych/merge'
15
23
  require_relative 'rb/version'
16
24
 
17
25
  module Smorg
18
- module RB
26
+ module RB # rubocop:disable Metrics/ModuleLength
19
27
  EXIT_SUCCESS = 0
20
28
  EXIT_UNRESOLVED_CONFLICT = 1
21
29
  EXIT_USER_ERROR = 2
@@ -438,7 +446,8 @@ module Smorg
438
446
  path_name: path_name,
439
447
  ok: ok,
440
448
  exit_code: exit_code,
441
- fallbacks: fallbacks,
449
+ fallbacks: result.fetch(:fallbacks, []) + fallbacks,
450
+ conflicts: result.fetch(:conflicts, []),
442
451
  change_classifications: result.fetch(:change_classifications, []),
443
452
  owned_regions: result.fetch(:owned_regions, []),
444
453
  render_report: result[:render_report],
@@ -447,6 +456,8 @@ module Smorg
447
456
  secondary_formatting_metrics: result[:secondary_formatting_metrics],
448
457
  default_driver_evaluation: result[:default_driver_evaluation],
449
458
  profile: result[:profile],
459
+ provider: result[:provider],
460
+ verification: result[:verification],
450
461
  diagnostics: result.fetch(:diagnostics, [])
451
462
  }
452
463
  File.write(report_path, "#{JSON.pretty_generate(Ast::Merge.json_ready(report))}\n")
@@ -632,164 +643,100 @@ module Smorg
632
643
  end
633
644
 
634
645
  [
646
+ '*.bash merge=smorg-rb diff=smorg-rb smorg.language=bash',
635
647
  '*.go merge=smorg-rb diff=smorg-rb smorg.language=go',
648
+ '*.htm merge=smorg-rb diff=smorg-rb smorg.language=html',
649
+ '*.html merge=smorg-rb diff=smorg-rb smorg.language=html',
650
+ '*.env merge=smorg-rb diff=smorg-rb smorg.language=dotenv',
651
+ '.env merge=smorg-rb diff=smorg-rb smorg.language=dotenv',
652
+ '.env.* merge=smorg-rb diff=smorg-rb smorg.language=dotenv',
636
653
  '*.json merge=smorg-rb diff=smorg-rb smorg.language=json',
637
654
  '*.jsonc merge=smorg-rb diff=smorg-rb smorg.language=jsonc',
638
655
  '*.json5 merge=smorg-rb diff=smorg-rb smorg.language=json5',
639
656
  '*.md merge=smorg-rb diff=smorg-rb smorg.language=markdown',
640
- '*.markdown merge=smorg-rb diff=smorg-rb smorg.language=markdown'
657
+ '*.markdown merge=smorg-rb diff=smorg-rb smorg.language=markdown',
658
+ '*.rb merge=smorg-rb diff=smorg-rb smorg.language=ruby',
659
+ '*.rbs merge=smorg-rb diff=smorg-rb smorg.language=rbs',
660
+ '*.rs merge=smorg-rb diff=smorg-rb smorg.language=rust',
661
+ '*.sh merge=smorg-rb diff=smorg-rb smorg.language=bash',
662
+ '*.toml merge=smorg-rb diff=smorg-rb smorg.language=toml',
663
+ '*.ts merge=smorg-rb diff=smorg-rb smorg.language=typescript',
664
+ '*.tsx merge=smorg-rb diff=smorg-rb smorg.language=tsx',
665
+ '*.yml merge=smorg-rb diff=smorg-rb smorg.language=yaml',
666
+ '*.yaml merge=smorg-rb diff=smorg-rb smorg.language=yaml'
641
667
  ].each { |line| stdout.puts(line) }
642
668
  EXIT_SUCCESS
643
669
  end
644
670
 
671
+ MERGE_PROVIDER_OPTIONS = {
672
+ 'bash' => {
673
+ provider_id: 'ruby.bash', family: 'bash', dialect: 'bash', backend: 'kreuzberg-language-pack'
674
+ },
675
+ 'go' => {
676
+ provider_id: 'ruby.go', family: 'go', dialect: 'go', backend: 'kreuzberg-language-pack'
677
+ },
678
+ 'html' => {
679
+ provider_id: 'ruby.html', family: 'html', dialect: 'html', backend: 'kreuzberg-language-pack'
680
+ },
681
+ 'dotenv' => { family: 'dotenv', dialect: 'dotenv', backend: 'dotenv-line' },
682
+ 'json' => { family: 'json', dialect: 'json' },
683
+ 'jsonc' => { family: 'json', dialect: 'jsonc' },
684
+ 'json5' => { family: 'json', dialect: 'json5' },
685
+ 'markdown' => {
686
+ provider_id: 'ruby.markdown', family: 'markdown', dialect: 'markdown', backend: 'kreuzberg-language-pack'
687
+ },
688
+ 'ruby' => { family: 'ruby', dialect: 'ruby', backend: 'prism' },
689
+ 'rbs' => { provider_id: 'ruby.rbs', family: 'rbs', dialect: 'rbs', backend: 'rbs' },
690
+ 'rust' => {
691
+ provider_id: 'ruby.rust', family: 'rust', dialect: 'rust', backend: 'kreuzberg-language-pack'
692
+ },
693
+ 'toml' => {
694
+ provider_id: 'ruby.toml', family: 'toml', dialect: 'toml', backend: 'kreuzberg-language-pack'
695
+ },
696
+ 'typescript' => {
697
+ provider_id: 'ruby.typescript', family: 'typescript', dialect: 'typescript',
698
+ backend: 'kreuzberg-language-pack'
699
+ },
700
+ 'tsx' => {
701
+ provider_id: 'ruby.typescript', family: 'typescript', dialect: 'tsx', backend: 'kreuzberg-language-pack'
702
+ },
703
+ 'yaml' => { provider_id: 'ruby.yaml.psych', family: 'yaml', dialect: 'yaml', backend: 'psych' },
704
+ 'text' => { family: 'text', dialect: 'text', profile_id: 'coarse_document' }
705
+ }.freeze
706
+
645
707
  def merge_by_path(path_name, language, conflict_marker_size, fallback_policy, ancestor_source, current_source,
646
708
  other_source)
647
- case normalize_language(language, path_name)
648
- when 'go'
649
- Go::Merge.merge_go(other_source, current_source, 'go')
650
- when 'json'
651
- merge3_result(
652
- Ast::Merge::Git.merge3(
653
- base_source: ancestor_source,
654
- ours_source: current_source,
655
- theirs_source: other_source,
656
- path_name: path_name,
657
- language: 'json',
658
- dialect: 'json',
659
- profile_id: 'json.keyed-object',
660
- fallback_policy: fallback_policy,
661
- conflict_marker_size: conflict_marker_size,
662
- render_policy: 'canonical'
663
- )
664
- )
665
- when 'jsonc'
666
- Json::Merge.merge_json(other_source, current_source, 'jsonc')
667
- when 'json5'
668
- Json::Merge.merge_json(other_source, current_source, 'json5')
669
- when 'markdown'
670
- merge_markdown(ancestor_source, current_source, other_source)
671
- when 'text'
672
- Plain::Merge.merge_text(other_source, current_source)
673
- else
674
- unsupported_language_result(normalize_language(language, path_name), path_name)
675
- end
676
- end
709
+ normalized_language = normalize_language(language, path_name)
710
+ provider_options = MERGE_PROVIDER_OPTIONS[normalized_language]
711
+ return unsupported_language_result(normalized_language, path_name) unless provider_options
677
712
 
678
- def merge_markdown(ancestor_source, current_source, other_source)
679
- return { ok: true, diagnostics: [], output: current_source, policies: [] } if other_source == ancestor_source
680
- return { ok: true, diagnostics: [], output: other_source, policies: [] } if current_source == ancestor_source
681
- if markdown_theirs_changes_already_present?(ancestor_source, current_source, other_source)
682
- return { ok: true, diagnostics: [], output: current_source, policies: [] }
683
- end
713
+ merge_with_provider(
714
+ path_name, provider_options,
715
+ merge_options_for(ancestor_source, current_source, other_source, fallback_policy, conflict_marker_size)
716
+ )
717
+ end
684
718
 
685
- result = Markly::Merge::SmartMerger.new(
686
- other_source,
687
- current_source,
688
- add_template_only_nodes: true,
689
- inner_merge_code_blocks: true,
690
- inner_merge_lists: true,
691
- normalize_whitespace: :basic
692
- ).merge_result
693
- {
694
- ok: result.success?,
695
- diagnostics: result.conflicts.map do |conflict|
696
- {
697
- severity: 'error',
698
- category: 'merge_conflict',
699
- message: conflict[:message] || conflict[:reason] || 'unresolved Markdown conflict'
700
- }
701
- end,
702
- output: result.content,
703
- policies: []
704
- }
705
- rescue Markdown::Merge::ParseError => e
706
- {
707
- ok: false,
708
- diagnostics: [{ severity: 'error', category: 'parse_error', message: e.message }],
709
- policies: []
710
- }
711
- rescue StandardError => e
719
+ def merge_options_for(ancestor_source, current_source, other_source, fallback_policy, conflict_marker_size)
712
720
  {
713
- ok: false,
714
- diagnostics: [{ severity: 'error', category: 'merge_error', message: e.message }],
715
- policies: []
721
+ base_source: ancestor_source,
722
+ ours_source: current_source,
723
+ theirs_source: other_source,
724
+ fallback_policy: fallback_policy,
725
+ conflict_marker_size: conflict_marker_size
716
726
  }
717
727
  end
718
728
 
719
- def markdown_theirs_changes_already_present?(ancestor_source, current_source, other_source)
720
- ancestor_sections = markdown_sections(ancestor_source)
721
- current_sections = markdown_sections(current_source)
722
- other_sections = markdown_sections(other_source)
723
- return false unless ancestor_sections && current_sections && other_sections
724
-
725
- ancestor_by_path = sections_by_path(ancestor_sections)
726
- ancestor_by_heading = sections_by_heading(ancestor_sections)
727
- current_by_path = sections_by_path(current_sections)
728
- current_by_heading = sections_by_heading(current_sections)
729
-
730
- changed_sections = other_sections.filter do |section|
731
- ancestor_text = ancestor_by_path[section[:path]] || ancestor_by_heading[markdown_heading_key(section[:text])]
732
- ancestor_text != section[:text]
733
- end
734
- return false if changed_sections.empty?
735
-
736
- changed_sections.all? do |section|
737
- other_text = section[:text]
738
- heading_key = markdown_heading_key(other_text)
739
- ancestor_text = ancestor_by_heading[heading_key] || ancestor_by_path.fetch(section[:path], '')
740
- current_text = current_by_heading[heading_key] || current_by_path[section[:path]]
741
- next false unless current_text
742
-
743
- introduced_tokens = markdown_significant_tokens(other_text) - markdown_significant_tokens(ancestor_text)
744
- !introduced_tokens.empty? && introduced_tokens.subset?(markdown_significant_tokens(current_text))
745
- end
746
- end
747
-
748
- def markdown_sections(source)
749
- parsed = Markly::Merge.parse_markdown(source, 'markdown')
750
- return nil unless parsed[:ok]
751
-
752
- Markdown::Merge.collect_markdown_sections(
753
- parsed.dig(:analysis, :normalized_source),
754
- parsed.dig(:analysis, :owners)
729
+ def merge_with_provider(path_name, provider_options, merge_options)
730
+ merge3_result(
731
+ Ast::Merge::Git.merge3(
732
+ **merge_options,
733
+ path_name: path_name,
734
+ profile_id: provider_options.fetch(:profile_id, 'source_preserving'),
735
+ **provider_options.except(:profile_id)
736
+ )
755
737
  )
756
738
  end
757
739
 
758
- def sections_by_path(sections)
759
- sections.to_h { |section| [section[:path], section[:text]] }
760
- end
761
-
762
- def sections_by_heading(sections)
763
- sections.each_with_object({}) do |section, map|
764
- key = markdown_heading_key(section[:text])
765
- map[key] ||= section[:text] unless key.empty?
766
- end
767
- end
768
-
769
- def markdown_heading_key(section_text)
770
- heading = section_text.each_line.find { |line| line.start_with?('#') }.to_s.strip
771
- heading = heading.delete_prefix('#').strip while heading.start_with?('#')
772
- heading.downcase
773
- end
774
-
775
- def markdown_significant_tokens(source)
776
- source.split.each_with_object(Set.new) do |raw_token, tokens|
777
- token = normalize_markdown_token(raw_token)
778
- tokens << token if token.length >= 8
779
- end
780
- end
781
-
782
- def normalize_markdown_token(raw_token)
783
- token = raw_token.downcase
784
- loop do
785
- previous = token
786
- token = token.delete_prefix('(').delete_prefix('[').delete_prefix('{').delete_prefix('<')
787
- token = token.delete_suffix(')').delete_suffix(']').delete_suffix('}').delete_suffix('>')
788
- token = token.delete_suffix('.').delete_suffix(',').delete_suffix(';').delete_suffix(':')
789
- return token if token == previous
790
- end
791
- end
792
-
793
740
  def unsupported_language_result(language, path_name)
794
741
  {
795
742
  ok: false,
@@ -805,63 +752,69 @@ module Smorg
805
752
  end
806
753
 
807
754
  def merge3_result(result)
808
- merge3_report_fields = {
809
- change_classifications: result.fetch(:change_classifications, []),
810
- reparse_after_render: result[:reparse_after_render],
811
- formatting_preservation: result[:formatting_preservation],
812
- secondary_formatting_metrics: result[:secondary_formatting_metrics],
813
- default_driver_evaluation: result[:default_driver_evaluation]
814
- }
815
755
  if result[:ok] && result[:merged_source]
816
- {
817
- ok: true,
818
- diagnostics: result.fetch(:diagnostics),
819
- output: result.fetch(:merged_source),
820
- owned_regions: result.fetch(:owned_regions, []),
821
- render_report: result.fetch(:render_report),
822
- profile: result.fetch(:profile),
823
- policies: [],
824
- **merge3_report_fields
825
- }
756
+ return merge3_result_payload(result, true, result.fetch(:merged_source))
826
757
  elsif !result[:ok] && result[:conflicted_source]
827
- {
828
- ok: false,
829
- diagnostics: result.fetch(:diagnostics),
830
- output: result.fetch(:conflicted_source),
831
- owned_regions: result.fetch(:owned_regions, []),
832
- render_report: result.fetch(:render_report),
833
- profile: result.fetch(:profile),
834
- policies: [],
835
- **merge3_report_fields
836
- }
837
- else
838
- {
839
- ok: false,
840
- diagnostics: result.fetch(:diagnostics),
841
- owned_regions: result.fetch(:owned_regions, []),
842
- render_report: result.fetch(:render_report),
843
- profile: result.fetch(:profile),
844
- policies: [],
845
- **merge3_report_fields
846
- }
758
+ return merge3_result_payload(result, false, result.fetch(:conflicted_source))
847
759
  end
760
+
761
+ merge3_result_payload(result, false, nil)
762
+ end
763
+
764
+ def merge3_result_payload(result, success, output)
765
+ payload = {
766
+ ok: success,
767
+ diagnostics: result.fetch(:diagnostics),
768
+ owned_regions: result.fetch(:owned_regions, []),
769
+ render_report: result.fetch(:render_report),
770
+ profile: result.fetch(:profile),
771
+ policies: [],
772
+ **merge3_report_fields(result)
773
+ }
774
+ output ? payload.merge(output: output) : payload
775
+ end
776
+
777
+ def merge3_report_fields(result)
778
+ {
779
+ change_classifications: result.fetch(:change_classifications, []),
780
+ conflicts: result.fetch(:conflicts, []),
781
+ fallbacks: result.fetch(:fallbacks, []),
782
+ provider: result.fetch(:provider, {}),
783
+ verification: result.fetch(:verification, {})
784
+ }
848
785
  end
849
786
 
850
787
  def normalize_language(language, path_name)
851
- case language.to_s.strip.downcase
852
- when 'go', 'golang'
853
- 'go'
854
- when 'json'
855
- 'json'
856
- when 'jsonc', 'json with comments'
857
- 'jsonc'
858
- when 'json5'
859
- 'json5'
860
- when 'markdown', 'md', 'gfm', 'text/markdown'
861
- 'markdown'
862
- when 'plain', 'text', 'plaintext', 'text/plain'
863
- 'text'
864
- else
788
+ normalized_language = language.to_s.strip.downcase
789
+ return language_from_path(path_name) if normalized_language.empty?
790
+
791
+ LANGUAGE_ALIASES.fetch(normalized_language) do
792
+ Ast::Merge.classify_template_target_path(path_name)[:family]
793
+ end
794
+ end
795
+
796
+ LANGUAGE_BY_EXTENSION = {
797
+ '.bash' => 'bash', '.sh' => 'bash', '.go' => 'go', '.htm' => 'html', '.html' => 'html',
798
+ '.rs' => 'rust', '.ts' => 'typescript', '.tsx' => 'tsx'
799
+ }.freeze
800
+
801
+ LANGUAGE_ALIASES = {
802
+ 'bash' => 'bash', 'sh' => 'bash', 'application/x-sh' => 'bash', 'text/x-shellscript' => 'bash',
803
+ 'go' => 'go', 'golang' => 'go', 'html' => 'html', 'htm' => 'html', 'text/html' => 'html', 'html5' => 'html',
804
+ 'dotenv' => 'dotenv', 'env' => 'dotenv', 'config-env' => 'dotenv', 'json' => 'json', 'jsonc' => 'jsonc',
805
+ 'json with comments' => 'jsonc', 'json5' => 'json5', 'markdown' => 'markdown', 'md' => 'markdown',
806
+ 'gfm' => 'markdown', 'text/markdown' => 'markdown', 'ruby' => 'ruby', 'rb' => 'ruby',
807
+ 'application/x-ruby' => 'ruby', 'rbs' => 'rbs', 'rust' => 'rust', 'rs' => 'rust',
808
+ 'application/rust' => 'rust', 'text/rust' => 'rust', 'toml' => 'toml', 'application/toml' => 'toml',
809
+ 'typescript' => 'typescript', 'ts' => 'typescript', 'application/typescript' => 'typescript',
810
+ 'text/typescript' => 'typescript', 'tsx' => 'tsx', 'typescriptreact' => 'tsx', 'yaml' => 'yaml',
811
+ 'yml' => 'yaml', 'application/yaml' => 'yaml', 'text/yaml' => 'yaml', 'plain' => 'text', 'text' => 'text',
812
+ 'plaintext' => 'text', 'text/plain' => 'text'
813
+ }.freeze
814
+
815
+ def language_from_path(path_name)
816
+ extension = File.extname(path_name.to_s).downcase
817
+ LANGUAGE_BY_EXTENSION.fetch(extension) do
865
818
  Ast::Merge.classify_template_target_path(path_name)[:family]
866
819
  end
867
820
  end
@@ -877,50 +830,63 @@ module Smorg
877
830
  end
878
831
 
879
832
  def attribute_files_for_path(path_name)
880
- clean_path = if File.expand_path(path_name,
881
- Dir.pwd).start_with?(Dir.pwd)
882
- Pathname.new(path_name).cleanpath.to_s
883
- else
884
- path_name
885
- end
833
+ clean_path = normalized_attribute_path(path_name)
834
+ return ['.gitattributes'] if root_attribute_path?(clean_path)
835
+
836
+ ['.gitattributes', *attribute_directory_paths(File.dirname(clean_path))]
837
+ end
838
+
839
+ def normalized_attribute_path(path_name)
840
+ expanded_path = File.expand_path(path_name, Dir.pwd)
841
+ return Pathname.new(path_name).cleanpath.to_s if expanded_path.start_with?(Dir.pwd)
842
+
843
+ path_name
844
+ end
845
+
846
+ def root_attribute_path?(clean_path)
886
847
  dir = File.dirname(clean_path)
887
- return ['.gitattributes'] if dir == '.' || clean_path.start_with?('..') || Pathname.new(clean_path).absolute?
848
+ dir == '.' || clean_path.start_with?('..') || Pathname.new(clean_path).absolute?
849
+ end
888
850
 
889
- files = ['.gitattributes']
851
+ def attribute_directory_paths(dir)
890
852
  parts = dir.split(File::SEPARATOR).reject(&:empty?)
891
- parts.each_index do |index|
892
- files << File.join(*parts[0..index], '.gitattributes')
853
+ parts.each_index.map do |index|
854
+ File.join(*parts[0..index], '.gitattributes')
893
855
  end
894
- files
895
856
  end
896
857
 
897
858
  def apply_attributes(settings, path_name, source)
898
859
  source.each_line do |raw_line|
899
- line = raw_line.strip
900
- next if line.empty? || line.start_with?('#')
901
-
902
- pattern, *fields = line.split(/\s+/)
903
- next if fields.empty? || !attribute_pattern_matches?(pattern, path_name)
904
-
905
- fields.each do |field|
906
- key, value = field.split('=', 2)
907
- next unless value
908
-
909
- case key
910
- when 'smorg.language', 'linguist-language'
911
- settings[:language] = value
912
- when 'smorg.profile'
913
- settings[:profile_id] = value
914
- when 'smorg.requireProfileStatus'
915
- settings[:require_profile_status] = value
916
- when 'conflict-marker-size'
917
- marker_size = value.to_i
918
- settings[:conflict_marker_size] = marker_size if marker_size.positive?
919
- end
920
- end
860
+ attribute_fields(raw_line, path_name).each { |field| apply_attribute_field(settings, field) }
921
861
  end
922
862
  end
923
863
 
864
+ def attribute_fields(raw_line, path_name)
865
+ line = raw_line.strip
866
+ return [] if line.empty? || line.start_with?('#')
867
+
868
+ pattern, *fields = line.split(/\s+/)
869
+ return [] if fields.empty? || !attribute_pattern_matches?(pattern, path_name)
870
+
871
+ fields
872
+ end
873
+
874
+ ATTRIBUTE_KEYS = {
875
+ 'smorg.language' => :language,
876
+ 'linguist-language' => :language,
877
+ 'smorg.profile' => :profile_id,
878
+ 'smorg.requireProfileStatus' => :require_profile_status
879
+ }.freeze
880
+
881
+ def apply_attribute_field(settings, field)
882
+ key, value = field.split('=', 2)
883
+ return unless value
884
+ return settings[ATTRIBUTE_KEYS[key]] = value if ATTRIBUTE_KEYS.key?(key)
885
+
886
+ marker_size = value.to_i
887
+ settings[:conflict_marker_size] = marker_size if key == 'conflict-marker-size' && marker_size.positive?
888
+ end
889
+
924
890
  def attribute_pattern_matches?(pattern, path_name)
925
891
  return true if pattern == path_name
926
892
 
@@ -932,26 +898,33 @@ module Smorg
932
898
  end
933
899
 
934
900
  def find_conflict_regions(source, marker_size)
935
- marker_size = [marker_size.to_i, 1].max
936
- start_prefix = '<' * marker_size
937
- separator_prefix = '=' * marker_size
938
- end_prefix = '>' * marker_size
901
+ prefixes = conflict_marker_prefixes(marker_size)
939
902
  regions = []
940
903
  current = nil
941
904
  source.split("\n").each_with_index do |line, index|
942
- line_number = index + 1
943
- if line.start_with?(start_prefix)
944
- current = { start_line: line_number, separator_line: 0 }
945
- elsif current && current[:separator_line].zero? && line.start_with?(separator_prefix)
946
- current[:separator_line] = line_number
947
- elsif current && line.start_with?(end_prefix)
948
- regions << current.merge(end_line: line_number)
949
- current = nil
950
- end
905
+ current, completed = conflict_region_state(current, line, index + 1, prefixes)
906
+ regions << completed if completed
951
907
  end
952
908
  regions
953
909
  end
954
910
 
911
+ def conflict_marker_prefixes(marker_size)
912
+ size = [marker_size.to_i, 1].max
913
+ { start: '<' * size, separator: '=' * size, end: '>' * size }
914
+ end
915
+
916
+ def conflict_region_state(current, line, line_number, prefixes)
917
+ if line.start_with?(prefixes[:start])
918
+ [{ start_line: line_number, separator_line: 0 }, nil]
919
+ elsif current && current[:separator_line].zero? && line.start_with?(prefixes[:separator])
920
+ [current.merge(separator_line: line_number), nil]
921
+ elsif current && line.start_with?(prefixes[:end])
922
+ [nil, current.merge(end_line: line_number)]
923
+ else
924
+ [current, nil]
925
+ end
926
+ end
927
+
955
928
  def print_conflict_diff(stdout, path_name, regions)
956
929
  stdout.puts("conflicts #{path_name}")
957
930
  stdout.puts("count #{regions.length}")
@@ -971,5 +944,7 @@ module Smorg
971
944
  stderr.puts("#{diagnostic[:category]}: #{diagnostic[:message]}")
972
945
  end
973
946
  end
974
- end
947
+ end # rubocop:enable Metrics/ModuleLength
975
948
  end
949
+
950
+ require 'rust/merge'
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smorg-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.1.1
4
+ version: 7.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter H. Boling
@@ -43,28 +43,42 @@ dependencies:
43
43
  requirements:
44
44
  - - '='
45
45
  - !ruby/object:Gem::Version
46
- version: 7.1.1
46
+ version: 7.1.4
47
47
  type: :runtime
48
48
  prerelease: false
49
49
  version_requirements: !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - '='
52
52
  - !ruby/object:Gem::Version
53
- version: 7.1.1
53
+ version: 7.1.4
54
54
  - !ruby/object:Gem::Dependency
55
55
  name: ast-merge-git
56
56
  requirement: !ruby/object:Gem::Requirement
57
57
  requirements:
58
58
  - - '='
59
59
  - !ruby/object:Gem::Version
60
- version: 7.1.1
60
+ version: 7.1.4
61
61
  type: :runtime
62
62
  prerelease: false
63
63
  version_requirements: !ruby/object:Gem::Requirement
64
64
  requirements:
65
65
  - - '='
66
66
  - !ruby/object:Gem::Version
67
- version: 7.1.1
67
+ version: 7.1.4
68
+ - !ruby/object:Gem::Dependency
69
+ name: bash-merge
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - '='
73
+ - !ruby/object:Gem::Version
74
+ version: 7.1.4
75
+ type: :runtime
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - '='
80
+ - !ruby/object:Gem::Version
81
+ version: 7.1.4
68
82
  - !ruby/object:Gem::Dependency
69
83
  name: diff-lcs
70
84
  requirement: !ruby/object:Gem::Requirement
@@ -79,76 +93,194 @@ dependencies:
79
93
  - - "~>"
80
94
  - !ruby/object:Gem::Version
81
95
  version: '1.5'
96
+ - !ruby/object:Gem::Dependency
97
+ name: dotenv-merge
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - '='
101
+ - !ruby/object:Gem::Version
102
+ version: 7.1.4
103
+ type: :runtime
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - '='
108
+ - !ruby/object:Gem::Version
109
+ version: 7.1.4
82
110
  - !ruby/object:Gem::Dependency
83
111
  name: go-merge
84
112
  requirement: !ruby/object:Gem::Requirement
85
113
  requirements:
86
114
  - - '='
87
115
  - !ruby/object:Gem::Version
88
- version: 7.1.1
116
+ version: 7.1.4
89
117
  type: :runtime
90
118
  prerelease: false
91
119
  version_requirements: !ruby/object:Gem::Requirement
92
120
  requirements:
93
121
  - - '='
94
122
  - !ruby/object:Gem::Version
95
- version: 7.1.1
123
+ version: 7.1.4
124
+ - !ruby/object:Gem::Dependency
125
+ name: html-merge
126
+ requirement: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - '='
129
+ - !ruby/object:Gem::Version
130
+ version: 7.1.4
131
+ type: :runtime
132
+ prerelease: false
133
+ version_requirements: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - '='
136
+ - !ruby/object:Gem::Version
137
+ version: 7.1.4
96
138
  - !ruby/object:Gem::Dependency
97
139
  name: json-merge
98
140
  requirement: !ruby/object:Gem::Requirement
99
141
  requirements:
100
142
  - - '='
101
143
  - !ruby/object:Gem::Version
102
- version: 7.1.1
144
+ version: 7.1.4
103
145
  type: :runtime
104
146
  prerelease: false
105
147
  version_requirements: !ruby/object:Gem::Requirement
106
148
  requirements:
107
149
  - - '='
108
150
  - !ruby/object:Gem::Version
109
- version: 7.1.1
151
+ version: 7.1.4
110
152
  - !ruby/object:Gem::Dependency
111
153
  name: kettle-jem
154
+ requirement: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - "~>"
157
+ - !ruby/object:Gem::Version
158
+ version: '7.1'
159
+ - - ">="
160
+ - !ruby/object:Gem::Version
161
+ version: 7.1.5
162
+ type: :runtime
163
+ prerelease: false
164
+ version_requirements: !ruby/object:Gem::Requirement
165
+ requirements:
166
+ - - "~>"
167
+ - !ruby/object:Gem::Version
168
+ version: '7.1'
169
+ - - ">="
170
+ - !ruby/object:Gem::Version
171
+ version: 7.1.5
172
+ - !ruby/object:Gem::Dependency
173
+ name: markdown-merge
112
174
  requirement: !ruby/object:Gem::Requirement
113
175
  requirements:
114
176
  - - '='
115
177
  - !ruby/object:Gem::Version
116
- version: 7.1.1
178
+ version: 7.1.4
117
179
  type: :runtime
118
180
  prerelease: false
119
181
  version_requirements: !ruby/object:Gem::Requirement
120
182
  requirements:
121
183
  - - '='
122
184
  - !ruby/object:Gem::Version
123
- version: 7.1.1
185
+ version: 7.1.4
124
186
  - !ruby/object:Gem::Dependency
125
- name: markly-merge
187
+ name: plain-merge
126
188
  requirement: !ruby/object:Gem::Requirement
127
189
  requirements:
128
190
  - - '='
129
191
  - !ruby/object:Gem::Version
130
- version: 7.1.1
192
+ version: 7.1.4
131
193
  type: :runtime
132
194
  prerelease: false
133
195
  version_requirements: !ruby/object:Gem::Requirement
134
196
  requirements:
135
197
  - - '='
136
198
  - !ruby/object:Gem::Version
137
- version: 7.1.1
199
+ version: 7.1.4
138
200
  - !ruby/object:Gem::Dependency
139
- name: plain-merge
201
+ name: prism-merge
202
+ requirement: !ruby/object:Gem::Requirement
203
+ requirements:
204
+ - - '='
205
+ - !ruby/object:Gem::Version
206
+ version: 7.1.4
207
+ type: :runtime
208
+ prerelease: false
209
+ version_requirements: !ruby/object:Gem::Requirement
210
+ requirements:
211
+ - - '='
212
+ - !ruby/object:Gem::Version
213
+ version: 7.1.4
214
+ - !ruby/object:Gem::Dependency
215
+ name: psych-merge
216
+ requirement: !ruby/object:Gem::Requirement
217
+ requirements:
218
+ - - '='
219
+ - !ruby/object:Gem::Version
220
+ version: 7.1.4
221
+ type: :runtime
222
+ prerelease: false
223
+ version_requirements: !ruby/object:Gem::Requirement
224
+ requirements:
225
+ - - '='
226
+ - !ruby/object:Gem::Version
227
+ version: 7.1.4
228
+ - !ruby/object:Gem::Dependency
229
+ name: rbs-merge
230
+ requirement: !ruby/object:Gem::Requirement
231
+ requirements:
232
+ - - '='
233
+ - !ruby/object:Gem::Version
234
+ version: 7.1.4
235
+ type: :runtime
236
+ prerelease: false
237
+ version_requirements: !ruby/object:Gem::Requirement
238
+ requirements:
239
+ - - '='
240
+ - !ruby/object:Gem::Version
241
+ version: 7.1.4
242
+ - !ruby/object:Gem::Dependency
243
+ name: rust-merge
244
+ requirement: !ruby/object:Gem::Requirement
245
+ requirements:
246
+ - - '='
247
+ - !ruby/object:Gem::Version
248
+ version: 7.1.4
249
+ type: :runtime
250
+ prerelease: false
251
+ version_requirements: !ruby/object:Gem::Requirement
252
+ requirements:
253
+ - - '='
254
+ - !ruby/object:Gem::Version
255
+ version: 7.1.4
256
+ - !ruby/object:Gem::Dependency
257
+ name: toml-merge
258
+ requirement: !ruby/object:Gem::Requirement
259
+ requirements:
260
+ - - '='
261
+ - !ruby/object:Gem::Version
262
+ version: 7.1.4
263
+ type: :runtime
264
+ prerelease: false
265
+ version_requirements: !ruby/object:Gem::Requirement
266
+ requirements:
267
+ - - '='
268
+ - !ruby/object:Gem::Version
269
+ version: 7.1.4
270
+ - !ruby/object:Gem::Dependency
271
+ name: typescript-merge
140
272
  requirement: !ruby/object:Gem::Requirement
141
273
  requirements:
142
274
  - - '='
143
275
  - !ruby/object:Gem::Version
144
- version: 7.1.1
276
+ version: 7.1.4
145
277
  type: :runtime
146
278
  prerelease: false
147
279
  version_requirements: !ruby/object:Gem::Requirement
148
280
  requirements:
149
281
  - - '='
150
282
  - !ruby/object:Gem::Version
151
- version: 7.1.1
283
+ version: 7.1.4
152
284
  - !ruby/object:Gem::Dependency
153
285
  name: version_gem
154
286
  requirement: !ruby/object:Gem::Requirement
@@ -175,20 +307,20 @@ dependencies:
175
307
  requirements:
176
308
  - - "~>"
177
309
  - !ruby/object:Gem::Version
178
- version: '2.5'
310
+ version: '3.0'
179
311
  - - ">="
180
312
  - !ruby/object:Gem::Version
181
- version: 2.5.17
313
+ version: 3.0.7
182
314
  type: :development
183
315
  prerelease: false
184
316
  version_requirements: !ruby/object:Gem::Requirement
185
317
  requirements:
186
318
  - - "~>"
187
319
  - !ruby/object:Gem::Version
188
- version: '2.5'
320
+ version: '3.0'
189
321
  - - ">="
190
322
  - !ruby/object:Gem::Version
191
- version: 2.5.17
323
+ version: 3.0.7
192
324
  - !ruby/object:Gem::Dependency
193
325
  name: bundler-audit
194
326
  requirement: !ruby/object:Gem::Requirement
@@ -266,7 +398,7 @@ dependencies:
266
398
  version: '3.2'
267
399
  - - ">="
268
400
  - !ruby/object:Gem::Version
269
- version: 3.2.0
401
+ version: 3.2.2
270
402
  type: :development
271
403
  prerelease: false
272
404
  version_requirements: !ruby/object:Gem::Requirement
@@ -276,7 +408,7 @@ dependencies:
276
408
  version: '3.2'
277
409
  - - ">="
278
410
  - !ruby/object:Gem::Version
279
- version: 3.2.0
411
+ version: 3.2.2
280
412
  - !ruby/object:Gem::Dependency
281
413
  name: kettle-test
282
414
  requirement: !ruby/object:Gem::Requirement
@@ -286,7 +418,7 @@ dependencies:
286
418
  version: '2.0'
287
419
  - - ">="
288
420
  - !ruby/object:Gem::Version
289
- version: 2.0.19
421
+ version: 2.0.20
290
422
  type: :development
291
423
  prerelease: false
292
424
  version_requirements: !ruby/object:Gem::Requirement
@@ -296,7 +428,7 @@ dependencies:
296
428
  version: '2.0'
297
429
  - - ">="
298
430
  - !ruby/object:Gem::Version
299
- version: 2.0.19
431
+ version: 2.0.20
300
432
  - !ruby/object:Gem::Dependency
301
433
  name: turbo_tests2
302
434
  requirement: !ruby/object:Gem::Requirement
@@ -306,7 +438,7 @@ dependencies:
306
438
  version: '3.2'
307
439
  - - ">="
308
440
  - !ruby/object:Gem::Version
309
- version: 3.2.4
441
+ version: 3.2.6
310
442
  type: :development
311
443
  prerelease: false
312
444
  version_requirements: !ruby/object:Gem::Requirement
@@ -316,7 +448,7 @@ dependencies:
316
448
  version: '3.2'
317
449
  - - ">="
318
450
  - !ruby/object:Gem::Version
319
- version: 3.2.4
451
+ version: 3.2.6
320
452
  - !ruby/object:Gem::Dependency
321
453
  name: ruby-progressbar
322
454
  requirement: !ruby/object:Gem::Requirement
@@ -360,7 +492,7 @@ dependencies:
360
492
  version: '2.0'
361
493
  - - ">="
362
494
  - !ruby/object:Gem::Version
363
- version: 2.0.11
495
+ version: 2.0.12
364
496
  type: :development
365
497
  prerelease: false
366
498
  version_requirements: !ruby/object:Gem::Requirement
@@ -370,7 +502,7 @@ dependencies:
370
502
  version: '2.0'
371
503
  - - ">="
372
504
  - !ruby/object:Gem::Version
373
- version: 2.0.11
505
+ version: 2.0.12
374
506
  description: "\U0001F48E Git-compatible StructuredMerge command surface for the Ruby
375
507
  implementation."
376
508
  email:
@@ -393,10 +525,10 @@ licenses:
393
525
  - PolyForm-Small-Business-1.0.0
394
526
  metadata:
395
527
  homepage_uri: https://structuredmerge.org
396
- source_code_uri: https://github.com/structuredmerge/structuredmerge-ruby/tree/v7.1.1
397
- changelog_uri: https://github.com/structuredmerge/structuredmerge-ruby/blob/v7.1.1/CHANGELOG.md
528
+ source_code_uri: https://github.com/structuredmerge/structuredmerge-ruby/tree/v7.1.4
529
+ changelog_uri: https://github.com/structuredmerge/structuredmerge-ruby/blob/v7.1.4/CHANGELOG.md
398
530
  bug_tracker_uri: https://github.com/structuredmerge/structuredmerge-ruby/issues
399
- documentation_uri: https://www.rubydoc.info/gems/smorg-rb/7.1.1
531
+ documentation_uri: https://www.rubydoc.info/gems/smorg-rb/7.1.4
400
532
  funding_uri: https://github.com/sponsors/pboling
401
533
  wiki_uri: https://github.com/structuredmerge/structuredmerge-ruby/wiki
402
534
  news_uri: https://www.railsbling.com/tags/smorg-rb
metadata.gz.sig CHANGED
Binary file