Skip to content

Commit 7d8f55e

Browse files
authored
Merge pull request microsoft#135698 from thebinarysearchtree/TitleCaseAction
Handle apostrophes in TitleCaseAction
2 parents 8eae60d + 5baf3ab commit 7d8f55e

File tree

2 files changed

+43
-48
lines changed

2 files changed

+43
-48
lines changed

src/vs/editor/contrib/linesOperations/linesOperations.ts

Lines changed: 36 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,43 +1076,6 @@ export class LowerCaseAction extends AbstractCaseAction {
10761076
}
10771077
}
10781078

1079-
export class TitleCaseAction extends AbstractCaseAction {
1080-
constructor() {
1081-
super({
1082-
id: 'editor.action.transformToTitlecase',
1083-
label: nls.localize('editor.transformToTitlecase', "Transform to Title Case"),
1084-
alias: 'Transform to Title Case',
1085-
precondition: EditorContextKeys.writable
1086-
});
1087-
}
1088-
1089-
protected _modifyText(text: string, wordSeparators: string): string {
1090-
const separators = '\r\n\t ' + wordSeparators;
1091-
const excludedChars = separators.split('');
1092-
1093-
let title = '';
1094-
let startUpperCase = true;
1095-
1096-
for (let i = 0; i < text.length; i++) {
1097-
let currentChar = text[i];
1098-
1099-
if (excludedChars.indexOf(currentChar) >= 0) {
1100-
startUpperCase = true;
1101-
1102-
title += currentChar;
1103-
} else if (startUpperCase) {
1104-
startUpperCase = false;
1105-
1106-
title += currentChar.toLocaleUpperCase();
1107-
} else {
1108-
title += currentChar.toLocaleLowerCase();
1109-
}
1110-
}
1111-
1112-
return title;
1113-
}
1114-
}
1115-
11161079
class BackwardsCompatibleRegExp {
11171080

11181081
private _actual: RegExp | null;
@@ -1143,10 +1106,35 @@ class BackwardsCompatibleRegExp {
11431106
}
11441107
}
11451108

1109+
export class TitleCaseAction extends AbstractCaseAction {
1110+
1111+
public static titleBoundary = new BackwardsCompatibleRegExp('(^|[^\\p{L}\\p{N}\']|((^|\\P{L})\'))\\p{L}', 'gmu');
1112+
1113+
constructor() {
1114+
super({
1115+
id: 'editor.action.transformToTitlecase',
1116+
label: nls.localize('editor.transformToTitlecase', "Transform to Title Case"),
1117+
alias: 'Transform to Title Case',
1118+
precondition: EditorContextKeys.writable
1119+
});
1120+
}
1121+
1122+
protected _modifyText(text: string, wordSeparators: string): string {
1123+
const titleBoundary = TitleCaseAction.titleBoundary.get();
1124+
if (!titleBoundary) {
1125+
// cannot support this
1126+
return text;
1127+
}
1128+
return text
1129+
.toLocaleLowerCase()
1130+
.replace(titleBoundary, (b) => b.toLocaleUpperCase());
1131+
}
1132+
}
1133+
11461134
export class SnakeCaseAction extends AbstractCaseAction {
11471135

1148-
public static regExp1 = new BackwardsCompatibleRegExp('(\\p{Ll})(\\p{Lu})', 'gmu');
1149-
public static regExp2 = new BackwardsCompatibleRegExp('(\\p{Lu}|\\p{N})(\\p{Lu})(\\p{Ll})', 'gmu');
1136+
public static caseBoundary = new BackwardsCompatibleRegExp('(\\p{Ll})(\\p{Lu})', 'gmu');
1137+
public static singleLetters = new BackwardsCompatibleRegExp('(\\p{Lu}|\\p{N})(\\p{Lu})(\\p{Ll})', 'gmu');
11501138

11511139
constructor() {
11521140
super({
@@ -1158,15 +1146,15 @@ export class SnakeCaseAction extends AbstractCaseAction {
11581146
}
11591147

11601148
protected _modifyText(text: string, wordSeparators: string): string {
1161-
const regExp1 = SnakeCaseAction.regExp1.get();
1162-
const regExp2 = SnakeCaseAction.regExp2.get();
1163-
if (!regExp1 || !regExp2) {
1149+
const caseBoundary = SnakeCaseAction.caseBoundary.get();
1150+
const singleLetters = SnakeCaseAction.singleLetters.get();
1151+
if (!caseBoundary || !singleLetters) {
11641152
// cannot support this
11651153
return text;
11661154
}
11671155
return (text
1168-
.replace(regExp1, '$1_$2')
1169-
.replace(regExp2, '$1_$2$3')
1156+
.replace(caseBoundary, '$1_$2')
1157+
.replace(singleLetters, '$1_$2$3')
11701158
.toLocaleLowerCase()
11711159
);
11721160
}
@@ -1192,8 +1180,10 @@ registerEditorAction(JoinLinesAction);
11921180
registerEditorAction(TransposeAction);
11931181
registerEditorAction(UpperCaseAction);
11941182
registerEditorAction(LowerCaseAction);
1195-
registerEditorAction(TitleCaseAction);
11961183

1197-
if (SnakeCaseAction.regExp1.isSupported() && SnakeCaseAction.regExp2.isSupported()) {
1184+
if (SnakeCaseAction.caseBoundary.isSupported() && SnakeCaseAction.singleLetters.isSupported()) {
11981185
registerEditorAction(SnakeCaseAction);
11991186
}
1187+
if (TitleCaseAction.titleBoundary.isSupported()) {
1188+
registerEditorAction(TitleCaseAction);
1189+
}

src/vs/editor/contrib/linesOperations/test/linesOperations.test.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -748,7 +748,8 @@ suite('Editor Contrib - Line Operations', () => {
748748
'foO[baR]BaZ',
749749
'foO`baR~BaZ',
750750
'foO^baR%BaZ',
751-
'foO$baR!BaZ'
751+
'foO$baR!BaZ',
752+
'\'physician\'s assistant\''
752753
], {}, (editor) => {
753754
let model = editor.getModel()!;
754755
let titlecaseAction = new TitleCaseAction();
@@ -759,7 +760,7 @@ suite('Editor Contrib - Line Operations', () => {
759760

760761
editor.setSelection(new Selection(2, 1, 2, 12));
761762
executeAction(titlecaseAction, editor);
762-
assert.strictEqual(model.getLineContent(2), 'Foo\'Bar\'Baz');
763+
assert.strictEqual(model.getLineContent(2), 'Foo\'bar\'baz');
763764

764765
editor.setSelection(new Selection(3, 1, 3, 12));
765766
executeAction(titlecaseAction, editor);
@@ -776,6 +777,10 @@ suite('Editor Contrib - Line Operations', () => {
776777
editor.setSelection(new Selection(6, 1, 6, 12));
777778
executeAction(titlecaseAction, editor);
778779
assert.strictEqual(model.getLineContent(6), 'Foo$Bar!Baz');
780+
781+
editor.setSelection(new Selection(7, 1, 7, 23));
782+
executeAction(titlecaseAction, editor);
783+
assert.strictEqual(model.getLineContent(7), '\'Physician\'s Assistant\'');
779784
}
780785
);
781786

0 commit comments

Comments
 (0)