Skip to content

Commit 2d46dec

Browse files
committed
Handle apostrophes in TitleCaseAction
1 parent 96cda0b commit 2d46dec

File tree

2 files changed

+46
-47
lines changed

2 files changed

+46
-47
lines changed

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

Lines changed: 39 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,43 +1008,6 @@ export class LowerCaseAction extends AbstractCaseAction {
10081008
}
10091009
}
10101010

1011-
export class TitleCaseAction extends AbstractCaseAction {
1012-
constructor() {
1013-
super({
1014-
id: 'editor.action.transformToTitlecase',
1015-
label: nls.localize('editor.transformToTitlecase', "Transform to Title Case"),
1016-
alias: 'Transform to Title Case',
1017-
precondition: EditorContextKeys.writable
1018-
});
1019-
}
1020-
1021-
protected _modifyText(text: string, wordSeparators: string): string {
1022-
const separators = '\r\n\t ' + wordSeparators;
1023-
const excludedChars = separators.split('');
1024-
1025-
let title = '';
1026-
let startUpperCase = true;
1027-
1028-
for (let i = 0; i < text.length; i++) {
1029-
let currentChar = text[i];
1030-
1031-
if (excludedChars.indexOf(currentChar) >= 0) {
1032-
startUpperCase = true;
1033-
1034-
title += currentChar;
1035-
} else if (startUpperCase) {
1036-
startUpperCase = false;
1037-
1038-
title += currentChar.toLocaleUpperCase();
1039-
} else {
1040-
title += currentChar.toLocaleLowerCase();
1041-
}
1042-
}
1043-
1044-
return title;
1045-
}
1046-
}
1047-
10481011
class BackwardsCompatibleRegExp {
10491012

10501013
private _actual: RegExp | null;
@@ -1075,10 +1038,38 @@ class BackwardsCompatibleRegExp {
10751038
}
10761039
}
10771040

1041+
export class TitleCaseAction extends AbstractCaseAction {
1042+
1043+
public static titleBoundary = new BackwardsCompatibleRegExp('(^|[^\\p{L}\\p{N}])[\\p{L}\\p{N}]', 'gmu');
1044+
public static apostrophe = new BackwardsCompatibleRegExp('[\\p{L}\\p{N}]\'[\\p{L}\\p{N}]', 'gmu');
1045+
1046+
constructor() {
1047+
super({
1048+
id: 'editor.action.transformToTitlecase',
1049+
label: nls.localize('editor.transformToTitlecase', "Transform to Title Case"),
1050+
alias: 'Transform to Title Case',
1051+
precondition: EditorContextKeys.writable
1052+
});
1053+
}
1054+
1055+
protected _modifyText(text: string, wordSeparators: string): string {
1056+
const titleBoundary = TitleCaseAction.titleBoundary.get();
1057+
const apostrophe = TitleCaseAction.apostrophe.get();
1058+
if (!titleBoundary || !apostrophe) {
1059+
// cannot support this
1060+
return text;
1061+
}
1062+
return text
1063+
.toLocaleLowerCase()
1064+
.replace(titleBoundary, (b) => b.toLocaleUpperCase())
1065+
.replace(apostrophe, (a) => a.toLocaleLowerCase());
1066+
}
1067+
}
1068+
10781069
export class SnakeCaseAction extends AbstractCaseAction {
10791070

1080-
public static regExp1 = new BackwardsCompatibleRegExp('(\\p{Ll})(\\p{Lu})', 'gmu');
1081-
public static regExp2 = new BackwardsCompatibleRegExp('(\\p{Lu}|\\p{N})(\\p{Lu})(\\p{Ll})', 'gmu');
1071+
public static caseBoundary = new BackwardsCompatibleRegExp('(\\p{Ll})(\\p{Lu})', 'gmu');
1072+
public static singleLetters = new BackwardsCompatibleRegExp('(\\p{Lu}|\\p{N})(\\p{Lu})(\\p{Ll})', 'gmu');
10821073

10831074
constructor() {
10841075
super({
@@ -1090,15 +1081,15 @@ export class SnakeCaseAction extends AbstractCaseAction {
10901081
}
10911082

10921083
protected _modifyText(text: string, wordSeparators: string): string {
1093-
const regExp1 = SnakeCaseAction.regExp1.get();
1094-
const regExp2 = SnakeCaseAction.regExp2.get();
1095-
if (!regExp1 || !regExp2) {
1084+
const caseBoundary = SnakeCaseAction.caseBoundary.get();
1085+
const singleLetters = SnakeCaseAction.singleLetters.get();
1086+
if (!caseBoundary || !singleLetters) {
10961087
// cannot support this
10971088
return text;
10981089
}
10991090
return (text
1100-
.replace(regExp1, '$1_$2')
1101-
.replace(regExp2, '$1_$2$3')
1091+
.replace(caseBoundary, '$1_$2')
1092+
.replace(singleLetters, '$1_$2$3')
11021093
.toLocaleLowerCase()
11031094
);
11041095
}
@@ -1125,6 +1116,9 @@ registerEditorAction(UpperCaseAction);
11251116
registerEditorAction(LowerCaseAction);
11261117
registerEditorAction(TitleCaseAction);
11271118

1128-
if (SnakeCaseAction.regExp1.isSupported() && SnakeCaseAction.regExp2.isSupported()) {
1119+
if (SnakeCaseAction.caseBoundary.isSupported() && SnakeCaseAction.singleLetters.isSupported()) {
11291120
registerEditorAction(SnakeCaseAction);
11301121
}
1122+
if (TitleCaseAction.titleBoundary.isSupported() && TitleCaseAction.apostrophe.isSupported()) {
1123+
registerEditorAction(TitleCaseAction);
1124+
}

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -687,7 +687,8 @@ suite('Editor Contrib - Line Operations', () => {
687687
'foO[baR]BaZ',
688688
'foO`baR~BaZ',
689689
'foO^baR%BaZ',
690-
'foO$baR!BaZ'
690+
'foO$baR!BaZ',
691+
'\'physician\'s assistant\''
691692
], {}, (editor) => {
692693
let model = editor.getModel()!;
693694
let titlecaseAction = new TitleCaseAction();
@@ -698,7 +699,7 @@ suite('Editor Contrib - Line Operations', () => {
698699

699700
editor.setSelection(new Selection(2, 1, 2, 12));
700701
executeAction(titlecaseAction, editor);
701-
assert.strictEqual(model.getLineContent(2), 'Foo\'Bar\'Baz');
702+
assert.strictEqual(model.getLineContent(2), 'Foo\'bar\'baz');
702703

703704
editor.setSelection(new Selection(3, 1, 3, 12));
704705
executeAction(titlecaseAction, editor);
@@ -715,6 +716,10 @@ suite('Editor Contrib - Line Operations', () => {
715716
editor.setSelection(new Selection(6, 1, 6, 12));
716717
executeAction(titlecaseAction, editor);
717718
assert.strictEqual(model.getLineContent(6), 'Foo$Bar!Baz');
719+
720+
editor.setSelection(new Selection(7, 1, 7, 23));
721+
executeAction(titlecaseAction, editor);
722+
assert.strictEqual(model.getLineContent(7), '\'Physician\'s Assistant\'');
718723
}
719724
);
720725

0 commit comments

Comments
 (0)