Commit 482cd7d1 authored by Brett Walker's avatar Brett Walker

Add user preference to enable surrounding text

in markdown fields using certain keystrokes
", ', `, (, [, {, <, *, _
parent 41ae5ee7
......@@ -283,9 +283,9 @@ function updateText({ textArea, tag, cursorOffset, blockTag, wrap, select, tagCo
/* eslint-disable @gitlab/require-i18n-strings */
export function keypressNoteText(e) {
if (this.selectionStart === this.selectionEnd) {
return;
}
if (!gon.markdown_surround_selection) return;
if (this.selectionStart === this.selectionEnd) return;
const keys = {
'*': '**{text}**', // wraps with bold character
_: '_{text}_', // wraps with italic character
......
......@@ -49,7 +49,8 @@ class Profiles::PreferencesController < Profiles::ApplicationController
:tab_width,
:sourcegraph_enabled,
:gitpod_enabled,
:render_whitespace_in_code
:render_whitespace_in_code,
:markdown_surround_selection
]
end
end
......
......@@ -294,6 +294,7 @@ class User < ApplicationRecord
:setup_for_company, :setup_for_company=,
:render_whitespace_in_code, :render_whitespace_in_code=,
:experience_level, :experience_level=,
:markdown_surround_selection, :markdown_surround_selection=,
to: :user_preference
delegate :path, to: :namespace, allow_nil: true, prefix: true
......
......@@ -27,6 +27,7 @@ class UserPreference < ApplicationRecord
default_value_for :time_display_relative, value: true, allows_nil: false
default_value_for :time_format_in_24h, value: false, allows_nil: false
default_value_for :render_whitespace_in_code, value: false, allows_nil: false
default_value_for :markdown_surround_selection, value: true, allows_nil: false
class << self
def notes_filters
......
......@@ -88,6 +88,15 @@
= s_("Preferences|Show one file at a time on merge request's Changes tab")
.form-text.text-muted
= s_("Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser.")
.form-group.form-check
= f.check_box :markdown_surround_selection, class: 'form-check-input'
= f.label :markdown_surround_selection, class: 'form-check-label' do
= s_('Preferences|Surround text selection when typing quotes or brackets')
.form-text.text-muted
- supported_characters = %w(" ' ` \( [ { < * _).map {|char| "<code>#{char}</code>" }.join(', ')
- msg = "Preferences|When you type in a description or comment box, selected text is surrounded by the corresponding character after typing one of the following characters: #{supported_characters}."
= s_(msg).html_safe
.form-group
= f.label :tab_width, s_('Preferences|Tab width'), class: 'label-bold'
= f.number_field :tab_width,
......
---
title: Add user preference to turn off selected text keystroke formatting
merge_request: 53079
author:
type: added
# frozen_string_literal: true
class AddMarkdownSurroundSelectionToUserPreferences < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
with_lock_retries do
add_column :user_preferences, :markdown_surround_selection, :boolean, default: true, null: false
end
end
def down
with_lock_retries do
remove_column :user_preferences, :markdown_surround_selection, :boolean
end
end
end
484751de711e873e0f0f22d5951e36bf60d4826ebc95afa45e4f6cdaa0e3c024
\ No newline at end of file
......@@ -17926,7 +17926,8 @@ CREATE TABLE user_preferences (
tab_width smallint,
experience_level smallint,
view_diffs_file_by_file boolean DEFAULT false NOT NULL,
gitpod_enabled boolean DEFAULT false NOT NULL
gitpod_enabled boolean DEFAULT false NOT NULL,
markdown_surround_selection boolean DEFAULT true NOT NULL
);
CREATE SEQUENCE user_preferences_id_seq
......@@ -7,13 +7,14 @@ module Gitlab
include WebpackHelper
def add_gon_variables
gon.api_version = 'v4'
gon.default_avatar_url = default_avatar_url
gon.max_file_size = Gitlab::CurrentSettings.max_attachment_size
gon.asset_host = ActionController::Base.asset_host
gon.webpack_public_path = webpack_public_path
gon.relative_url_root = Gitlab.config.gitlab.relative_url_root
gon.user_color_scheme = Gitlab::ColorSchemes.for_user(current_user).css_class
gon.api_version = 'v4'
gon.default_avatar_url = default_avatar_url
gon.max_file_size = Gitlab::CurrentSettings.max_attachment_size
gon.asset_host = ActionController::Base.asset_host
gon.webpack_public_path = webpack_public_path
gon.relative_url_root = Gitlab.config.gitlab.relative_url_root
gon.user_color_scheme = Gitlab::ColorSchemes.for_user(current_user).css_class
gon.markdown_surround_selection = current_user&.markdown_surround_selection
if Gitlab.config.sentry.enabled
gon.sentry_dsn = Gitlab.config.sentry.clientside_dsn
......
......@@ -22585,6 +22585,9 @@ msgstr ""
msgid "Preferences|Sourcegraph"
msgstr ""
msgid "Preferences|Surround text selection when typing quotes or brackets"
msgstr ""
msgid "Preferences|Syntax highlighting theme"
msgstr ""
......
......@@ -171,27 +171,40 @@ describe('init markdown', () => {
expect(textArea.value).toEqual(text.replace(selected, `[${selected}](url)`));
});
it.each`
key | expected
${'['} | ${`[${selected}]`}
${'*'} | ${`**${selected}**`}
${"'"} | ${`'${selected}'`}
${'_'} | ${`_${selected}_`}
${'`'} | ${`\`${selected}\``}
${'"'} | ${`"${selected}"`}
${'{'} | ${`{${selected}}`}
${'('} | ${`(${selected})`}
${'<'} | ${`<${selected}>`}
`('generates $expected when $key is pressed', ({ key, expected }) => {
const event = new KeyboardEvent('keydown', { key });
textArea.addEventListener('keydown', keypressNoteText);
textArea.dispatchEvent(event);
expect(textArea.value).toEqual(text.replace(selected, expected));
describe('surrounds selected text with matching character', () => {
it.each`
key | expected
${'['} | ${`[${selected}]`}
${'*'} | ${`**${selected}**`}
${"'"} | ${`'${selected}'`}
${'_'} | ${`_${selected}_`}
${'`'} | ${`\`${selected}\``}
${'"'} | ${`"${selected}"`}
${'{'} | ${`{${selected}}`}
${'('} | ${`(${selected})`}
${'<'} | ${`<${selected}>`}
`('generates $expected when $key is pressed', ({ key, expected }) => {
const event = new KeyboardEvent('keydown', { key });
gon.markdown_surround_selection = true;
textArea.addEventListener('keydown', keypressNoteText);
textArea.dispatchEvent(event);
expect(textArea.value).toEqual(text.replace(selected, expected));
// cursor placement should be after selection + 2 tag lengths
expect(textArea.selectionStart).toBe(selectedIndex + expected.length);
});
// cursor placement should be after selection + 2 tag lengths
expect(textArea.selectionStart).toBe(selectedIndex + expected.length);
it('does nothing if user preference disabled', () => {
const event = new KeyboardEvent('keydown', { key: '[' });
gon.markdown_surround_selection = false;
textArea.addEventListener('keydown', keypressNoteText);
textArea.dispatchEvent(event);
expect(textArea.value).toEqual(text);
});
});
describe('and text to be selected', () => {
......
......@@ -41,6 +41,9 @@ RSpec.describe User do
it { is_expected.to delegate_method(:show_whitespace_in_diffs).to(:user_preference) }
it { is_expected.to delegate_method(:show_whitespace_in_diffs=).to(:user_preference).with_arguments(:args) }
it { is_expected.to delegate_method(:view_diffs_file_by_file).to(:user_preference) }
it { is_expected.to delegate_method(:view_diffs_file_by_file=).to(:user_preference).with_arguments(:args) }
it { is_expected.to delegate_method(:tab_width).to(:user_preference) }
it { is_expected.to delegate_method(:tab_width=).to(:user_preference).with_arguments(:args) }
......@@ -59,6 +62,9 @@ RSpec.describe User do
it { is_expected.to delegate_method(:experience_level).to(:user_preference) }
it { is_expected.to delegate_method(:experience_level=).to(:user_preference).with_arguments(:args) }
it { is_expected.to delegate_method(:markdown_surround_selection).to(:user_preference) }
it { is_expected.to delegate_method(:markdown_surround_selection=).to(:user_preference).with_arguments(:args) }
it { is_expected.to delegate_method(:job_title).to(:user_detail).allow_nil }
it { is_expected.to delegate_method(:job_title=).to(:user_detail).with_arguments(:args).allow_nil }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment