From d450fce77b0592f1e7b6c19d778f6f6c3e85c60c Mon Sep 17 00:00:00 2001 From: Mario Pietsch Date: Tue, 10 Sep 2024 18:17:28 +0200 Subject: [PATCH] Add trimSelection parameter to text editor (#7944) * add trimSelection - both, start, end, no * add documentation for trimSelection to tm-edit-text-operation tiddler * add trimSelection both to all buttions where it makes sense * change "both" to "yes" as a default * fix trimSelection typo * fix trimSelection typo * docs trimSelection add "no" as possible value - it's the default * remove some redundant variable declarations * update from-version * fix comment typos * fix whitespace * make local variables more visible --- .../editor/operations/text/wrap-selection.js | 136 ++++++++++++++---- core/ui/EditorToolbar/bold.tid | 1 + core/ui/EditorToolbar/italic.tid | 1 + core/ui/EditorToolbar/linkify.tid | 1 + core/ui/EditorToolbar/mono-line.tid | 1 + .../stamp-dropdown-item-template.tid | 2 +- core/ui/EditorToolbar/strikethrough.tid | 1 + core/ui/EditorToolbar/subscript.tid | 1 + core/ui/EditorToolbar/superscript.tid | 1 + core/ui/EditorToolbar/transcludify.tid | 1 + core/ui/EditorToolbar/underline.tid | 1 + .../WidgetMessage_ tm-edit-text-operation.tid | 30 ++-- 12 files changed, 138 insertions(+), 39 deletions(-) diff --git a/core/modules/editor/operations/text/wrap-selection.js b/core/modules/editor/operations/text/wrap-selection.js index 6800cbe5b..665d72eb4 100644 --- a/core/modules/editor/operations/text/wrap-selection.js +++ b/core/modules/editor/operations/text/wrap-selection.js @@ -13,37 +13,125 @@ Text editor operation to wrap the selection with the specified prefix and suffix "use strict"; exports["wrap-selection"] = function(event,operation) { - if(operation.selStart === operation.selEnd) { - // No selection; check if we're within the prefix/suffix - if(operation.text.substring(operation.selStart - event.paramObject.prefix.length,operation.selStart + event.paramObject.suffix.length) === event.paramObject.prefix + event.paramObject.suffix) { + var o = operation, + prefix = event.paramObject.prefix, + suffix = event.paramObject.suffix, + trimSelection = event.paramObject.trimSelection || "no", + selLength = o.selEnd - o.selStart; + + // This function detects, if trailing spaces are part of the selection __and__ if the user wants to handle them + // Returns "yes", "start", "end", "no" (default) + // yes .. there are trailing spaces at both ends + // start .. there are trailing spaces at the start + // end .. there are trailing spaces at the end + // no .. no trailing spaces are taken into account + var trailingSpaceAt = function(sel) { + var _start, + _end, + result; + // trimSelection is a user parameter, which this evaluations takes into account + switch(trimSelection) { + case "end": + result = (sel.trimEnd().length !== selLength) ? "end" : "no"; + break; + case "yes": + _start = sel.trimStart().length !== selLength; + _end = sel.trimEnd().length !== selLength; + result = (_start && _end) ? "yes" : (_start) ? "start" : (_end) ? "end" : "no"; + break; + case "start": + result = (sel.trimStart().length !== selLength) ? "start" : "no"; + break; + default: + result = "no"; + break; + } + return result; + } + + function togglePrefixSuffix() { + if(o.text.substring(o.selStart - prefix.length, o.selStart + suffix.length) === prefix + suffix) { // Remove the prefix and suffix - operation.cutStart = operation.selStart - event.paramObject.prefix.length; - operation.cutEnd = operation.selEnd + event.paramObject.suffix.length; - operation.replacement = ""; - operation.newSelStart = operation.cutStart; - operation.newSelEnd = operation.newSelStart; + o.cutStart = o.selStart - prefix.length; + o.cutEnd = o.selEnd + suffix.length; + o.replacement = ""; + o.newSelStart = o.cutStart; + o.newSelEnd = o.newSelStart; } else { // Wrap the cursor instead - operation.cutStart = operation.selStart; - operation.cutEnd = operation.selEnd; - operation.replacement = event.paramObject.prefix + event.paramObject.suffix; - operation.newSelStart = operation.selStart + event.paramObject.prefix.length; - operation.newSelEnd = operation.newSelStart; + o.cutStart = o.selStart; + o.cutEnd = o.selEnd; + o.replacement = prefix + suffix; + o.newSelStart = o.selStart + prefix.length; + o.newSelEnd = o.newSelStart; } - } else if(operation.text.substring(operation.selStart,operation.selStart + event.paramObject.prefix.length) === event.paramObject.prefix && operation.text.substring(operation.selEnd - event.paramObject.suffix.length,operation.selEnd) === event.paramObject.suffix) { + } + + // options: lenPrefix, lenSuffix + function removePrefixSuffix(options) { + options = options || {}; + var _lenPrefix = options.lenPrefix || 0; + var _lenSuffix = options.lenSuffix || 0; + + o.cutStart = o.selStart - _lenPrefix; + o.cutEnd = o.selEnd + _lenSuffix; + o.replacement = (_lenPrefix || _lenSuffix) ? o.selection : o.selection.substring(prefix.length, o.selection.length - suffix.length); + o.newSelStart = o.cutStart; + o.newSelEnd = o.cutStart + o.replacement.length; + } + + function addPrefixSuffix() { + // remove trailing space if requested + switch(trailingSpaceAt(o.selection)) { + case "no": + // has no trailing spaces + o.cutStart = o.selStart; + o.cutEnd = o.selEnd; + o.replacement = prefix + o.selection + suffix; + o.newSelStart = o.selStart; + o.newSelEnd = o.selStart + o.replacement.length; + break; + case "yes": + // handle both ends + o.cutStart = o.selEnd - (o.selection.trimStart().length); + o.cutEnd = o.selection.trimEnd().length + o.selStart; + o.replacement = prefix + o.selection.trim() + suffix; + o.newSelStart = o.cutStart; + o.newSelEnd = o.cutStart + o.replacement.length; + break; + case "start": + // handle leading + o.cutStart = o.selEnd - (o.selection.trimStart().length); + o.cutEnd = o.selEnd; + o.replacement = prefix + o.selection.trimStart() + suffix; + o.newSelStart = o.cutStart; + o.newSelEnd = o.cutStart + o.replacement.length; + break; + case "end": + // handle trailing + o.cutStart = o.selStart; + o.cutEnd = o.selection.trimEnd().length + o.selStart; + o.replacement = prefix + o.selection.trimEnd() + suffix; + o.newSelStart = o.selStart; + o.newSelEnd = o.selStart + o.replacement.length; + break; + } + } + + if(o.selStart === o.selEnd) { + // No selection; Create prefix and suffix. Set cursor in between them: ""|"" + togglePrefixSuffix(); + } else if(o.text.substring(o.selStart, o.selStart + prefix.length) === prefix && + o.text.substring(o.selEnd - suffix.length,o.selEnd) === suffix) { // Prefix and suffix are already present, so remove them - operation.cutStart = operation.selStart; - operation.cutEnd = operation.selEnd; - operation.replacement = operation.selection.substring(event.paramObject.prefix.length,operation.selection.length - event.paramObject.suffix.length); - operation.newSelStart = operation.selStart; - operation.newSelEnd = operation.selStart + operation.replacement.length; + removePrefixSuffix(); + } else if(o.text.substring(o.selStart - prefix.length, o.selStart) === prefix && + o.text.substring(o.selEnd, o.selEnd + suffix.length) === suffix) { + // Prefix and suffix are present BUT not selected -> remove them + removePrefixSuffix({"lenPrefix": prefix.length, "lenSuffix": suffix.length}); } else { // Add the prefix and suffix - operation.cutStart = operation.selStart; - operation.cutEnd = operation.selEnd; - operation.replacement = event.paramObject.prefix + operation.selection + event.paramObject.suffix; - operation.newSelStart = operation.selStart; - operation.newSelEnd = operation.selStart + operation.replacement.length; + addPrefixSuffix(); } }; diff --git a/core/ui/EditorToolbar/bold.tid b/core/ui/EditorToolbar/bold.tid index 1cde1be39..258b13d75 100644 --- a/core/ui/EditorToolbar/bold.tid +++ b/core/ui/EditorToolbar/bold.tid @@ -11,4 +11,5 @@ shortcuts: ((bold)) $param="wrap-selection" prefix="''" suffix="''" + trimSelection="yes" /> diff --git a/core/ui/EditorToolbar/italic.tid b/core/ui/EditorToolbar/italic.tid index 7f3505ddb..47c86b46d 100644 --- a/core/ui/EditorToolbar/italic.tid +++ b/core/ui/EditorToolbar/italic.tid @@ -11,4 +11,5 @@ shortcuts: ((italic)) $param="wrap-selection" prefix="//" suffix="//" + trimSelection="yes" /> diff --git a/core/ui/EditorToolbar/linkify.tid b/core/ui/EditorToolbar/linkify.tid index e8b5fed62..f500669ad 100644 --- a/core/ui/EditorToolbar/linkify.tid +++ b/core/ui/EditorToolbar/linkify.tid @@ -12,4 +12,5 @@ tags: $:/tags/EditorToolbar $param="wrap-selection" prefix="[[" suffix="]]" + trimSelection="yes" /> diff --git a/core/ui/EditorToolbar/mono-line.tid b/core/ui/EditorToolbar/mono-line.tid index 2df863171..cfad08e71 100644 --- a/core/ui/EditorToolbar/mono-line.tid +++ b/core/ui/EditorToolbar/mono-line.tid @@ -11,4 +11,5 @@ shortcuts: ((mono-line)) $param="wrap-selection" prefix="`" suffix="`" + trimSelection="yes" /> diff --git a/core/ui/EditorToolbar/stamp-dropdown-item-template.tid b/core/ui/EditorToolbar/stamp-dropdown-item-template.tid index 5e5acb162..cef3de8b0 100644 --- a/core/ui/EditorToolbar/stamp-dropdown-item-template.tid +++ b/core/ui/EditorToolbar/stamp-dropdown-item-template.tid @@ -21,7 +21,7 @@ title: $:/core/ui/EditorToolbar/StampDropdown/ItemTemplate $message="tm-edit-text-operation" $param="wrap-selection" prefix={{{ [addsuffix[/prefix]get[text]] }}} - suffix={{{ [addsuffix[/suffix]get[text]] }}} + suffix={{{ [addsuffix[/suffix]get[text]] }}} /> diff --git a/core/ui/EditorToolbar/strikethrough.tid b/core/ui/EditorToolbar/strikethrough.tid index 0b00df486..51dbbede2 100644 --- a/core/ui/EditorToolbar/strikethrough.tid +++ b/core/ui/EditorToolbar/strikethrough.tid @@ -11,4 +11,5 @@ shortcuts: ((strikethrough)) $param="wrap-selection" prefix="~~" suffix="~~" + trimSelection="yes" /> diff --git a/core/ui/EditorToolbar/subscript.tid b/core/ui/EditorToolbar/subscript.tid index 1c980a896..4be11ba54 100644 --- a/core/ui/EditorToolbar/subscript.tid +++ b/core/ui/EditorToolbar/subscript.tid @@ -11,4 +11,5 @@ shortcuts: ((subscript)) $param="wrap-selection" prefix=",," suffix=",," + trimSelection="yes" /> diff --git a/core/ui/EditorToolbar/superscript.tid b/core/ui/EditorToolbar/superscript.tid index ee4713409..02cb1176e 100644 --- a/core/ui/EditorToolbar/superscript.tid +++ b/core/ui/EditorToolbar/superscript.tid @@ -11,4 +11,5 @@ shortcuts: ((superscript)) $param="wrap-selection" prefix="^^" suffix="^^" + trimSelection="yes" /> diff --git a/core/ui/EditorToolbar/transcludify.tid b/core/ui/EditorToolbar/transcludify.tid index d24b2041c..61195ed1e 100644 --- a/core/ui/EditorToolbar/transcludify.tid +++ b/core/ui/EditorToolbar/transcludify.tid @@ -12,4 +12,5 @@ tags: $:/tags/EditorToolbar $param="wrap-selection" prefix="{{" suffix="}}" + trimSelection="yes" /> diff --git a/core/ui/EditorToolbar/underline.tid b/core/ui/EditorToolbar/underline.tid index 9f0fc9c30..9d4c4d151 100644 --- a/core/ui/EditorToolbar/underline.tid +++ b/core/ui/EditorToolbar/underline.tid @@ -11,4 +11,5 @@ shortcuts: ((underline)) $param="wrap-selection" prefix="__" suffix="__" + trimSelection="yes" /> diff --git a/editions/tw5.com/tiddlers/messages/WidgetMessage_ tm-edit-text-operation.tid b/editions/tw5.com/tiddlers/messages/WidgetMessage_ tm-edit-text-operation.tid index 72cc90381..9aaca9bf3 100644 --- a/editions/tw5.com/tiddlers/messages/WidgetMessage_ tm-edit-text-operation.tid +++ b/editions/tw5.com/tiddlers/messages/WidgetMessage_ tm-edit-text-operation.tid @@ -1,6 +1,6 @@ caption: tm-edit-text-operation created: 20160424211339792 -modified: 20230803045746596 +modified: 20240909083525060 tags: Messages title: WidgetMessage: tm-edit-text-operation type: text/vnd.tiddlywiki @@ -11,9 +11,9 @@ type: text/vnd.tiddlywiki Excises the currently selected text into a new tiddler and replaces it with a link, a macro or a transclude of the new tiddler. Parameters include: |!Name |!Description | -|title |Title of the new tiddler the selected content is excised to| -|type |Type of the replacement to be inserted: Can be one of <<.value "transclude">>, <<.value "link">> or <<.value "macro">>| -|macro |In case //type=<<.value "macro">>//, specifies the name of the macro to be inserted. The title of the new tiddler is provided as the first parameter to the macro. Defaults to the [[translink macro|translink Macro]]| +|title |Title of the new tiddler the selected content is excised to | +|type |Type of the replacement to be inserted: Can be one of <<.value "transclude">>, <<.value "link">> or <<.value "macro">> | +|macro |In case //type=<<.value "macro">>//, specifies the name of the macro to be inserted. The title of the new tiddler is provided as the first parameter to the macro. Defaults to the [[translink macro|translink Macro]] | |tagnew |If '<<.value "yes">>', will tag the new tiddler with the title of the tiddler currently being edited | @@ -25,7 +25,7 @@ Excises the currently selected text into a new tiddler and replaces it with a li Replaces ''all'' contents of the editor with the provided text. |!Name |!Description | -|text |Text to be inserted| +|text |Text to be inserted | \end @@ -36,7 +36,7 @@ Replaces ''all'' contents of the editor with the provided text. Replaces the current selection with the provided text. |!Name |!Description | -|text |Text to be inserted| +|text |Text to be inserted | \end @@ -47,8 +47,8 @@ Replaces the current selection with the provided text. Prefixes the currently selected line//(s)// with the provided character. If a line is already prefixed by the provided prefix, the prefix is removed instead. |!Name |!Description | -|character |Prefix character| -|count |Number of characters that make up the prefix| +|character |Prefix character | +|count |Number of characters that make up the prefix | ''Example'' Setting //character="<<.value "!">>"// and //count="<<.value "3">>"// would insert the prefix "<<.value "!!!" >>", which will resolve to a subheading when parsed as WikiText. @@ -61,8 +61,8 @@ Prefixes the currently selected line//(s)// with the provided character. If a li Surrounds the selected //lines// with the provided <<.param "prefix">> and <<.param "suffix">>. |!Name |!Description | -|prefix |String to be prefixed to the selected lines| -|suffix |Suffix to be inserted after the selected lines| +|prefix |String to be prefixed to the selected lines | +|suffix |Suffix to be inserted after the selected lines | @@ -75,8 +75,9 @@ Surrounds the selected //lines// with the provided <<.param "prefix">> and <<.pa Surrounds the current //selection// with the provided <<.param "prefix">> and <<.param "suffix">>. |!Name |!Description | -|prefix |String to be prefixed to the selection| -|suffix |Suffix to be inserted after the selection| +|prefix |String to be prefixed to the selection | +|suffix |Suffix to be inserted after the selection | +|trimSelection |<<.from-version 5.3.6>> Trim leading and trailing white-space from the selection and move it to the surrounding text. Possible values are: `yes`, `no` (default), `start` and `end` | \end @@ -138,11 +139,11 @@ At this point the following text operations have been implemented: |<<.def "wrap-selection">>|<> | |<<.def "save-selection">>|<> | |<<.def "make-link">>|<> | -|<<.def "insert-text">>|<>| +|<<.def "insert-text">>|<> | |<<.def "focus-editor">>|<<.from-version 5.2.0>> <> | -!Example +! Example An example can be seen in [[$:/core/ui/EditorToolbar/bold]]: @@ -152,6 +153,7 @@ An example can be seen in [[$:/core/ui/EditorToolbar/bold]]: $param="wrap-selection" prefix="''" suffix="''" + trimSelection="yes" /> ```