From 2c4886d220b38c5ff5a2cc8f0e0a64a0d5be9375 Mon Sep 17 00:00:00 2001 From: SeradedStripes Date: Sat, 4 Jul 2026 19:42:03 +0200 Subject: [PATCH 1/5] Map .asm to Assembly language --- app/lib/language_utils.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/language_utils.rb b/app/lib/language_utils.rb index 46a0941a7..93e207ffc 100644 --- a/app/lib/language_utils.rb +++ b/app/lib/language_utils.rb @@ -23,7 +23,7 @@ def self.build_lookup(key, include_name_as_key: false) private_class_method :build_lookup def self.alias_map = @alias_map ||= build_lookup("aliases", include_name_as_key: true) - def self.extension_map = @extension_map ||= build_lookup("extensions") + def self.extension_map = @extension_map ||= build_lookup("extensions").merge(".asm" => "Assembly") def self.filename_map = @filename_map ||= build_lookup("filenames") # Resolve a raw language string to its canonical name. From 51f0b59d3b89112d9286f3895ad2548446767058 Mon Sep 17 00:00:00 2001 From: SeradedStripes Date: Sat, 4 Jul 2026 20:03:45 +0200 Subject: [PATCH 2/5] Add a test --- test/lib/language_utils_test.rb | 84 +++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 test/lib/language_utils_test.rb diff --git a/test/lib/language_utils_test.rb b/test/lib/language_utils_test.rb new file mode 100644 index 000000000..367edcf80 --- /dev/null +++ b/test/lib/language_utils_test.rb @@ -0,0 +1,84 @@ +require "test_helper" + +class LanguageUtilsTest < Minitest::Test + def setup + ActiveRecord::FixtureSet.reset_cache + end + + def test_detect_asm_extension_returns_assembly + assert_equal "Assembly", LanguageUtils.detect_from_extension("test.asm") + end + + def test_detect_known_extension + assert_equal "Ruby", LanguageUtils.detect_from_extension("test.rb") + assert_equal "Python", LanguageUtils.detect_from_extension("test.py") + assert_equal "JavaScript", LanguageUtils.detect_from_extension("test.js") + end + + def test_detect_motorola_68k_specific_extensions + assert_equal "Motorola 68K Assembly", LanguageUtils.detect_from_extension("test.x68") + end + + def test_detect_unknown_extension_returns_nil + assert_nil LanguageUtils.detect_from_extension("test.xyzzy") + end + + def test_blank_or_unknown_returns_true_for_blank + assert LanguageUtils.blank_or_unknown?(nil) + assert LanguageUtils.blank_or_unknown?("") + assert LanguageUtils.blank_or_unknown?("Unknown") + assert LanguageUtils.blank_or_unknown?("unknown") + end + + def test_blank_or_unknown_returns_false_for_known + refute LanguageUtils.blank_or_unknown?("Assembly") + refute LanguageUtils.blank_or_unknown?("Ruby") + end + + def test_find_name_resolves_aliases + assert_equal "Assembly", LanguageUtils.find_name("asm") + assert_equal "Assembly", LanguageUtils.find_name("nasm") + assert_equal "Motorola 68K Assembly", LanguageUtils.find_name("m68k") + end + + def test_find_name_returns_nil_for_unknown + assert_nil LanguageUtils.find_name("NotARealLanguage") + end + + def test_display_name_returns_canonical + assert_equal "Assembly", LanguageUtils.display_name("asm") + assert_equal "Assembly", LanguageUtils.display_name("Assembly") + end + + def test_display_name_returns_unknown_for_nil + assert_equal "Unknown", LanguageUtils.display_name(nil) + assert_equal "Unknown", LanguageUtils.display_name("") + end + + def test_fill_missing_language_returns_raw_when_present + assert_equal "Assembly", LanguageUtils.fill_missing_language("Assembly", entity: "test.asm") + end + + def test_fill_missing_language_detects_from_extension_when_blank + assert_equal "Assembly", LanguageUtils.fill_missing_language(nil, entity: "test.asm") + assert_equal "Assembly", LanguageUtils.fill_missing_language("", entity: "test.asm") + assert_equal "Assembly", LanguageUtils.fill_missing_language("Unknown", entity: "test.asm") + end + + def test_color_returns_hex_for_known_language + assert_equal "#6E4C13", LanguageUtils.color("Assembly") + assert_equal "#701516", LanguageUtils.color("Ruby") + end + + def test_color_returns_default_for_unknown + assert_equal LanguageUtils::DEFAULT_COLOR, LanguageUtils.color("NotARealLanguage") + end + + def test_extension_map_overrides_asm_to_assembly + assert_equal "Assembly", LanguageUtils.extension_map[".asm"] + end + + def test_extension_map_still_has_motorola_specific_extensions + assert_equal "Motorola 68K Assembly", LanguageUtils.extension_map[".x68"] + end +end From 8310066677d8888ec2bf7869d093984f1e3b40a0 Mon Sep 17 00:00:00 2001 From: SeradedStripes Date: Sat, 4 Jul 2026 20:07:26 +0200 Subject: [PATCH 3/5] Revert ruby code change & override the mapping in languages_custom.yml --- app/lib/language_utils.rb | 2 +- config/languages_custom.yml | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/lib/language_utils.rb b/app/lib/language_utils.rb index 93e207ffc..46a0941a7 100644 --- a/app/lib/language_utils.rb +++ b/app/lib/language_utils.rb @@ -23,7 +23,7 @@ def self.build_lookup(key, include_name_as_key: false) private_class_method :build_lookup def self.alias_map = @alias_map ||= build_lookup("aliases", include_name_as_key: true) - def self.extension_map = @extension_map ||= build_lookup("extensions").merge(".asm" => "Assembly") + def self.extension_map = @extension_map ||= build_lookup("extensions") def self.filename_map = @filename_map ||= build_lookup("filenames") # Resolve a raw language string to its canonical name. diff --git a/config/languages_custom.yml b/config/languages_custom.yml index e7de2d090..cf3b6922a 100644 --- a/config/languages_custom.yml +++ b/config/languages_custom.yml @@ -11,3 +11,10 @@ Lapse: AsciiDoc: extensions: - ".ad" + +Motorola 68K Assembly: + extensions: + - ".i" + - ".inc" + - ".s" + - ".x68" From 0ec3b65bca2351221cba64bf3fc309d5d7b75702 Mon Sep 17 00:00:00 2001 From: SeradedStripes Date: Sat, 4 Jul 2026 20:23:46 +0200 Subject: [PATCH 4/5] Refactor language detection logic to always detect extension for .asm entities and enhance tests for Assembly handling --- app/lib/language_utils.rb | 8 +++++++- app/services/heartbeat_ingest.rb | 5 +---- test/lib/language_utils_test.rb | 12 ++++++++++++ 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/app/lib/language_utils.rb b/app/lib/language_utils.rb index 46a0941a7..ddc1ddbef 100644 --- a/app/lib/language_utils.rb +++ b/app/lib/language_utils.rb @@ -43,7 +43,13 @@ def self.detect_from_extension(entity) end def self.detect_from_entity(entity) = detect_from_filename(entity) || detect_from_extension(entity) - def self.fill_missing_language(raw, entity:) = blank_or_unknown?(raw) ? detect_from_entity(entity) : raw + def self.fill_missing_language(raw, entity:) + if blank_or_unknown?(raw) || File.extname(entity.to_s).downcase == ".asm" + detect_from_entity(entity) || raw + else + raw + end + end # Canonical display name: "js" → "JavaScript", "cpp" → "C++" def self.display_name(raw) = raw.blank? ? "Unknown" : (find_name(raw) || raw) diff --git a/app/services/heartbeat_ingest.rb b/app/services/heartbeat_ingest.rb index c76cd0b39..d94f479a3 100644 --- a/app/services/heartbeat_ingest.rb +++ b/app/services/heartbeat_ingest.rb @@ -64,10 +64,7 @@ def normalize_direct_heartbeat(heartbeat, last_language:) .where.not(language: [ nil, "", LAST_LANGUAGE_SENTINEL ]).order(time: :desc).pick(:language) end - if attrs[:language].blank? || attrs[:language] == "Unknown" - inferred = LanguageUtils.detect_from_extension(attrs[:entity]) - attrs[:language] = inferred if inferred - end + attrs[:language] = LanguageUtils.fill_missing_language(attrs[:language], entity: attrs[:entity]) attrs[:user_agent] ||= attrs.delete(:plugin) parsed_ua = WakatimeService.parse_user_agent(attrs[:user_agent]) diff --git a/test/lib/language_utils_test.rb b/test/lib/language_utils_test.rb index 367edcf80..c05992809 100644 --- a/test/lib/language_utils_test.rb +++ b/test/lib/language_utils_test.rb @@ -65,6 +65,18 @@ def test_fill_missing_language_detects_from_extension_when_blank assert_equal "Assembly", LanguageUtils.fill_missing_language("Unknown", entity: "test.asm") end + def test_fill_missing_language_overrides_asm_variants + assert_equal "Assembly", LanguageUtils.fill_missing_language("RGBDS Assembly", entity: "test.asm") + assert_equal "Assembly", LanguageUtils.fill_missing_language("TASM", entity: "test.asm") + assert_equal "Assembly", LanguageUtils.fill_missing_language("ca65 assembler", entity: "test.asm") + assert_equal "Assembly", LanguageUtils.fill_missing_language("Assembly", entity: "test.asm") + end + + def test_fill_missing_language_does_not_override_non_asm + assert_equal "Python", LanguageUtils.fill_missing_language("Python", entity: "test.py") + assert_equal "RGBDS Assembly", LanguageUtils.fill_missing_language("RGBDS Assembly", entity: "test.py") + end + def test_color_returns_hex_for_known_language assert_equal "#6E4C13", LanguageUtils.color("Assembly") assert_equal "#701516", LanguageUtils.color("Ruby") From 58397cfcfb23ba8cf2f081bb6118d44cda7d2c56 Mon Sep 17 00:00:00 2001 From: SeradedStripes Date: Sat, 11 Jul 2026 21:18:45 +0200 Subject: [PATCH 5/5] Commit to rerun tests as they are flakey