diff --git a/.eslintrc.js b/.eslintrc.js index 24961cdd9..91dcd8e60 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -81,6 +81,15 @@ module.exports = { { property: 'substring', message: 'Use .slice instead of .substring.' }, { property: 'substr', message: 'Use .slice instead of .substr.' }, ], + 'no-restricted-syntax': [ + 'error', + { + // eslint-disable-next-line no-restricted-syntax + selector: 'Literal[value=/•/], JSXText[value=/•/]', + // eslint-disable-next-line no-restricted-syntax + message: "Use '·' (middle dot) instead of '•' (bullet)", + }, + ], 'no-self-assign': 'off', 'no-unused-expressions': 'error', 'no-unused-vars': 'off', diff --git a/.haml-lint.yml b/.haml-lint.yml index 12ca46342..d1ed30b26 100644 --- a/.haml-lint.yml +++ b/.haml-lint.yml @@ -4,6 +4,11 @@ exclude: - 'vendor/**/*' - lib/templates/haml/scaffold/_form.html.haml +require: + - ./lib/linter/haml_middle_dot.rb + linters: AltText: enabled: true + MiddleDot: + enabled: true diff --git a/.rubocop.yml b/.rubocop.yml index bd561df1d..eff89bdae 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -11,6 +11,7 @@ require: - rubocop-rspec - rubocop-performance - rubocop-capybara + - ./lib/linter/rubocop_middle_dot AllCops: TargetRubyVersion: 3.0 # Set to minimum supported version of CI @@ -205,3 +206,6 @@ Style/TrailingCommaInArrayLiteral: # https://docs.rubocop.org/rubocop/cops_style.html#styletrailingcommainhashliteral Style/TrailingCommaInHashLiteral: EnforcedStyleForMultiline: 'comma' + +Style/MiddleDot: + Enabled: true diff --git a/app/views/admin/email_domain_blocks/_email_domain_block.html.haml b/app/views/admin/email_domain_blocks/_email_domain_block.html.haml index c5a55bc27..7cb973c4b 100644 --- a/app/views/admin/email_domain_blocks/_email_domain_block.html.haml +++ b/app/views/admin/email_domain_blocks/_email_domain_block.html.haml @@ -9,6 +9,6 @@ - if email_domain_block.parent.present? = t('admin.email_domain_blocks.resolved_through_html', domain: content_tag(:samp, email_domain_block.parent.domain)) - • + · = t('admin.email_domain_blocks.attempts_over_week', count: email_domain_block.history.reduce(0) { |sum, day| sum + day.accounts }) diff --git a/app/views/admin/export_domain_blocks/_domain_block.html.haml b/app/views/admin/export_domain_blocks/_domain_block.html.haml index 5d4b6c4d0..cdce4fd28 100644 --- a/app/views/admin/export_domain_blocks/_domain_block.html.haml +++ b/app/views/admin/export_domain_blocks/_domain_block.html.haml @@ -17,11 +17,11 @@ %br/ - = f.object.policies.map { |policy| t(policy, scope: 'admin.instances.content_policies.policies') }.join(' • ') + = f.object.policies.map { |policy| t(policy, scope: 'admin.instances.content_policies.policies') }.join(' · ') - if f.object.public_comment.present? - • + · = f.object.public_comment - if existing_relationships - • + · = fa_icon 'warning fw' = t('admin.export_domain_blocks.import.existing_relationships_warning') diff --git a/app/views/admin/instances/_instance.html.haml b/app/views/admin/instances/_instance.html.haml index 93f9bd418..65cf789ce 100644 --- a/app/views/admin/instances/_instance.html.haml +++ b/app/views/admin/instances/_instance.html.haml @@ -6,7 +6,7 @@ %small - if instance.domain_block - = instance.domain_block.policies.map { |policy| t(policy, scope: 'admin.instances.content_policies.policies') }.join(' • ') + = instance.domain_block.policies.map { |policy| t(policy, scope: 'admin.instances.content_policies.policies') }.join(' · ') - elsif instance.domain_allow = t('admin.accounts.whitelisted') - else diff --git a/app/views/admin/instances/show.html.haml b/app/views/admin/instances/show.html.haml index ab290912e..6d67d389d 100644 --- a/app/views/admin/instances/show.html.haml +++ b/app/views/admin/instances/show.html.haml @@ -58,7 +58,7 @@ %td= @instance.domain_block.public_comment %tr %th= t('admin.instances.content_policies.policy') - %td= @instance.domain_block.policies.map { |policy| t(policy, scope: 'admin.instances.content_policies.policies') }.join(' • ') + %td= @instance.domain_block.policies.map { |policy| t(policy, scope: 'admin.instances.content_policies.policies') }.join(' · ') = link_to t('admin.domain_blocks.edit'), edit_admin_domain_block_path(@instance.domain_block), class: 'button' = link_to t('admin.domain_blocks.undo'), admin_domain_block_path(@instance.domain_block), class: 'button', data: { confirm: t('admin.accounts.are_you_sure'), method: :delete } diff --git a/app/views/admin/ip_blocks/_ip_block.html.haml b/app/views/admin/ip_blocks/_ip_block.html.haml index b8d3ac0e8..3dc6f8f8e 100644 --- a/app/views/admin/ip_blocks/_ip_block.html.haml +++ b/app/views/admin/ip_blocks/_ip_block.html.haml @@ -5,7 +5,7 @@ .pending-account__header %samp= link_to "#{ip_block.ip}/#{ip_block.ip.prefix}", admin_accounts_path(ip: "#{ip_block.ip}/#{ip_block.ip.prefix}") - if ip_block.comment.present? - • + · = ip_block.comment %br/ = t("simple_form.labels.ip_block.severities.#{ip_block.severity}") diff --git a/app/views/admin/roles/_role.html.haml b/app/views/admin/roles/_role.html.haml index 798d8d8b4..d6c6b62c8 100644 --- a/app/views/admin/roles/_role.html.haml +++ b/app/views/admin/roles/_role.html.haml @@ -24,7 +24,7 @@ = t('admin.roles.everyone_full_description_html') - else = link_to t('admin.roles.assigned_users', count: role.users.count), admin_accounts_path(role_ids: role.id) - • + · %abbr{ title: role.permissions_as_keys.map { |privilege| I18n.t("admin.roles.privileges.#{privilege}") }.join(', ') }= t('admin.roles.permissions_count', count: role.permissions_as_keys.size) %div = table_link_to 'pencil', t('admin.accounts.edit'), edit_admin_role_path(role) if can?(:update, role) diff --git a/app/views/admin/trends/links/_preview_card.html.haml b/app/views/admin/trends/links/_preview_card.html.haml index 8812feb31..1ca348371 100644 --- a/app/views/admin/trends/links/_preview_card.html.haml +++ b/app/views/admin/trends/links/_preview_card.html.haml @@ -10,21 +10,21 @@ - if preview_card.provider_name.present? = preview_card.provider_name - • + · - if preview_card.language.present? = standard_locale_name(preview_card.language) - • + · = t('admin.trends.links.shared_by_over_week', count: preview_card.history.reduce(0) { |sum, day| sum + day.accounts }) - if preview_card.trend.allowed? - • + · %abbr{ title: t('admin.trends.tags.current_score', score: preview_card.trend.score) }= t('admin.trends.tags.trending_rank', rank: preview_card.trend.rank) - if preview_card.decaying? - • + · = t('admin.trends.tags.peaked_on_and_decaying', date: l(preview_card.max_score_at.to_date, format: :short)) - elsif preview_card.requires_review? - • + · = t('admin.trends.pending_review') diff --git a/app/views/admin/trends/statuses/_status.html.haml b/app/views/admin/trends/statuses/_status.html.haml index f35e13d12..98f2e7709 100644 --- a/app/views/admin/trends/statuses/_status.html.haml +++ b/app/views/admin/trends/statuses/_status.html.haml @@ -17,17 +17,17 @@ = t('admin.trends.statuses.shared_by', count: status.reblogs_count + status.favourites_count, friendly_count: friendly_number_to_human(status.reblogs_count + status.favourites_count)) - if status.account.domain.present? - • + · = status.account.domain - if status.language.present? - • + · = standard_locale_name(status.language) - if status.trendable? && !status.account.discoverable? - • + · = t('admin.trends.statuses.not_discoverable') - if status.trend.allowed? - • + · %abbr{ title: t('admin.trends.tags.current_score', score: status.trend.score) }= t('admin.trends.tags.trending_rank', rank: status.trend.rank) - elsif status.requires_review? - • + · = t('admin.trends.pending_review') diff --git a/app/views/admin/trends/tags/_tag.html.haml b/app/views/admin/trends/tags/_tag.html.haml index a30666a08..3bbdd08db 100644 --- a/app/views/admin/trends/tags/_tag.html.haml +++ b/app/views/admin/trends/tags/_tag.html.haml @@ -13,12 +13,12 @@ = t('admin.trends.tags.used_by_over_week', count: tag.history.reduce(0) { |sum, day| sum + day.accounts }) - if tag.trendable? && (rank = Trends.tags.rank(tag.id)) - • + · %abbr{ title: t('admin.trends.tags.current_score', score: Trends.tags.score(tag.id)) }= t('admin.trends.tags.trending_rank', rank: rank + 1) - if tag.decaying? - • + · = t('admin.trends.tags.peaked_on_and_decaying', date: l(tag.max_score_at.to_date, format: :short)) - elsif tag.requires_review? - • + · = t('admin.trends.pending_review') diff --git a/app/views/admin/webhooks/_webhook.html.haml b/app/views/admin/webhooks/_webhook.html.haml index d94a41eb3..6b3e49eba 100644 --- a/app/views/admin/webhooks/_webhook.html.haml +++ b/app/views/admin/webhooks/_webhook.html.haml @@ -10,7 +10,7 @@ - else %span.negative-hint= t('admin.webhooks.disabled') - • + · %abbr{ title: webhook.events.join(', ') }= t('admin.webhooks.enabled_events', count: webhook.events.size) diff --git a/app/views/admin_mailer/_new_trending_links.text.erb b/app/views/admin_mailer/_new_trending_links.text.erb index 602e12793..85f3f8039 100644 --- a/app/views/admin_mailer/_new_trending_links.text.erb +++ b/app/views/admin_mailer/_new_trending_links.text.erb @@ -1,8 +1,8 @@ <%= raw t('admin_mailer.new_trends.new_trending_links.title') %> <% @links.each do |link| %> -- <%= link.title %> • <%= link.url %> - <%= standard_locale_name(link.language) %> • <%= raw t('admin.trends.links.usage_comparison', today: link.history.get(Time.now.utc).accounts, yesterday: link.history.get(Time.now.utc - 1.day).accounts) %> • <%= t('admin.trends.tags.current_score', score: link.trend.score.round(2)) %> +- <%= link.title %> · <%= link.url %> + <%= standard_locale_name(link.language) %> · <%= raw t('admin.trends.links.usage_comparison', today: link.history.get(Time.now.utc).accounts, yesterday: link.history.get(Time.now.utc - 1.day).accounts) %> · <%= t('admin.trends.tags.current_score', score: link.trend.score.round(2)) %> <% end %> <%= raw t('application_mailer.view')%> <%= admin_trends_links_url %> diff --git a/app/views/admin_mailer/_new_trending_statuses.text.erb b/app/views/admin_mailer/_new_trending_statuses.text.erb index 1ed3ae857..eedbfff9d 100644 --- a/app/views/admin_mailer/_new_trending_statuses.text.erb +++ b/app/views/admin_mailer/_new_trending_statuses.text.erb @@ -2,7 +2,7 @@ <% @statuses.each do |status| %> - <%= ActivityPub::TagManager.instance.url_for(status) %> - <%= standard_locale_name(status.language) %> • <%= raw t('admin.trends.tags.current_score', score: status.trend.score.round(2)) %> + <%= standard_locale_name(status.language) %> · <%= raw t('admin.trends.tags.current_score', score: status.trend.score.round(2)) %> <% end %> <%= raw t('application_mailer.view')%> <%= admin_trends_statuses_url %> diff --git a/app/views/admin_mailer/_new_trending_tags.text.erb b/app/views/admin_mailer/_new_trending_tags.text.erb index 363df369d..d528ab8eb 100644 --- a/app/views/admin_mailer/_new_trending_tags.text.erb +++ b/app/views/admin_mailer/_new_trending_tags.text.erb @@ -2,7 +2,7 @@ <% @tags.each do |tag| %> - #<%= tag.display_name %> - <%= raw t('admin.trends.tags.usage_comparison', today: tag.history.get(Time.now.utc).accounts, yesterday: tag.history.get(Time.now.utc - 1.day).accounts) %> • <%= t('admin.trends.tags.current_score', score: Trends.tags.score(tag.id).round(2)) %> + <%= raw t('admin.trends.tags.usage_comparison', today: tag.history.get(Time.now.utc).accounts, yesterday: tag.history.get(Time.now.utc - 1.day).accounts) %> · <%= t('admin.trends.tags.current_score', score: Trends.tags.score(tag.id).round(2)) %> <% end %> <% if @lowest_trending_tag %> diff --git a/app/views/oauth/authorized_applications/index.html.haml b/app/views/oauth/authorized_applications/index.html.haml index 55d8524db..689f05102 100644 --- a/app/views/oauth/authorized_applications/index.html.haml +++ b/app/views/oauth/authorized_applications/index.html.haml @@ -23,7 +23,7 @@ - else = t('doorkeeper.authorized_applications.index.never_used') - • + · = t('doorkeeper.authorized_applications.index.authorized_at', date: l(application.created_at.to_date)) diff --git a/lib/linter/haml_middle_dot.rb b/lib/linter/haml_middle_dot.rb new file mode 100644 index 000000000..3b2771152 --- /dev/null +++ b/lib/linter/haml_middle_dot.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module HamlLint + # Bans the usage of “•” (bullet) in HTML/HAML in favor of “·” (middle dot) in anything that will end up as a text node. (including string literals in Ruby code) + class Linter::MiddleDot < Linter + include LinterRegistry + + # rubocop:disable Style/MiddleDot + BULLET = '•' + # rubocop:enable Style/MiddleDot + MIDDLE_DOT = '·' + MESSAGE = "Use '#{MIDDLE_DOT}' (middle dot) instead of '#{BULLET}' (bullet)".freeze + + def visit_plain(node) + return unless node.text.include?(BULLET) + + record_lint(node, MESSAGE) + end + + def visit_script(node) + return unless node.script.include?(BULLET) + + record_lint(node, MESSAGE) + end + end +end diff --git a/lib/linter/rubocop_middle_dot.rb b/lib/linter/rubocop_middle_dot.rb new file mode 100644 index 000000000..3a1d97c0c --- /dev/null +++ b/lib/linter/rubocop_middle_dot.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module Style + # Bans the usage of “•” (bullet) in HTML/HAML in favor of “·” (middle dot) in string literals + class MiddleDot < Base + extend AutoCorrector + extend Util + + # rubocop:disable Style/MiddleDot + BULLET = '•' + # rubocop:enable Style/MiddleDot + MIDDLE_DOT = '·' + MESSAGE = "Use '#{MIDDLE_DOT}' (middle dot) instead of '#{BULLET}' (bullet)".freeze + + def on_str(node) + # Constants like __FILE__ are handled as strings, + # but don't respond to begin. + return unless node.loc.respond_to?(:begin) && node.loc.begin + + return unless node.value.include?(BULLET) + + add_offense(node, message: MESSAGE) do |corrector| + corrector.replace(node, node.source.gsub(BULLET, MIDDLE_DOT)) + end + end + end + end + end +end