Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 58575c3

Browse files
authored
Merge pull request #5510 from SimonBrandner/feature-surround-with
Add surround with feature
2 parents b5a7081 + b9b98c6 commit 58575c3

File tree

4 files changed

+48
-0
lines changed

4 files changed

+48
-0
lines changed

src/components/views/rooms/BasicMessageComposer.tsx

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,14 @@ const REGEX_EMOTICON_WHITESPACE = new RegExp('(?:^|\\s)(' + EMOTICON_REGEX.sourc
5555

5656
const IS_MAC = navigator.platform.indexOf("Mac") !== -1;
5757

58+
const SURROUND_WITH_CHARACTERS = ["\"", "_", "`", "'", "*", "~", "$"];
59+
const SURROUND_WITH_DOUBLE_CHARACTERS = new Map([
60+
["(", ")"],
61+
["[", "]"],
62+
["{", "}"],
63+
["<", ">"],
64+
]);
65+
5866
function ctrlShortcutLabel(key: string): string {
5967
return (IS_MAC ? "⌘" : "Ctrl") + "+" + key;
6068
}
@@ -99,6 +107,7 @@ interface IState {
99107
showVisualBell?: boolean;
100108
autoComplete?: AutocompleteWrapperModel;
101109
completionIndex?: number;
110+
surroundWith: boolean;
102111
}
103112

104113
@replaceableComponent("views.rooms.BasicMessageEditor")
@@ -117,19 +126,23 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
117126

118127
private readonly emoticonSettingHandle: string;
119128
private readonly shouldShowPillAvatarSettingHandle: string;
129+
private readonly surroundWithHandle: string;
120130
private readonly historyManager = new HistoryManager();
121131

122132
constructor(props) {
123133
super(props);
124134
this.state = {
125135
showPillAvatar: SettingsStore.getValue("Pill.shouldShowPillAvatar"),
136+
surroundWith: SettingsStore.getValue("MessageComposerInput.surroundWith"),
126137
};
127138

128139
this.emoticonSettingHandle = SettingsStore.watchSetting('MessageComposerInput.autoReplaceEmoji', null,
129140
this.configureEmoticonAutoReplace);
130141
this.configureEmoticonAutoReplace();
131142
this.shouldShowPillAvatarSettingHandle = SettingsStore.watchSetting("Pill.shouldShowPillAvatar", null,
132143
this.configureShouldShowPillAvatar);
144+
this.surroundWithHandle = SettingsStore.watchSetting("MessageComposerInput.surroundWith", null,
145+
this.surroundWithSettingChanged);
133146
}
134147

135148
public componentDidUpdate(prevProps: IProps) {
@@ -422,6 +435,28 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
422435
private onKeyDown = (event: React.KeyboardEvent): void => {
423436
const model = this.props.model;
424437
let handled = false;
438+
439+
if (this.state.surroundWith && document.getSelection().type != "Caret") {
440+
// This surrounds the selected text with a character. This is
441+
// intentionally left out of the keybinding manager as the keybinds
442+
// here shouldn't be changeable
443+
444+
const selectionRange = getRangeForSelection(
445+
this.editorRef.current,
446+
this.props.model,
447+
document.getSelection(),
448+
);
449+
// trim the range as we want it to exclude leading/trailing spaces
450+
selectionRange.trim();
451+
452+
if ([...SURROUND_WITH_DOUBLE_CHARACTERS.keys(), ...SURROUND_WITH_CHARACTERS].includes(event.key)) {
453+
this.historyManager.ensureLastChangesPushed(this.props.model);
454+
this.modifiedFlag = true;
455+
toggleInlineFormat(selectionRange, event.key, SURROUND_WITH_DOUBLE_CHARACTERS.get(event.key));
456+
handled = true;
457+
}
458+
}
459+
425460
const action = getKeyBindingsManager().getMessageComposerAction(event);
426461
switch (action) {
427462
case MessageComposerAction.FormatBold:
@@ -574,13 +609,19 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
574609
this.setState({ showPillAvatar });
575610
};
576611

612+
private surroundWithSettingChanged = () => {
613+
const surroundWith = SettingsStore.getValue("MessageComposerInput.surroundWith");
614+
this.setState({ surroundWith });
615+
};
616+
577617
componentWillUnmount() {
578618
document.removeEventListener("selectionchange", this.onSelectionChange);
579619
this.editorRef.current.removeEventListener("input", this.onInput, true);
580620
this.editorRef.current.removeEventListener("compositionstart", this.onCompositionStart, true);
581621
this.editorRef.current.removeEventListener("compositionend", this.onCompositionEnd, true);
582622
SettingsStore.unwatchSetting(this.emoticonSettingHandle);
583623
SettingsStore.unwatchSetting(this.shouldShowPillAvatarSettingHandle);
624+
SettingsStore.unwatchSetting(this.surroundWithHandle);
584625
}
585626

586627
componentDidMount() {

src/components/views/settings/tabs/user/PreferencesUserSettingsTab.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ export default class PreferencesUserSettingsTab extends React.Component<{}, ISta
6161
'MessageComposerInput.suggestEmoji',
6262
'sendTypingNotifications',
6363
'MessageComposerInput.ctrlEnterToSend',
64+
'MessageComposerInput.surroundWith',
6465
'MessageComposerInput.showStickersButton',
6566
];
6667

src/i18n/strings/en_EN.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -847,6 +847,7 @@
847847
"Use Ctrl + F to search timeline": "Use Ctrl + F to search timeline",
848848
"Use Command + Enter to send a message": "Use Command + Enter to send a message",
849849
"Use Ctrl + Enter to send a message": "Use Ctrl + Enter to send a message",
850+
"Surround selected text when typing special characters": "Surround selected text when typing special characters",
850851
"Automatically replace plain text Emoji": "Automatically replace plain text Emoji",
851852
"Mirror local video feed": "Mirror local video feed",
852853
"Enable Community Filter Panel": "Enable Community Filter Panel",

src/settings/Settings.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,11 @@ export const SETTINGS: {[setting: string]: ISetting} = {
449449
displayName: isMac ? _td("Use Command + Enter to send a message") : _td("Use Ctrl + Enter to send a message"),
450450
default: false,
451451
},
452+
"MessageComposerInput.surroundWith": {
453+
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
454+
displayName: _td("Surround selected text when typing special characters"),
455+
default: false,
456+
},
452457
"MessageComposerInput.autoReplaceEmoji": {
453458
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
454459
displayName: _td('Automatically replace plain text Emoji'),

0 commit comments

Comments
 (0)