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):  | 
