diff options
-rw-r--r-- | doc/ScintillaDoc.html | 19 | ||||
-rw-r--r-- | doc/ScintillaHistory.html | 12 | ||||
-rw-r--r-- | include/Scintilla.h | 6 | ||||
-rw-r--r-- | include/Scintilla.iface | 20 | ||||
-rw-r--r-- | src/Document.cxx | 2 | ||||
-rw-r--r-- | src/Document.h | 8 | ||||
-rw-r--r-- | src/Editor.cxx | 75 | ||||
-rw-r--r-- | src/Editor.h | 2 | ||||
-rw-r--r-- | test/simpleTests.py | 54 |
9 files changed, 188 insertions, 10 deletions
diff --git a/doc/ScintillaDoc.html b/doc/ScintillaDoc.html index 6e4210bc9..3e40adab2 100644 --- a/doc/ScintillaDoc.html +++ b/doc/ScintillaDoc.html @@ -641,6 +641,7 @@ struct Sci_TextRange { <a class="message" href="#SCI_GETTARGETEND">SCI_GETTARGETEND</a><br /> <a class="message" href="#SCI_SETTARGETRANGE">SCI_SETTARGETRANGE(int start, int end)</a><br /> <a class="message" href="#SCI_TARGETFROMSELECTION">SCI_TARGETFROMSELECTION</a><br /> + <a class="message" href="#SCI_TARGETWHOLEDOCUMENT">SCI_TARGETWHOLEDOCUMENT</a><br /> <a class="message" href="#SCI_SETSEARCHFLAGS">SCI_SETSEARCHFLAGS(int searchFlags)</a><br /> <a class="message" href="#SCI_GETSEARCHFLAGS">SCI_GETSEARCHFLAGS</a><br /> <a class="message" href="#SCI_SEARCHINTARGET">SCI_SEARCHINTARGET(int length, const char @@ -667,6 +668,9 @@ struct Sci_TextRange { <p><b id="SCI_TARGETFROMSELECTION">SCI_TARGETFROMSELECTION</b><br /> Set the target start and end to the start and end positions of the selection.</p> + <p><b id="SCI_TARGETWHOLEDOCUMENT">SCI_TARGETWHOLEDOCUMENT</b><br /> + Set the target start to the start of the document and target end to the end of the document.</p> + <p><b id="SCI_SETSEARCHFLAGS">SCI_SETSEARCHFLAGS(int searchFlags)</b><br /> <b id="SCI_GETSEARCHFLAGS">SCI_GETSEARCHFLAGS</b><br /> These get and set the <a class="jump" href="#searchFlags"><code>searchFlags</code></a> used by @@ -1175,6 +1179,7 @@ struct Sci_TextToFind { onlyWordCharacters)</a><br /> <a class="message" href="#SCI_WORDSTARTPOSITION">SCI_WORDSTARTPOSITION(int position, bool onlyWordCharacters)</a><br /> + <a class="message" href="#SCI_ISRANGEWORD">SCI_ISRANGEWORD(int start, int end)</a><br /> <a class="message" href="#SCI_POSITIONBEFORE">SCI_POSITIONBEFORE(int position)</a><br /> <a class="message" href="#SCI_POSITIONAFTER">SCI_POSITIONAFTER(int position)</a><br /> <a class="message" href="#SCI_POSITIONRELATIVE">SCI_POSITIONRELATIVE(int position, int relative)</a><br /> @@ -1399,6 +1404,12 @@ struct Sci_TextToFind { sets the start or the search, which is forwards when searching for the end and backwards when searching for the start.</p> + <p><b id="SCI_ISRANGEWORD">SCI_ISRANGEWORD(int start, int end)</b><br /> + Is the range start..end a word or set of words? This message checks that start is at a word start transition and that + end is at a word end transition. It does not check whether there are any spaces inside the range.</p> + + <a class="message" href="#SCI_ISRANGEWORD">SCI_ISRANGEWORD(int start, int end)</a><br /> + <p>Set <code>onlyWordCharacters</code> to <code>true</code> (1) to stop searching at the first non-word character in the search direction. If <code>onlyWordCharacters</code> is <code>false</code> (0), the first character in the search direction sets the type of the search @@ -1624,6 +1635,7 @@ struct Sci_TextToFind { <a class="message" href="#SCI_SWAPMAINANCHORCARET">SCI_SWAPMAINANCHORCARET</a><br /> <a class="message" href="#SCI_ROTATESELECTION">SCI_ROTATESELECTION</a><br /> + <a class="message" href="#SCI_MULTIPLESELECTADDNEXT">SCI_MULTIPLESELECTADDNEXT</a><br /> </code> <p> @@ -1789,9 +1801,14 @@ struct Sci_TextToFind { <p> <b id="SCI_SWAPMAINANCHORCARET">SCI_SWAPMAINANCHORCARET</b><br /> <b id="SCI_ROTATESELECTION">SCI_ROTATESELECTION</b><br /> + <b id="SCI_MULTIPLESELECTADDNEXT">SCI_MULTIPLESELECTADDNEXT</b><br /> These commands may be assigned to keys to make it possible to manipulate multiple selections. <code>SCI_SWAPMAINANCHORCARET</code> moves the caret to the opposite end of the main selection. - <code>SCI_ROTATESELECTION</code> makes the next selection be the main selection. + <code>SCI_ROTATESELECTION</code> makes the next selection be the main selection.<br /> + <code>SCI_MULTIPLESELECTADDNEXT</code> adds the next occurrence of the main selection to + the set of selections as main. If the current selection is empty then select word around caret. + The current <a class="jump" href="#searchFlags"><code>searchFlags</code></a> + are used so the application may choose case sensitivity and word search options. </p> <h2 id="ScrollingAndAutomaticScrolling">Scrolling and automatic scrolling</h2> diff --git a/doc/ScintillaHistory.html b/doc/ScintillaHistory.html index 67a5927da..9ffcaaacc 100644 --- a/doc/ScintillaHistory.html +++ b/doc/ScintillaHistory.html @@ -491,6 +491,18 @@ Released 26 May 2015. </li> <li> + Added SCI_MULTIPLESELECTADDNEXT to add the next occurrence of the main selection within the + target to the set of selections as main. If the current selection is empty then select word around caret. + SCI_MULTIPLESELECTADDNEXT adds each occurrence of the main selection within the + target to the set of selections. + </li> + <li> + Added SCI_ISRANGEWORD to determine if the parameters are at the start and end of a word. + </li> + <li> + Added SCI_TARGETWHOLEDOCUMENT to set the target to the whole document. + </li> + <li> Verilog lexer recognises protected regions and the folder folds protected regions. </li> <li> diff --git a/include/Scintilla.h b/include/Scintilla.h index 22d36d34d..50deb755e 100644 --- a/include/Scintilla.h +++ b/include/Scintilla.h @@ -434,6 +434,8 @@ typedef sptr_t (*SciFnDirect)(sptr_t ptr, unsigned int iMessage, uptr_t wParam, #define SCI_GETTARGETEND 2193 #define SCI_SETTARGETRANGE 2686 #define SCI_GETTARGETTEXT 2687 +#define SCI_TARGETFROMSELECTION 2287 +#define SCI_TARGETWHOLEDOCUMENT 2690 #define SCI_REPLACETARGET 2194 #define SCI_REPLACETARGETRE 2195 #define SCI_SEARCHINTARGET 2197 @@ -498,6 +500,7 @@ typedef sptr_t (*SciFnDirect)(sptr_t ptr, unsigned int iMessage, uptr_t wParam, #define SCI_GETMOUSEDWELLTIME 2265 #define SCI_WORDSTARTPOSITION 2266 #define SCI_WORDENDPOSITION 2267 +#define SCI_ISRANGEWORD 2691 #define SC_WRAP_NONE 0 #define SC_WRAP_WORD 1 #define SC_WRAP_CHAR 2 @@ -559,7 +562,6 @@ typedef sptr_t (*SciFnDirect)(sptr_t ptr, unsigned int iMessage, uptr_t wParam, #define SCI_SETMULTIPASTE 2614 #define SCI_GETMULTIPASTE 2615 #define SCI_GETTAG 2616 -#define SCI_TARGETFROMSELECTION 2287 #define SCI_LINESJOIN 2288 #define SCI_LINESSPLIT 2289 #define SCI_SETFOLDMARGINCOLOUR 2290 @@ -894,6 +896,8 @@ typedef sptr_t (*SciFnDirect)(sptr_t ptr, unsigned int iMessage, uptr_t wParam, #define SCI_GETADDITIONALCARETFORE 2605 #define SCI_ROTATESELECTION 2606 #define SCI_SWAPMAINANCHORCARET 2607 +#define SCI_MULTIPLESELECTADDNEXT 2688 +#define SCI_MULTIPLESELECTADDEACH 2689 #define SCI_CHANGELEXERSTATE 2617 #define SCI_CONTRACTEDFOLDNEXT 2618 #define SCI_VERTICALCENTRECARET 2619 diff --git a/include/Scintilla.iface b/include/Scintilla.iface index 8cd651540..9a084785a 100644 --- a/include/Scintilla.iface +++ b/include/Scintilla.iface @@ -1069,6 +1069,12 @@ fun void SetTargetRange=2686(position start, position end) # Retrieve the text in the target. get int GetTargetText=2687(, stringresult characters) +# Make the target range start and end be the same as the selection range start and end. +fun void TargetFromSelection=2287(,) + +# Sets the target to the whole document. +fun void TargetWholeDocument=2690(,) + # Replace the target text with the argument text. # Text is counted so it can contain NULs. # Returns the length of the replacement text. @@ -1248,6 +1254,9 @@ fun int WordStartPosition=2266(position pos, bool onlyWordCharacters) # Get position of end of word. fun int WordEndPosition=2267(position pos, bool onlyWordCharacters) +# Is the range start..end considered a word? +fun bool IsRangeWord=2691(position start, position end) + enu Wrap=SC_WRAP_ val SC_WRAP_NONE=0 val SC_WRAP_WORD=1 @@ -1403,9 +1412,6 @@ get int GetMultiPaste=2615(,) # Result is NUL-terminated. get int GetTag=2616(int tagNumber, stringresult tagValue) -# Make the target range start and end be the same as the selection range start and end. -fun void TargetFromSelection=2287(,) - # Join the lines in the target. fun void LinesJoin=2288(,) @@ -2350,6 +2356,14 @@ fun void RotateSelection=2606(,) # Swap that caret and anchor of the main selection. fun void SwapMainAnchorCaret=2607(,) +# Add the next occurrence of the main selection to the set of selections as main. +# If the current selection is empty then select word around caret. +fun void MultipleSelectAddNext=2688(,) + +# Add each occurrence of the main selection in the target to the set of selections. +# If the current selection is empty then select word around caret. +fun void MultipleSelectAddEach=2689(,) + # Indicate that the internal state of a lexer has changed over a range and therefore # there may be a need to redraw. fun int ChangeLexerState=2617(position start, position end) diff --git a/src/Document.cxx b/src/Document.cxx index 3065b1828..2eb412f73 100644 --- a/src/Document.cxx +++ b/src/Document.cxx @@ -1603,7 +1603,7 @@ bool Document::IsWordEndAt(int pos) const { * ends and where the characters on the inside are word or punctuation characters. */ bool Document::IsWordAt(int start, int end) const { - return IsWordStartAt(start) && IsWordEndAt(end); + return (start < end) && IsWordStartAt(start) && IsWordEndAt(end); } bool Document::MatchesWordOptions(bool word, bool wordStart, int pos, int length) const { diff --git a/src/Document.h b/src/Document.h index 923462e7a..308a67062 100644 --- a/src/Document.h +++ b/src/Document.h @@ -381,6 +381,10 @@ public: }; CharacterExtracted ExtractCharacter(int position) const; + bool IsWordStartAt(int pos) const; + bool IsWordEndAt(int pos) const; + bool IsWordAt(int start, int end) const; + bool MatchesWordOptions(bool word, bool wordStart, int pos, int length) const; bool HasCaseFolder(void) const; void SetCaseFolder(CaseFolder *pcf_); @@ -437,10 +441,6 @@ public: int BraceMatch(int position, int maxReStyle); private: - bool IsWordStartAt(int pos) const; - bool IsWordEndAt(int pos) const; - bool IsWordAt(int start, int end) const; - void NotifyModifyAttempt(); void NotifySavePoint(bool atSavePoint); void NotifyModified(DocModification mh); diff --git a/src/Editor.cxx b/src/Editor.cxx index ce465808c..beb270219 100644 --- a/src/Editor.cxx +++ b/src/Editor.cxx @@ -692,6 +692,59 @@ void Editor::SetEmptySelection(int currentPos_) { SetEmptySelection(SelectionPosition(currentPos_)); } +void Editor::MultipleSelectAdd(AddNumber addNumber) { + if (SelectionEmpty() || !multipleSelection) { + // Select word at caret + const int startWord = pdoc->ExtendWordSelect(sel.MainCaret(), -1, true); + const int endWord = pdoc->ExtendWordSelect(startWord, 1, true); + TrimAndSetSelection(endWord, startWord); + + } else { + + if (!pdoc->HasCaseFolder()) + pdoc->SetCaseFolder(CaseFolderForEncoding()); + + const Range rangeMainSelection(sel.RangeMain().Start().Position(), sel.RangeMain().End().Position()); + const std::string selectedText = RangeText(rangeMainSelection.start, rangeMainSelection.end); + + const Range rangeTarget(targetStart, targetEnd); + std::vector<Range> searchRanges; + // Search should be over the target range excluding the current selection so + // may need to search 2 ranges, after the selection then before the selection. + if (rangeTarget.Overlaps(rangeMainSelection)) { + // Common case is that the selection is completely within the target but + // may also have overlap at start or end. + if (rangeMainSelection.end < rangeTarget.end) + searchRanges.push_back(Range(rangeMainSelection.end, rangeTarget.end)); + if (rangeTarget.start < rangeMainSelection.start) + searchRanges.push_back(Range(rangeTarget.start, rangeMainSelection.start)); + } else { + // No overlap + searchRanges.push_back(rangeTarget); + } + + for (std::vector<Range>::const_iterator it = searchRanges.begin(); it != searchRanges.end(); ++it) { + int searchStart = it->start; + const int searchEnd = it->end; + for (;;) { + int lengthFound = selectedText.length(); + int pos = pdoc->FindText(searchStart, searchEnd, selectedText.c_str(), + searchFlags, &lengthFound); + if (pos >= 0) { + sel.AddSelection(SelectionRange(pos + lengthFound, pos)); + ScrollRange(sel.RangeMain()); + Redraw(); + if (addNumber == addOne) + return; + searchStart = pos + lengthFound; + } else { + break; + } + } + } + } +} + bool Editor::RangeContainsProtected(int start, int end) const { if (vs.ProtectionActive()) { if (start > end) { @@ -2895,6 +2948,12 @@ void Editor::Duplicate(bool forLine) { void Editor::CancelModes() { sel.SetMoveExtends(false); + if (sel.Count() > 1) { + // Drop additional selections + const SelectionRange rangeOnly = sel.RangeMain(); + InvalidateSelection(rangeOnly, true); + sel.SetSelection(rangeOnly); + } } void Editor::NewLine() { @@ -5532,6 +5591,11 @@ sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) { targetEnd = static_cast<int>(lParam); break; + case SCI_TARGETWHOLEDOCUMENT: + targetStart = 0; + targetEnd = pdoc->Length(); + break; + case SCI_TARGETFROMSELECTION: if (sel.MainCaret() < sel.MainAnchor()) { targetStart = sel.MainCaret(); @@ -6113,6 +6177,9 @@ sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) { case SCI_WORDENDPOSITION: return pdoc->ExtendWordSelect(static_cast<int>(wParam), 1, lParam != 0); + case SCI_ISRANGEWORD: + return pdoc->IsWordAt(static_cast<int>(wParam), lParam); + case SCI_SETWRAPMODE: if (vs.SetWrapState(static_cast<int>(wParam))) { xOffset = 0; @@ -7636,6 +7703,14 @@ sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) { sel.RangeMain() = SelectionRange(sel.RangeMain().anchor, sel.RangeMain().caret); break; + case SCI_MULTIPLESELECTADDNEXT: + MultipleSelectAdd(addOne); + break; + + case SCI_MULTIPLESELECTADDEACH: + MultipleSelectAdd(addEach); + break; + case SCI_CHANGELEXERSTATE: pdoc->ChangeLexerState(static_cast<int>(wParam), static_cast<int>(lParam)); break; diff --git a/src/Editor.h b/src/Editor.h index 2bf8336fa..b08fc7159 100644 --- a/src/Editor.h +++ b/src/Editor.h @@ -319,6 +319,8 @@ protected: // ScintillaBase subclass needs access to much of Editor void SetSelection(int currentPos_); void SetEmptySelection(SelectionPosition currentPos_); void SetEmptySelection(int currentPos_); + enum AddNumber { addOne, addEach }; + void MultipleSelectAdd(AddNumber addNumber); bool RangeContainsProtected(int start, int end) const; bool SelectionContainsProtected(); int MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd=true) const; diff --git a/test/simpleTests.py b/test/simpleTests.py index 4e8462c71..9b7525066 100644 --- a/test/simpleTests.py +++ b/test/simpleTests.py @@ -576,6 +576,14 @@ class TestSimple(unittest.TestCase): self.assertEquals(self.ed.TargetStart, 4) self.assertEquals(self.ed.TargetEnd, 5) + def testTargetWhole(self): + self.ed.SetContents(b"abcd") + self.ed.TargetStart = 1 + self.ed.TargetEnd = 3 + self.ed.TargetWholeDocument() + self.assertEquals(self.ed.TargetStart, 0) + self.assertEquals(self.ed.TargetEnd, 4) + def testTargetEscape(self): # Checks that a literal \ can be in the replacement. Bug #2959876 self.ed.SetContents(b"abcd") @@ -619,6 +627,18 @@ class TestSimple(unittest.TestCase): self.assertEquals(self.ed.WordEndPosition(5, 0), 6) self.assertEquals(self.ed.WordEndPosition(6, 0), 8) + def testWordRange(self): + text = b"ab cd\t++" + self.ed.AddText(len(text), text) + self.assertEquals(self.ed.IsRangeWord(0, 0), 0) + self.assertEquals(self.ed.IsRangeWord(0, 1), 0) + self.assertEquals(self.ed.IsRangeWord(0, 2), 1) + self.assertEquals(self.ed.IsRangeWord(0, 3), 0) + self.assertEquals(self.ed.IsRangeWord(0, 4), 0) + self.assertEquals(self.ed.IsRangeWord(0, 5), 1) + self.assertEquals(self.ed.IsRangeWord(6, 7), 0) + self.assertEquals(self.ed.IsRangeWord(6, 8), 1) + MODI = 1 UNDO = 2 REDO = 4 @@ -1107,6 +1127,40 @@ class TestSearch(unittest.TestCase): self.assertEquals(-1, self.ed.FindBytes(0, self.ed.Length, b"\\xAB", flags)) self.assertEquals(0, self.ed.FindBytes(0, self.ed.Length, b"\\xAD", flags)) + def testMultipleAddSelection(self): + # Find both 'a' + self.assertEquals(self.ed.MultipleSelection, 0) + self.ed.MultipleSelection = 1 + self.assertEquals(self.ed.MultipleSelection, 1) + self.ed.TargetWholeDocument() + self.ed.SearchFlags = 0 + self.ed.SetSelection(1, 0) + self.assertEquals(self.ed.Selections, 1) + self.ed.MultipleSelectAddNext() + self.assertEquals(self.ed.Selections, 2) + self.assertEquals(self.ed.GetSelectionNAnchor(0), 0) + self.assertEquals(self.ed.GetSelectionNCaret(0), 1) + self.assertEquals(self.ed.GetSelectionNAnchor(1), 8) + self.assertEquals(self.ed.GetSelectionNCaret(1), 9) + self.ed.MultipleSelection = 0 + + def testMultipleAddEachSelection(self): + # Find each 'b' + self.assertEquals(self.ed.MultipleSelection, 0) + self.ed.MultipleSelection = 1 + self.assertEquals(self.ed.MultipleSelection, 1) + self.ed.TargetWholeDocument() + self.ed.SearchFlags = 0 + self.ed.SetSelection(3, 2) + self.assertEquals(self.ed.Selections, 1) + self.ed.MultipleSelectAddEach() + self.assertEquals(self.ed.Selections, 2) + self.assertEquals(self.ed.GetSelectionNAnchor(0), 2) + self.assertEquals(self.ed.GetSelectionNCaret(0), 3) + self.assertEquals(self.ed.GetSelectionNAnchor(1), 6) + self.assertEquals(self.ed.GetSelectionNCaret(1), 7) + self.ed.MultipleSelection = 0 + class TestRepresentations(unittest.TestCase): def setUp(self): |