Add indent aware begin/end of line action methods

The methods going to “begin of indented line” will go to the first non-whitespace character on the line, unless the caret is already there or to the left of this character, in which case it will go to the actual beginning of the line.

The “end of indented line” methods work similarly.

If you want [⇪]⌘⇠/⇢ and ⌘⌫/⌦ to use this behavior, you can add the following to your key bindings file:

	"@\UF702"  = "moveToBeginningOfIndentedLine:";
	"$@\UF702" = "moveToBeginningOfIndentedLineAndModifySelection:";
	"@\UF703"  = "moveToEndOfIndentedLine:";
	"$@\UF703" = "moveToEndOfIndentedLineAndModifySelection:";
	"@\U007F"  = "deleteToBeginningOfIndentedLine:";
	"@\UF728"  = "deleteToEndOfIndentedLine:";
This commit is contained in:
Allan Odgaard
2012-09-21 19:55:27 +02:00
parent 13f35bd5b1
commit bae6270d6c
7 changed files with 119 additions and 6 deletions

View File

@@ -2600,6 +2600,8 @@ static scope::context_t add_modifiers_to_scope (scope::context_t scope, NSUInteg
- ACTION(moveToBeginningOfColumnAndModifySelection);
- ACTION(moveToBeginningOfDocument);
- ACTION(moveToBeginningOfDocumentAndModifySelection);
- ACTION(moveToBeginningOfIndentedLine);
- ACTION(moveToBeginningOfIndentedLineAndModifySelection);
- ACTION(moveToBeginningOfLine);
- ACTION(moveToBeginningOfLineAndModifySelection);
- ACTION(moveToBeginningOfParagraph);
@@ -2610,6 +2612,8 @@ static scope::context_t add_modifiers_to_scope (scope::context_t scope, NSUInteg
- ACTION(moveToEndOfColumnAndModifySelection);
- ACTION(moveToEndOfDocument);
- ACTION(moveToEndOfDocumentAndModifySelection);
- ACTION(moveToEndOfIndentedLine);
- ACTION(moveToEndOfIndentedLineAndModifySelection);
- ACTION(moveToEndOfLine);
- ACTION(moveToEndOfLineAndModifySelection);
- ACTION(moveToEndOfParagraph);
@@ -2662,8 +2666,10 @@ static scope::context_t add_modifiers_to_scope (scope::context_t scope, NSUInteg
- ACTION(deleteForward);
- ACTION(deleteSubWordLeft);
- ACTION(deleteSubWordRight);
- ACTION(deleteToBeginningOfIndentedLine);
- ACTION(deleteToBeginningOfLine);
- ACTION(deleteToBeginningOfParagraph);
- ACTION(deleteToEndOfIndentedLine);
- ACTION(deleteToEndOfLine);
- ACTION(deleteToEndOfParagraph);
- ACTION(deleteWordBackward);

View File

@@ -25,6 +25,10 @@ namespace ng
{ "moveToBeginningOfColumnAndModifySelection:", kMoveToBeginningOfColumnAndModifySelection },
{ "moveToBeginningOfDocument:", kMoveToBeginningOfDocument },
{ "moveToBeginningOfDocumentAndModifySelection:", kMoveToBeginningOfDocumentAndModifySelection },
{ "moveToBeginningOfIndentedLine:", kMoveToBeginningOfIndentedLine },
{ "moveToBeginningOfIndentedLineAndModifySelection:", kMoveToBeginningOfIndentedLineAndModifySelection },
{ "moveToEndOfIndentedLine:", kMoveToEndOfIndentedLine },
{ "moveToEndOfIndentedLineAndModifySelection:", kMoveToEndOfIndentedLineAndModifySelection },
{ "moveToBeginningOfLine:", kMoveToBeginningOfLine },
{ "moveToBeginningOfLineAndModifySelection:", kMoveToBeginningOfLineAndModifySelection },
{ "moveToBeginningOfParagraph:", kMoveToBeginningOfParagraph },
@@ -89,8 +93,10 @@ namespace ng
{ "deleteForward:", kDeleteForward },
{ "deleteSubWordLeft:", kDeleteSubWordLeft },
{ "deleteSubWordRight:", kDeleteSubWordRight },
{ "deleteToBeginningOfIndentedLine:", kDeleteToBeginningOfIndentedLine },
{ "deleteToBeginningOfLine:", kDeleteToBeginningOfLine },
{ "deleteToBeginningOfParagraph:", kDeleteToBeginningOfParagraph },
{ "deleteToEndOfIndentedLine:", kDeleteToEndOfIndentedLine },
{ "deleteToEndOfLine:", kDeleteToEndOfLine },
{ "deleteToEndOfParagraph:", kDeleteToEndOfParagraph },
{ "deleteWordBackward:", kDeleteWordBackward },

View File

@@ -728,6 +728,8 @@ namespace ng
case kDeleteSubWordRight: _selections = ng::extend_if_empty(_buffer, _selections, kSelectionExtendToEndOfSubWord, layout); break;
case kDeleteWordBackward: _selections = ng::extend_if_empty(_buffer, _selections, kSelectionExtendToBeginOfWord, layout); break;
case kDeleteWordForward: _selections = ng::extend_if_empty(_buffer, _selections, kSelectionExtendToEndOfWord, layout); break;
case kDeleteToBeginningOfIndentedLine: _selections = ng::extend_if_empty(_buffer, _selections, kSelectionExtendToBeginOfIndentedLine, layout); break;
case kDeleteToEndOfIndentedLine: _selections = ng::extend_if_empty(_buffer, _selections, kSelectionExtendToEndOfIndentedLine, layout); break;
case kDeleteToBeginningOfLine: _selections = ng::extend_if_empty(_buffer, _selections, kSelectionExtendToBeginOfSoftLine, layout); break;
case kDeleteToEndOfLine: _selections = ng::extend_if_empty(_buffer, _selections, kSelectionExtendToEndOfSoftLine, layout); break;
case kDeleteToBeginningOfParagraph: _selections = ng::extend_if_empty(_buffer, _selections, kSelectionExtendToBeginOfParagraph, layout); break;
@@ -748,8 +750,8 @@ namespace ng
}
static std::set<action_t> const deleteActions = { kDeleteBackward, kDeleteForward };
static std::set<action_t> const yankAppendActions = { kDeleteSubWordRight, kDeleteWordForward, kDeleteToEndOfLine, kDeleteToEndOfParagraph };
static std::set<action_t> const yankPrependActions = { kDeleteSubWordLeft, kDeleteWordBackward, kDeleteToBeginningOfLine, kDeleteToBeginningOfParagraph };
static std::set<action_t> const yankAppendActions = { kDeleteSubWordRight, kDeleteWordForward, kDeleteToEndOfIndentedLine, kDeleteToEndOfLine, kDeleteToEndOfParagraph };
static std::set<action_t> const yankPrependActions = { kDeleteSubWordLeft, kDeleteWordBackward, kDeleteToBeginningOfIndentedLine, kDeleteToBeginningOfLine, kDeleteToBeginningOfParagraph };
if(deleteActions.find(action) != deleteActions.end())
action = kDeleteSelection;
else if(yankAppendActions.find(action) != yankAppendActions.end())
@@ -768,6 +770,8 @@ namespace ng
case kMoveSubWordRight: _selections = ng::move(_buffer, _selections, kSelectionMoveToEndOfSubWord, layout); break;
case kMoveWordBackward: _selections = ng::move(_buffer, _selections, kSelectionMoveToBeginOfWord, layout); break;
case kMoveWordForward: _selections = ng::move(_buffer, _selections, kSelectionMoveToEndOfWord, layout); break;
case kMoveToBeginningOfIndentedLine: _selections = ng::move(_buffer, _selections, kSelectionMoveToBeginOfIndentedLine, layout); break;
case kMoveToEndOfIndentedLine: _selections = ng::move(_buffer, _selections, kSelectionMoveToEndOfIndentedLine, layout); break;
case kMoveToBeginningOfLine: _selections = ng::move(_buffer, _selections, kSelectionMoveToBeginOfSoftLine, layout); break;
case kMoveToEndOfLine: _selections = ng::move(_buffer, _selections, kSelectionMoveToEndOfSoftLine, layout); break;
case kMoveToBeginningOfParagraph: _selections = ng::move(_buffer, _selections, kSelectionMoveToBeginOfLine, layout); break;
@@ -789,6 +793,8 @@ namespace ng
case kMoveSubWordRightAndModifySelection: _selections = ng::extend(_buffer, _selections, kSelectionExtendToEndOfSubWord, layout); break;
case kMoveWordBackwardAndModifySelection: _selections = ng::extend(_buffer, _selections, kSelectionExtendToBeginOfWord, layout); break;
case kMoveWordForwardAndModifySelection: _selections = ng::extend(_buffer, _selections, kSelectionExtendToEndOfWord, layout); break;
case kMoveToBeginningOfIndentedLineAndModifySelection: _selections = ng::extend(_buffer, _selections, kSelectionExtendToBeginOfIndentedLine, layout); break;
case kMoveToEndOfIndentedLineAndModifySelection: _selections = ng::extend(_buffer, _selections, kSelectionExtendToEndOfIndentedLine, layout); break;
case kMoveToBeginningOfLineAndModifySelection: _selections = ng::extend(_buffer, _selections, kSelectionExtendToBeginOfSoftLine, layout); break;
case kMoveToEndOfLineAndModifySelection: _selections = ng::extend(_buffer, _selections, kSelectionExtendToEndOfSoftLine, layout); break;
case kMoveToBeginningOfParagraphAndModifySelection: _selections = ng::extend(_buffer, _selections, kSelectionExtendToBeginOfParagraph, layout); break;

View File

@@ -30,6 +30,8 @@ namespace ng
kMoveToBeginningOfColumnAndModifySelection,
kMoveToBeginningOfDocument,
kMoveToBeginningOfDocumentAndModifySelection,
kMoveToBeginningOfIndentedLine,
kMoveToBeginningOfIndentedLineAndModifySelection,
kMoveToBeginningOfLine,
kMoveToBeginningOfLineAndModifySelection,
kMoveToBeginningOfParagraph,
@@ -40,6 +42,8 @@ namespace ng
kMoveToEndOfColumnAndModifySelection,
kMoveToEndOfDocument,
kMoveToEndOfDocumentAndModifySelection,
kMoveToEndOfIndentedLine,
kMoveToEndOfIndentedLineAndModifySelection,
kMoveToEndOfLine,
kMoveToEndOfLineAndModifySelection,
kMoveToEndOfParagraph,
@@ -84,8 +88,10 @@ namespace ng
kDeleteForward,
kDeleteSubWordLeft,
kDeleteSubWordRight,
kDeleteToBeginningOfIndentedLine,
kDeleteToBeginningOfLine,
kDeleteToBeginningOfParagraph,
kDeleteToEndOfIndentedLine,
kDeleteToEndOfLine,
kDeleteToEndOfParagraph,
kDeleteWordBackward,

View File

@@ -353,6 +353,15 @@ namespace ng
return res;
}
static size_t end_of_leading_indent (buffer_t const& buffer, size_t line)
{
size_t bol = buffer.begin(line);
size_t eol = buffer.eol(line);
while(bol < eol && isspace(utf8::to_ch(buffer[bol])))
++bol;
return bol;
}
static index_t move (buffer_t const& buffer, index_t const& index, move_unit_type unit, layout_movement_t const* layout)
{
size_t const caret = index.index;
@@ -393,6 +402,22 @@ namespace ng
case kSelectionMovePageUp: return layout ? layout->page_up_for(index) : index;
case kSelectionMovePageDown: return layout ? layout->page_down_for(index) : index;
case kSelectionMoveToBeginOfIndentedLine:
{
size_t bol = buffer.begin(line);
size_t eoi = end_of_leading_indent(buffer, line);
return eoi < caret ? eoi : bol;
}
break;
case kSelectionMoveToEndOfIndentedLine:
{
size_t eoi = end_of_leading_indent(buffer, line);
size_t eol = buffer.eol(line);
return caret < eoi ? eoi : eol;
}
break;
case kSelectionMoveToBeginOfHardParagraph:
{
if(line == 0)
@@ -672,8 +697,8 @@ namespace ng
if(range.unanchored)
{
static std::set<select_unit_type> towardBegin = { kSelectionExtendLeft, kSelectionExtendFreehandedLeft, kSelectionExtendUp, kSelectionExtendToBeginOfWord, kSelectionExtendToBeginOfSubWord, kSelectionExtendToBeginOfSoftLine, kSelectionExtendToBeginOfLine, kSelectionExtendToBeginOfParagraph, kSelectionExtendToBeginOfColumn, kSelectionExtendToBeginOfDocument, kSelectionExtendPageUp };
static std::set<select_unit_type> towardEnd = { kSelectionExtendRight, kSelectionExtendFreehandedRight, kSelectionExtendDown, kSelectionExtendToEndOfWord, kSelectionExtendToEndOfSubWord, kSelectionExtendToEndOfSoftLine, kSelectionExtendToEndOfLine, kSelectionExtendToEndOfParagraph, kSelectionExtendToEndOfColumn, kSelectionExtendToEndOfDocument, kSelectionExtendPageDown };
static std::set<select_unit_type> towardBegin = { kSelectionExtendLeft, kSelectionExtendFreehandedLeft, kSelectionExtendUp, kSelectionExtendToBeginOfWord, kSelectionExtendToBeginOfSubWord, kSelectionExtendToBeginOfSoftLine, kSelectionExtendToBeginOfIndentedLine, kSelectionExtendToBeginOfLine, kSelectionExtendToBeginOfParagraph, kSelectionExtendToBeginOfColumn, kSelectionExtendToBeginOfDocument, kSelectionExtendPageUp };
static std::set<select_unit_type> towardEnd = { kSelectionExtendRight, kSelectionExtendFreehandedRight, kSelectionExtendDown, kSelectionExtendToEndOfWord, kSelectionExtendToEndOfSubWord, kSelectionExtendToEndOfSoftLine, kSelectionExtendToEndOfIndentedLine, kSelectionExtendToEndOfLine, kSelectionExtendToEndOfParagraph, kSelectionExtendToEndOfColumn, kSelectionExtendToEndOfDocument, kSelectionExtendPageDown };
if(first < last && towardBegin.find(unit) != towardBegin.end())
std::swap(first, last);
@@ -701,6 +726,8 @@ namespace ng
case kSelectionExtendToEndOfWord: return range_t(first, move(buffer, last, kSelectionMoveToEndOfWord, layout), range.columnar, range.freehanded);
case kSelectionExtendToBeginOfSubWord: return range_t(first, move(buffer, last, kSelectionMoveToBeginOfSubWord, layout), range.columnar, range.freehanded);
case kSelectionExtendToEndOfSubWord: return range_t(first, move(buffer, last, kSelectionMoveToEndOfSubWord, layout), range.columnar, range.freehanded);
case kSelectionExtendToBeginOfIndentedLine: return range_t(first, move(buffer, last, kSelectionMoveToBeginOfIndentedLine, layout), range.columnar, range.freehanded);
case kSelectionExtendToEndOfIndentedLine: return range_t(first, move(buffer, last, kSelectionMoveToEndOfIndentedLine, layout), range.columnar, range.freehanded);
case kSelectionExtendToBeginOfSoftLine: return range_t(first, move(buffer, last, kSelectionMoveToBeginOfSoftLine, layout), range.columnar, range.freehanded);
case kSelectionExtendToEndOfSoftLine: return range_t(first, move(buffer, last, kSelectionMoveToEndOfSoftLine, layout), range.columnar, range.freehanded);
case kSelectionExtendToBeginOfLine: return range_t(first, move(buffer, last, kSelectionMoveToBeginOfLine, layout), range.columnar, range.freehanded);

View File

@@ -6,8 +6,8 @@
#include <text/types.h>
#include <oak/misc.h>
enum move_unit_type { kSelectionMoveLeft, kSelectionMoveRight, kSelectionMoveFreehandedLeft, kSelectionMoveFreehandedRight, kSelectionMoveUp, kSelectionMoveDown, kSelectionMoveToBeginOfSelection, kSelectionMoveToEndOfSelection, kSelectionMoveToBeginOfSubWord, kSelectionMoveToEndOfSubWord, kSelectionMoveToBeginOfWord, kSelectionMoveToEndOfWord, kSelectionMoveToBeginOfSoftLine, kSelectionMoveToEndOfSoftLine, kSelectionMoveToBeginOfLine, kSelectionMoveToEndOfLine, kSelectionMoveToBeginOfParagraph, kSelectionMoveToEndOfParagraph, kSelectionMoveToBeginOfHardParagraph, kSelectionMoveToEndOfHardParagraph, kSelectionMoveToBeginOfTypingPair, kSelectionMoveToEndOfTypingPair, kSelectionMoveToBeginOfColumn, kSelectionMoveToEndOfColumn, kSelectionMovePageUp, kSelectionMovePageDown, kSelectionMoveToBeginOfDocument, kSelectionMoveToEndOfDocument, kSelectionMoveNowhere };
enum select_unit_type { kSelectionExtendLeft, kSelectionExtendRight, kSelectionExtendFreehandedLeft, kSelectionExtendFreehandedRight, kSelectionExtendUp, kSelectionExtendDown, kSelectionExtendToBeginOfSubWord, kSelectionExtendToEndOfSubWord, kSelectionExtendToBeginOfWord, kSelectionExtendToEndOfWord, kSelectionExtendToBeginOfSoftLine, kSelectionExtendToEndOfSoftLine, kSelectionExtendToBeginOfLine, kSelectionExtendToEndOfLine, kSelectionExtendToBeginOfParagraph, kSelectionExtendToEndOfParagraph, kSelectionExtendToBeginOfTypingPair, kSelectionExtendToEndOfTypingPair, kSelectionExtendToBeginOfColumn, kSelectionExtendToEndOfColumn, kSelectionExtendPageUp, kSelectionExtendPageDown, kSelectionExtendToBeginOfDocument, kSelectionExtendToEndOfDocument, kSelectionExtendToWord, kSelectionExtendToScope, kSelectionExtendToSoftLine, kSelectionExtendToLineExclLF, kSelectionExtendToLine, kSelectionExtendToParagraph, kSelectionExtendToTypingPair, kSelectionExtendToAll };
enum move_unit_type { kSelectionMoveLeft, kSelectionMoveRight, kSelectionMoveFreehandedLeft, kSelectionMoveFreehandedRight, kSelectionMoveUp, kSelectionMoveDown, kSelectionMoveToBeginOfSelection, kSelectionMoveToEndOfSelection, kSelectionMoveToBeginOfSubWord, kSelectionMoveToEndOfSubWord, kSelectionMoveToBeginOfWord, kSelectionMoveToEndOfWord, kSelectionMoveToBeginOfSoftLine, kSelectionMoveToEndOfSoftLine, kSelectionMoveToBeginOfIndentedLine, kSelectionMoveToEndOfIndentedLine, kSelectionMoveToBeginOfLine, kSelectionMoveToEndOfLine, kSelectionMoveToBeginOfParagraph, kSelectionMoveToEndOfParagraph, kSelectionMoveToBeginOfHardParagraph, kSelectionMoveToEndOfHardParagraph, kSelectionMoveToBeginOfTypingPair, kSelectionMoveToEndOfTypingPair, kSelectionMoveToBeginOfColumn, kSelectionMoveToEndOfColumn, kSelectionMovePageUp, kSelectionMovePageDown, kSelectionMoveToBeginOfDocument, kSelectionMoveToEndOfDocument, kSelectionMoveNowhere };
enum select_unit_type { kSelectionExtendLeft, kSelectionExtendRight, kSelectionExtendFreehandedLeft, kSelectionExtendFreehandedRight, kSelectionExtendUp, kSelectionExtendDown, kSelectionExtendToBeginOfSubWord, kSelectionExtendToEndOfSubWord, kSelectionExtendToBeginOfWord, kSelectionExtendToEndOfWord, kSelectionExtendToBeginOfSoftLine, kSelectionExtendToEndOfSoftLine, kSelectionExtendToBeginOfIndentedLine, kSelectionExtendToEndOfIndentedLine, kSelectionExtendToBeginOfLine, kSelectionExtendToEndOfLine, kSelectionExtendToBeginOfParagraph, kSelectionExtendToEndOfParagraph, kSelectionExtendToBeginOfTypingPair, kSelectionExtendToEndOfTypingPair, kSelectionExtendToBeginOfColumn, kSelectionExtendToEndOfColumn, kSelectionExtendPageUp, kSelectionExtendPageDown, kSelectionExtendToBeginOfDocument, kSelectionExtendToEndOfDocument, kSelectionExtendToWord, kSelectionExtendToScope, kSelectionExtendToSoftLine, kSelectionExtendToLineExclLF, kSelectionExtendToLine, kSelectionExtendToParagraph, kSelectionExtendToTypingPair, kSelectionExtendToAll };
namespace scope { struct context_t; struct selector_t; }

View File

@@ -0,0 +1,62 @@
#include <buffer/buffer.h>
#include <selection/selection.h>
class IndentMovementTests : public CxxTest::TestSuite
{
ng::buffer_t _buffer;
std::string move (size_t caret, move_unit_type unit)
{
return to_s(_buffer, ng::move(_buffer, ng::range_t(caret), unit));
}
std::string extend (size_t caret, select_unit_type unit)
{
return to_s(_buffer, ng::extend(_buffer, ng::range_t(caret), unit).last().sorted());
}
public:
void setUp ()
{
if(_buffer.empty())
_buffer.insert(0, " indented line\n");
}
void test_indent_begin_movement ()
{
TS_ASSERT_EQUALS(move(0, kSelectionMoveToBeginOfIndentedLine), "1");
TS_ASSERT_EQUALS(move(2, kSelectionMoveToBeginOfIndentedLine), "1");
TS_ASSERT_EQUALS(move(4, kSelectionMoveToBeginOfIndentedLine), "1");
TS_ASSERT_EQUALS(move(6, kSelectionMoveToBeginOfIndentedLine), "1:5");
TS_ASSERT_EQUALS(move(8, kSelectionMoveToBeginOfIndentedLine), "1:5");
}
void test_indent_end_movement ()
{
TS_ASSERT_EQUALS(move( 0, kSelectionMoveToEndOfIndentedLine), "1:5");
TS_ASSERT_EQUALS(move( 2, kSelectionMoveToEndOfIndentedLine), "1:5");
TS_ASSERT_EQUALS(move( 4, kSelectionMoveToEndOfIndentedLine), "1:18");
TS_ASSERT_EQUALS(move( 6, kSelectionMoveToEndOfIndentedLine), "1:18");
TS_ASSERT_EQUALS(move( 8, kSelectionMoveToEndOfIndentedLine), "1:18");
TS_ASSERT_EQUALS(move(17, kSelectionMoveToEndOfIndentedLine), "1:18");
}
void test_indent_begin_selection ()
{
TS_ASSERT_EQUALS(extend(0, kSelectionExtendToBeginOfIndentedLine), "1");
TS_ASSERT_EQUALS(extend(2, kSelectionExtendToBeginOfIndentedLine), "1-1:3");
TS_ASSERT_EQUALS(extend(4, kSelectionExtendToBeginOfIndentedLine), "1-1:5");
TS_ASSERT_EQUALS(extend(6, kSelectionExtendToBeginOfIndentedLine), "1:5-1:7");
TS_ASSERT_EQUALS(extend(8, kSelectionExtendToBeginOfIndentedLine), "1:5-1:9");
}
void test_indent_end_selection ()
{
TS_ASSERT_EQUALS(extend( 0, kSelectionExtendToEndOfIndentedLine), "1-1:5");
TS_ASSERT_EQUALS(extend( 2, kSelectionExtendToEndOfIndentedLine), "1:3-1:5");
TS_ASSERT_EQUALS(extend( 4, kSelectionExtendToEndOfIndentedLine), "1:5-1:18");
TS_ASSERT_EQUALS(extend( 6, kSelectionExtendToEndOfIndentedLine), "1:7-1:18");
TS_ASSERT_EQUALS(extend( 8, kSelectionExtendToEndOfIndentedLine), "1:9-1:18");
TS_ASSERT_EQUALS(extend(17, kSelectionExtendToEndOfIndentedLine), "1:18");
}
};