Revision | 466d227e850ff97db16e8d30194a84d56c17771c (tree) |
---|---|
Time | 2017-08-18 01:39:38 |
Author | <exeal@user...> |
Removed kernel.positions namespace and moved its members into kernel.locations.
Added kernel.locations.makePointProxy free function.
@@ -316,17 +316,6 @@ | ||
316 | 316 | const Region& region, const text::Newline& newline = text::Newline::USE_INTRINSIC_VALUE); |
317 | 317 | /// @} |
318 | 318 | |
319 | - namespace positions { | |
320 | - Index absoluteOffset(const Document& document, const Position& at, bool fromAccessibleStart); | |
321 | -// bool isOutsideOfAccessibleRegion(const Document& document, const Position& position) BOOST_NOEXCEPT; | |
322 | - bool isOutsideOfDocumentRegion(const Document& document, const Position& position) BOOST_NOEXCEPT; | |
323 | - Position shrinkToAccessibleRegion(const Document& document, const Position& position) BOOST_NOEXCEPT; | |
324 | - Region shrinkToAccessibleRegion(const Document& document, const Region& region) BOOST_NOEXCEPT; | |
325 | - Position shrinkToDocumentRegion(const Document& document, const Position& position) BOOST_NOEXCEPT; | |
326 | - Region shrinkToDocumentRegion(const Document& document, const Region& region) BOOST_NOEXCEPT; | |
327 | - Position updatePosition(const Position& position, const DocumentChange& change, Direction gravity) BOOST_NOEXCEPT; | |
328 | - } // namespace positions | |
329 | - | |
330 | 319 | |
331 | 320 | // inline implementation ////////////////////////////////////////////////////////////////////////////////////// |
332 | 321 |
@@ -365,13 +354,6 @@ | ||
365 | 354 | return document.replace(Region::makeEmpty(at), text); |
366 | 355 | } |
367 | 356 | |
368 | - /// Returns @c true if the given position is outside of the document. | |
369 | - inline bool positions::isOutsideOfDocumentRegion( | |
370 | - const Document& document, const Position& position) BOOST_NOEXCEPT { | |
371 | - return line(position) >= document.numberOfLines() | |
372 | - || offsetInLine(position) > document.lineLength(line(position)); | |
373 | - } | |
374 | - | |
375 | 357 | /** |
376 | 358 | * @overload |
377 | 359 | * @tparam InputIterator UTF-16 character sequence type |
@@ -404,45 +386,6 @@ | ||
404 | 386 | return replace(region, boost::const_begin(text), boost::const_end(text)); |
405 | 387 | } |
406 | 388 | |
407 | - /** | |
408 | - * Shrinks the given position into the accessible region of the document. | |
409 | - * @param document The document | |
410 | - * @param position The source position. This value can be outside of the document | |
411 | - * @return The result | |
412 | - */ | |
413 | - inline Position positions::shrinkToAccessibleRegion(const Document& document, const Position& position) BOOST_NOEXCEPT { | |
414 | - if(!document.isNarrowed()) | |
415 | - return shrinkToDocumentRegion(document, position); | |
416 | - const Region accessibleRegion(document.accessibleRegion()); | |
417 | - if(position < *boost::const_begin(accessibleRegion)) | |
418 | - return *boost::const_begin(accessibleRegion); | |
419 | - else if(position > *boost::const_end(accessibleRegion)) | |
420 | - return *boost::const_end(accessibleRegion); | |
421 | - return Position(line(position), std::min(offsetInLine(position), document.lineLength(line(position)))); | |
422 | - } | |
423 | - | |
424 | - /** | |
425 | - * Shrinks the given region into the accessible region of the document. | |
426 | - * @param document The document | |
427 | - * @param region The source region. This value can intersect with outside of the document | |
428 | - * @return The result. This may not be normalized | |
429 | - */ | |
430 | - inline Region positions::shrinkToAccessibleRegion(const Document& document, const Region& region) BOOST_NOEXCEPT { | |
431 | - return Region(shrinkToAccessibleRegion(document, *boost::const_begin(region)), shrinkToAccessibleRegion(document, *boost::const_end(region))); | |
432 | - } | |
433 | - | |
434 | - /// Shrinks the given position into the document region. | |
435 | - inline Position positions::shrinkToDocumentRegion(const Document& document, const Position& position) BOOST_NOEXCEPT { | |
436 | - Position p(std::min(line(position), document.numberOfLines() - 1), 0); | |
437 | - p.offsetInLine = std::min(offsetInLine(position), document.lineLength(line(p))); | |
438 | - return p; | |
439 | - } | |
440 | - | |
441 | - /// Shrinks the given region into the document region. The result may not be normalized. | |
442 | - inline Region positions::shrinkToDocumentRegion(const Document& document, const Region& region) BOOST_NOEXCEPT { | |
443 | - return Region(shrinkToDocumentRegion(document, *boost::const_begin(region)), shrinkToDocumentRegion(document, *boost::const_end(region))); | |
444 | - } | |
445 | - | |
446 | 389 | /// Returns the bookmarker of the document. |
447 | 390 | inline Bookmarker& Document::bookmarker() BOOST_NOEXCEPT {return *bookmarker_;} |
448 | 391 |
@@ -11,25 +11,25 @@ | ||
11 | 11 | #define ASCENSION_LOCATIONS_HPP |
12 | 12 | #include <ascension/direction.hpp> |
13 | 13 | #include <ascension/corelib/text/code-point.hpp> |
14 | -#include <ascension/kernel/position.hpp> | |
14 | +#include <ascension/kernel/region.hpp> | |
15 | 15 | #include <utility> // std.pair |
16 | 16 | |
17 | 17 | namespace ascension { |
18 | 18 | namespace kernel { |
19 | 19 | class Document; |
20 | + class DocumentChange; | |
20 | 21 | |
21 | 22 | /** |
22 | 23 | * Provides several functions related to locations in document. |
23 | - * Many functions in this namespace take a @c std#pair<const Document&, const Position&> which | |
24 | - * describes a position in the document. @c Point and @c viewer#VisualPoint classes have conversion operators | |
25 | - * into this type. | |
24 | + * Many functions in this namespace take a @c PointProxy which describes a position in the document. @c Point | |
25 | + * and @c viewer#VisualPoint classes have conversion operators into this type. | |
26 | 26 | * @note All functions are *affected* by accessible region of the document. |
27 | 27 | * @see viewer#locations |
28 | 28 | */ |
29 | 29 | namespace locations { |
30 | 30 | /// Character unit defines what is one character. |
31 | 31 | enum CharacterUnit { |
32 | - UTF16_CODE_UNIT, ///< UTF-16 code unit. | |
32 | + UTF16_CODE_UNIT, ///< UTF-16 code unit. A surrogate pair is treated as two characters. | |
33 | 33 | UTF32_CODE_UNIT, ///< UTF-32 code unit. A surrogate pair is treated as one character. |
34 | 34 | GRAPHEME_CLUSTER, ///< A grapheme cluster is a character. |
35 | 35 | GLYPH_CLUSTER ///< A glyph is a character (not implemented). |
@@ -38,21 +38,59 @@ | ||
38 | 38 | /// Describes a position in the document. |
39 | 39 | typedef std::pair<const Document&, Position> PointProxy; |
40 | 40 | |
41 | + /** | |
42 | + * Returns a @c PointProxy from the given document and position. | |
43 | + * @param document The document | |
44 | + * @param position The position | |
45 | + * @return A @c PointProxy | |
46 | + */ | |
47 | + inline PointProxy makePointProxy(const Document& document, const Position& position) BOOST_NOEXCEPT { | |
48 | + return std::make_pair(std::ref(document), position); | |
49 | + } | |
50 | + | |
51 | + /// @defgroup special_locations_in_document Special Locations in Document | |
52 | + /// @note All functions are *affected* by accessible region of the document. | |
53 | + /// @{ | |
41 | 54 | Position beginningOfDocument(const PointProxy& p); |
42 | 55 | Position beginningOfLine(const PointProxy& p); |
43 | - CodePoint characterAt(const PointProxy& p, bool useLineFeed = false); | |
44 | 56 | Position endOfDocument(const PointProxy& p); |
45 | 57 | Position endOfLine(const PointProxy& p); |
46 | 58 | bool isBeginningOfDocument(const PointProxy& p); |
47 | 59 | bool isBeginningOfLine(const PointProxy& p); |
48 | 60 | bool isEndOfDocument(const PointProxy& p); |
49 | 61 | bool isEndOfLine(const PointProxy& p); |
62 | + /// @} | |
63 | + | |
64 | + /// @defgroup motions_in_document Motions in Document | |
65 | + /// @note All functions are *affected* by accessible region of the document. | |
66 | + /// @{ | |
50 | 67 | boost::optional<Position> nextBookmark(const PointProxy& p, Direction direction, Index marks = 1); |
51 | - Position nextCharacter(const PointProxy& p, | |
52 | - Direction direction, CharacterUnit characterUnit, Index offset = 1); | |
68 | + Position nextCharacter(const PointProxy& p, Direction direction, CharacterUnit characterUnit, Index offset = 1); | |
53 | 69 | Position nextLine(const PointProxy& p, Direction direction, Index lines = 1); |
54 | 70 | Position nextWord(const PointProxy& p, Direction direction, Index words = 1); |
55 | 71 | Position nextWordEnd(const PointProxy& p, Direction direction, Index words = 1); |
72 | + /// @} | |
73 | + | |
74 | + /// @defgroup regions_of_document Regions of Document | |
75 | + /// @{ | |
76 | + bool isOutsideOfDocumentRegion(const PointProxy& p) BOOST_NOEXCEPT; | |
77 | + Position shrinkToDocumentRegion(const PointProxy& p) BOOST_NOEXCEPT; | |
78 | + Region shrinkToDocumentRegion(const Document& document, const Region& region) BOOST_NOEXCEPT; | |
79 | + /// @} | |
80 | + | |
81 | + /// @defgroup accessible_regions_of_document Accessible Regions of Document | |
82 | + /// @{ | |
83 | +// bool isOutsideOfAccessibleRegion(const PointProxy& p) BOOST_NOEXCEPT; | |
84 | + Position shrinkToAccessibleRegion(const PointProxy& p) BOOST_NOEXCEPT; | |
85 | + Region shrinkToAccessibleRegion(const Document& document, const Region& region) BOOST_NOEXCEPT; | |
86 | + /// @} | |
87 | + | |
88 | + /// @defgroup miscellaneous_locational_functions Miscellaneous Locational Functions | |
89 | + /// @{ | |
90 | + Index absoluteOffset(const PointProxy& p, bool fromAccessibleStart); | |
91 | + CodePoint characterAt(const PointProxy& p, bool useLineFeed = false); | |
92 | + Position updatePosition(const Position& position, const DocumentChange& change, Direction gravity) BOOST_NOEXCEPT; | |
93 | + /// @} | |
56 | 94 | } |
57 | 95 | } |
58 | 96 | } |
@@ -150,7 +150,7 @@ | ||
150 | 150 | close(); |
151 | 151 | completionSession_->replacementRegion = kernel::Region( |
152 | 152 | *boost::const_begin(completionSession_->replacementRegion), |
153 | - kernel::positions::updatePosition(*boost::const_end(completionSession_->replacementRegion), change, Direction::forward())); | |
153 | + kernel::locations::updatePosition(*boost::const_end(completionSession_->replacementRegion), change, Direction::forward())); | |
154 | 154 | if(!boost::empty(change.insertedRegion()) && !encompasses(replacementRegion, change.insertedRegion())) |
155 | 155 | close(); |
156 | 156 |
@@ -82,81 +82,6 @@ | ||
82 | 82 | return out.flush(); |
83 | 83 | } |
84 | 84 | |
85 | - | |
86 | - // kernel.positions free functions //////////////////////////////////////////////////////////////////////////// | |
87 | - | |
88 | - /** | |
89 | - * Returns absolute character offset of the specified position from the start of the document. | |
90 | - * @param document The document | |
91 | - * @param at The position | |
92 | - * @param fromAccessibleStart | |
93 | - * @throw BadPositionException @a at is outside of @a document | |
94 | - * @throw DocumentAccessViolationException @a fromAccessibleStart is @c true and @a at is before the accessible | |
95 | - * region of @a document | |
96 | - */ | |
97 | - Index positions::absoluteOffset(const Document& document, const Position& at, bool fromAccessibleStart) { | |
98 | - if(at > *boost::const_end(document.region())) | |
99 | - throw BadPositionException(at); | |
100 | - else if(fromAccessibleStart && at < *boost::const_begin(document.accessibleRegion())) | |
101 | - throw DocumentAccessViolationException(); | |
102 | - Index offset = 0; | |
103 | - const Position start(*boost::const_begin(fromAccessibleStart ? document.accessibleRegion() : document.region())); | |
104 | - for(Index i = line(start); ; ++i) { | |
105 | - if(i == line(at)) { | |
106 | - offset += offsetInLine(at); | |
107 | - break; | |
108 | - } else { | |
109 | - offset += document.lineLength(i) + 1; // +1 is for a newline character | |
110 | - if(i == line(start)) | |
111 | - offset -= offsetInLine(start); | |
112 | - } | |
113 | - } | |
114 | - return offset; | |
115 | - } | |
116 | - | |
117 | - /** | |
118 | - * Adapts the specified position to the document change. | |
119 | - * @param position The original position | |
120 | - * @param change The content of the document change | |
121 | - * @param gravity The gravity which determines the direction to which the position should move if a text was | |
122 | - * inserted at the position. If @c Direction#forward() is specified, the position will move to | |
123 | - * the start of the inserted text (no movement occur). Otherwise, move to the end of the | |
124 | - * inserted text | |
125 | - * @return The result position | |
126 | - */ | |
127 | - Position positions::updatePosition(const Position& position, const DocumentChange& change, Direction gravity) BOOST_NOEXCEPT { | |
128 | - Position newPosition(position); | |
129 | - if(!boost::empty(change.erasedRegion())) { // deletion | |
130 | - if(position < *boost::const_end(change.erasedRegion())) { // the end is behind the current line | |
131 | - if(position <= *boost::const_begin(change.erasedRegion())) | |
132 | - return newPosition; | |
133 | - else // in the region | |
134 | - newPosition = *boost::const_begin(change.erasedRegion()); | |
135 | - } else if(line(position) > line(*boost::const_end(change.erasedRegion()))) // in front of the current line | |
136 | - newPosition.line -= boost::size(change.erasedRegion().lines()) - 1; | |
137 | - else { // the end is the current line | |
138 | - if(line(position) == line(*boost::const_begin(change.erasedRegion()))) // the region is single-line | |
139 | - newPosition.offsetInLine -= offsetInLine(*boost::const_end(change.erasedRegion())) - offsetInLine(*boost::const_begin(change.erasedRegion())); | |
140 | - else { // the region is multiline | |
141 | - newPosition.line -= boost::size(change.erasedRegion().lines()) - 1; | |
142 | - newPosition.offsetInLine -= offsetInLine(*boost::const_end(change.erasedRegion())) - offsetInLine(*boost::const_begin(change.erasedRegion())); | |
143 | - } | |
144 | - } | |
145 | - } | |
146 | - if(!boost::empty(change.insertedRegion())) { // insertion | |
147 | - if(newPosition == *boost::const_begin(change.insertedRegion())) { | |
148 | - if(gravity == Direction::forward()) | |
149 | - newPosition = *boost::const_end(change.insertedRegion()); | |
150 | - } else if(newPosition > *boost::const_begin(change.insertedRegion())) { | |
151 | - if(line(*boost::const_begin(change.insertedRegion())) == line(newPosition)) | |
152 | - newPosition.offsetInLine += offsetInLine(*boost::const_end(change.insertedRegion())) - offsetInLine(*boost::const_begin(change.insertedRegion())); | |
153 | - newPosition.line += boost::size(change.insertedRegion().lines()) - 1; | |
154 | - } | |
155 | - } | |
156 | - return newPosition; | |
157 | - } | |
158 | - | |
159 | - | |
160 | 85 | namespace { |
161 | 86 | #ifdef _DEBUG |
162 | 87 | // for Document.length_ diagnostic |
@@ -7,7 +7,6 @@ | ||
7 | 7 | * @date 2016-05-22 Separated from point.cpp. |
8 | 8 | */ |
9 | 9 | |
10 | -#include <ascension/corelib/numeric-range-algorithm/encompasses.hpp> | |
11 | 10 | #include <ascension/corelib/text/character-property.hpp> // text.ucd.BinaryProperty |
12 | 11 | #include <ascension/corelib/text/grapheme-break-iterator.hpp> |
13 | 12 | #include <ascension/corelib/text/word-break-iterator.hpp> |
@@ -29,18 +28,49 @@ | ||
29 | 28 | inline const Position& position(const T& p) BOOST_NOEXCEPT { |
30 | 29 | return std::get<1>(p); |
31 | 30 | } |
32 | - template<typename T> | |
33 | - inline Position shrinkToAccessibleRegion(const T& p) BOOST_NOEXCEPT { | |
34 | - return positions::shrinkToAccessibleRegion(document(p), position(p)); | |
31 | + inline Index line(const PointProxy& p) BOOST_NOEXCEPT { | |
32 | + return kernel::line(position(p)); | |
33 | + } | |
34 | + inline Index offsetInLine(const PointProxy& p) BOOST_NOEXCEPT { | |
35 | + return kernel::offsetInLine(position(p)); | |
35 | 36 | } |
36 | 37 | template<typename T> |
37 | 38 | inline void throwIfOutsideOfDocument(const T& p) { |
38 | - if(positions::isOutsideOfDocumentRegion(document(p), position(p))) | |
39 | + if(isOutsideOfDocumentRegion(p)) | |
39 | 40 | throw BadPositionException(position(p)); |
40 | 41 | } |
41 | 42 | } |
42 | 43 | |
43 | 44 | /** |
45 | + * Returns absolute character offset of the specified position from the start of the document. | |
46 | + * @param document The document | |
47 | + * @param p The position | |
48 | + * @param fromAccessibleStart | |
49 | + * @throw BadPositionException @a p is outside of the document | |
50 | + * @throw DocumentAccessViolationException @a fromAccessibleStart is @c true and @a p is before the | |
51 | + * accessible region of the document | |
52 | + */ | |
53 | + Index absoluteOffset(const PointProxy& p, bool fromAccessibleStart) { | |
54 | + if(position(p) > *boost::const_end(document(p).region())) | |
55 | + throw BadPositionException(position(p)); | |
56 | + else if(fromAccessibleStart && position(p) < *boost::const_begin(document(p).accessibleRegion())) | |
57 | + throw DocumentAccessViolationException(); | |
58 | + Index offset = 0; | |
59 | + const Position start(*boost::const_begin(fromAccessibleStart ? document(p).accessibleRegion() : document(p).region())); | |
60 | + for(Index i = line(start); ; ++i) { | |
61 | + if(i == line(p)) { | |
62 | + offset += offsetInLine(p); | |
63 | + break; | |
64 | + } else { | |
65 | + offset += document(p).lineLength(i) + 1; // +1 is for a newline character | |
66 | + if(i == line(start)) | |
67 | + offset -= offsetInLine(start); | |
68 | + } | |
69 | + } | |
70 | + return offset; | |
71 | + } | |
72 | + | |
73 | + /** | |
44 | 74 | * Returns the beginning of the accessible region of the document. |
45 | 75 | * @param p The base point |
46 | 76 | * @return The destination |
@@ -57,7 +87,7 @@ | ||
57 | 87 | */ |
58 | 88 | Position beginningOfLine(const PointProxy& p) { |
59 | 89 | throwIfOutsideOfDocument(p); |
60 | - return positions::shrinkToAccessibleRegion(document(p), Position::bol(position(p))); | |
90 | + return shrinkToAccessibleRegion(makePointProxy(document(p), Position::bol(position(p)))); | |
61 | 91 | } |
62 | 92 | |
63 | 93 | /** |
@@ -68,10 +98,10 @@ | ||
68 | 98 | * @return The code point of the character, or @c INVALID_CODE_POINT if @a p is the end of the document |
69 | 99 | */ |
70 | 100 | CodePoint characterAt(const PointProxy& p, bool useLineFeed /* = false */) { |
71 | - const String& lineString = document(p).lineString(line(position(p))); | |
72 | - if(offsetInLine(position(p)) == lineString.length()) | |
73 | - return (line(position(p)) == document(p).numberOfLines() - 1) ? text::INVALID_CODE_POINT : (useLineFeed ? text::LINE_FEED : text::LINE_SEPARATOR); | |
74 | - return text::utf::decodeFirst(std::begin(lineString) + offsetInLine(position(p)), std::end(lineString)); | |
101 | + const auto& lineString = document(p).lineString(line(p)); | |
102 | + if(offsetInLine(p) == lineString.length()) | |
103 | + return (line(p) == document(p).numberOfLines() - 1) ? text::INVALID_CODE_POINT : (useLineFeed ? text::LINE_FEED : text::LINE_SEPARATOR); | |
104 | + return text::utf::decodeFirst(std::begin(lineString) + offsetInLine(p), std::end(lineString)); | |
75 | 105 | } |
76 | 106 | |
77 | 107 | /** |
@@ -91,8 +121,8 @@ | ||
91 | 121 | */ |
92 | 122 | Position endOfLine(const PointProxy& p) { |
93 | 123 | throwIfOutsideOfDocument(p); |
94 | - const auto ln = line(position(p)); | |
95 | - return positions::shrinkToAccessibleRegion(document(p), Position(ln, document(p).lineLength(ln))); | |
124 | + const auto ln = line(p); | |
125 | + return shrinkToAccessibleRegion(makePointProxy(document(p), Position(ln, document(p).lineLength(ln)))); | |
96 | 126 | } |
97 | 127 | |
98 | 128 | /** |
@@ -130,7 +160,7 @@ | ||
130 | 160 | /** |
131 | 161 | * Returns @c true if the given point is the end of the line. |
132 | 162 | * @param p The point to check |
133 | - * @return true if @a is the end of the line | |
163 | + * @return true if @a p is the end of the line | |
134 | 164 | * @throw BadPositionException @a p is outside of the document |
135 | 165 | */ |
136 | 166 | bool isEndOfLine(const PointProxy& p) { |
@@ -138,6 +168,15 @@ | ||
138 | 168 | } |
139 | 169 | |
140 | 170 | /** |
171 | + * Returns @c true if the given position is outside of the document. | |
172 | + * @param p The point to check | |
173 | + * @return true if @a p is outside of the document | |
174 | + */ | |
175 | + bool isOutsideOfDocumentRegion(const PointProxy& p) BOOST_NOEXCEPT { | |
176 | + return line(p) >= document(p).numberOfLines() || offsetInLine(p) > document(p).lineLength(line(p)); | |
177 | + } | |
178 | + | |
179 | + /** | |
141 | 180 | * Returns the beginning of the next bookmarked line. |
142 | 181 | * @param p The base point |
143 | 182 | * @param direction The direction |
@@ -149,7 +188,7 @@ | ||
149 | 188 | */ |
150 | 189 | boost::optional<Position> nextBookmark(const PointProxy& p, Direction direction, Index marks /* = 1 */) { |
151 | 190 | throwIfOutsideOfDocument(p); |
152 | - const auto temp(document(p).bookmarker().next(line(position(p)), direction, true, marks)); | |
191 | + const auto temp(document(p).bookmarker().next(line(p), direction, true, marks)); | |
153 | 192 | if(temp != boost::none) { |
154 | 193 | const auto bookmark = boost::get(temp); |
155 | 194 | const auto accessibleRegion(document(p).accessibleRegion()); |
@@ -207,8 +246,7 @@ | ||
207 | 246 | while(i.hasPrevious() && offset-- > 0) --i; |
208 | 247 | return i.tell(); |
209 | 248 | } else if(characterUnit == locations::GRAPHEME_CLUSTER) { |
210 | - text::GraphemeBreakIterator<DocumentCharacterIterator> i( | |
211 | - DocumentCharacterIterator(document(p), document(p).accessibleRegion(), position(p))); | |
249 | + text::GraphemeBreakIterator<DocumentCharacterIterator> i(DocumentCharacterIterator(document(p), document(p).accessibleRegion(), position(p))); | |
212 | 250 | i.next((direction == Direction::forward()) ? offset : -static_cast<SignedIndex>(offset)); |
213 | 251 | return i.base().tell(); |
214 | 252 | } else if(characterUnit == locations::GLYPH_CLUSTER) { |
@@ -283,6 +321,100 @@ | ||
283 | 321 | Position nextWordEnd(const PointProxy& p, Direction direction, Index words /* = 1 */) { |
284 | 322 | return nextWord(p, direction, words, text::WordBreakIteratorBase::END_OF_SEGMENT); |
285 | 323 | } |
324 | + | |
325 | + /** | |
326 | + * Shrinks the given position into the accessible region of the document. | |
327 | + * @param p The source position. This value can be outside of the document | |
328 | + * @return The result | |
329 | + */ | |
330 | + Position shrinkToAccessibleRegion(const PointProxy& p) BOOST_NOEXCEPT { | |
331 | + if(!document(p).isNarrowed()) | |
332 | + return shrinkToDocumentRegion(p); | |
333 | + const auto accessibleRegion(document(p).accessibleRegion()); | |
334 | + if(position(p) < *boost::const_begin(accessibleRegion)) | |
335 | + return *boost::const_begin(accessibleRegion); | |
336 | + else if(position(p) > *boost::const_end(accessibleRegion)) | |
337 | + return *boost::const_end(accessibleRegion); | |
338 | + return Position(line(p), std::min(offsetInLine(p), document(p).lineLength(line(p)))); | |
339 | + } | |
340 | + | |
341 | + /** | |
342 | + * Shrinks the given region into the accessible region of the document. | |
343 | + * @param document The document | |
344 | + * @param region The source region. This value can intersect with outside of the document | |
345 | + * @return The result. This may not be normalized | |
346 | + */ | |
347 | + Region shrinkToAccessibleRegion(const Document& document, const Region& region) BOOST_NOEXCEPT { | |
348 | + return Region( | |
349 | + shrinkToAccessibleRegion(makePointProxy(document, *boost::const_begin(region))), | |
350 | + shrinkToAccessibleRegion(makePointProxy(document, *boost::const_end(region)))); | |
351 | + } | |
352 | + | |
353 | + /** | |
354 | + * Shrinks the given position into the document region. | |
355 | + * @param p The position | |
356 | + * @return The result | |
357 | + */ | |
358 | + Position shrinkToDocumentRegion(const PointProxy& p) BOOST_NOEXCEPT { | |
359 | + Position q(std::min(line(p), document(p).numberOfLines() - 1), 0); | |
360 | + q.offsetInLine = std::min(offsetInLine(q), document(p).lineLength(line(q))); | |
361 | + return q; | |
362 | + } | |
363 | + | |
364 | + /** | |
365 | + * Shrinks the given region into the document region. The result may not be normalized. | |
366 | + * @param document The document | |
367 | + * @param region The region to shrink | |
368 | + * @return The result | |
369 | + */ | |
370 | + Region shrinkToDocumentRegion(const Document& document, const Region& region) BOOST_NOEXCEPT { | |
371 | + return Region( | |
372 | + shrinkToDocumentRegion(makePointProxy(document, *boost::const_begin(region))), | |
373 | + shrinkToDocumentRegion(makePointProxy(document, *boost::const_end(region)))); | |
374 | + } | |
375 | + | |
376 | + /** | |
377 | + * Adapts the specified position to the document change. | |
378 | + * @param position The original position | |
379 | + * @param change The content of the document change | |
380 | + * @param gravity The gravity which determines the direction to which the position should move if a text | |
381 | + * was inserted at the position. If @c Direction#forward() is specified, the position will | |
382 | + * move to the start of the inserted text (no movement occur). Otherwise, move to the end of | |
383 | + * the inserted text | |
384 | + * @return The result position | |
385 | + */ | |
386 | + Position updatePosition(const Position& position, const DocumentChange& change, Direction gravity) BOOST_NOEXCEPT { | |
387 | + Position newPosition(position); | |
388 | + if(!boost::empty(change.erasedRegion())) { // deletion | |
389 | + if(position < *boost::const_end(change.erasedRegion())) { // the end is behind the current line | |
390 | + if(position <= *boost::const_begin(change.erasedRegion())) | |
391 | + return newPosition; | |
392 | + else // in the region | |
393 | + newPosition = *boost::const_begin(change.erasedRegion()); | |
394 | + } | |
395 | + else if(line(position) > line(*boost::const_end(change.erasedRegion()))) // in front of the current line | |
396 | + newPosition.line -= boost::size(change.erasedRegion().lines()) - 1; | |
397 | + else { // the end is the current line | |
398 | + if(line(position) == line(*boost::const_begin(change.erasedRegion()))) // the region is single-line | |
399 | + newPosition.offsetInLine -= offsetInLine(*boost::const_end(change.erasedRegion())) - offsetInLine(*boost::const_begin(change.erasedRegion())); | |
400 | + else { // the region is multiline | |
401 | + newPosition.line -= boost::size(change.erasedRegion().lines()) - 1; | |
402 | + newPosition.offsetInLine -= offsetInLine(*boost::const_end(change.erasedRegion())) - offsetInLine(*boost::const_begin(change.erasedRegion())); | |
403 | + } | |
404 | + } | |
405 | + } | |
406 | + if(!boost::empty(change.insertedRegion())) { // insertion | |
407 | + if(newPosition == *boost::const_begin(change.insertedRegion())) { | |
408 | + if(gravity == Direction::forward()) | |
409 | + newPosition = *boost::const_end(change.insertedRegion()); | |
410 | + } else if(newPosition > *boost::const_begin(change.insertedRegion())) { | |
411 | + if(line(*boost::const_begin(change.insertedRegion())) == line(newPosition)) | |
412 | + newPosition.offsetInLine += offsetInLine(*boost::const_end(change.insertedRegion())) - offsetInLine(*boost::const_begin(change.insertedRegion())); | |
413 | + newPosition.line += boost::size(change.insertedRegion().lines()) - 1; | |
414 | + } | |
415 | + } | |
416 | + return newPosition; | |
417 | + } | |
286 | 418 | } |
287 | 419 | } |
288 | 420 | } |
@@ -9,6 +9,7 @@ | ||
9 | 9 | #include <ascension/corelib/text/character-property.hpp> // text.ucd.BinaryProperty |
10 | 10 | #include <ascension/kernel/document.hpp> |
11 | 11 | #include <ascension/kernel/document-character-iterator.hpp> |
12 | +#include <ascension/kernel/locations.hpp> | |
12 | 13 | #include <ascension/kernel/point.hpp> |
13 | 14 | #include <boost/core/ignore_unused.hpp> |
14 | 15 |
@@ -117,7 +118,7 @@ | ||
117 | 118 | assert(!isDocumentDisposed()); |
118 | 119 | assert(adaptsToDocument()); |
119 | 120 | // normalize(); |
120 | - const Position newPosition(positions::updatePosition(position(), change, gravity())); | |
121 | + const Position newPosition(locations::updatePosition(position(), change, gravity())); | |
121 | 122 | if(newPosition != position()) |
122 | 123 | moveTo(newPosition); // TODO: this may throw... |
123 | 124 | } |
@@ -146,11 +147,11 @@ | ||
146 | 147 | Point& Point::moveTo(const Position& to) { |
147 | 148 | if(isDocumentDisposed()) |
148 | 149 | throw DocumentDisposedException(); |
149 | - else if(positions::isOutsideOfDocumentRegion(document(), to)) | |
150 | + else if(locations::isOutsideOfDocumentRegion(locations::makePointProxy(document(), to))) | |
150 | 151 | throw BadPositionException(to); |
151 | 152 | Position destination(to); |
152 | 153 | aboutToMove(destination); |
153 | - destination = positions::shrinkToDocumentRegion(document(), destination); | |
154 | + destination = locations::shrinkToDocumentRegion(locations::makePointProxy(document(), destination)); | |
154 | 155 | const Position from(position()); |
155 | 156 | position_ = destination; |
156 | 157 | moved(from); |
@@ -10,6 +10,7 @@ | ||
10 | 10 | #include <ascension/corelib/utility.hpp> // detail.searchBound |
11 | 11 | #include <ascension/kernel/document.hpp> |
12 | 12 | #include <ascension/kernel/document-character-iterator.hpp> |
13 | +#include <ascension/kernel/locations.hpp> | |
13 | 14 | #include <ascension/log.hpp> |
14 | 15 | #include <ascension/rules/lexical-partitioner.hpp> |
15 | 16 | #include <ascension/rules/transition-rule.hpp> |
@@ -61,8 +62,8 @@ | ||
61 | 62 | if(p.start < *boost::const_begin(change.erasedRegion())) |
62 | 63 | continue; |
63 | 64 | else if(p.start > *boost::const_end(change.erasedRegion())) { |
64 | - p.start = kernel::positions::updatePosition(p.start, change, Direction::forward()); | |
65 | - p.tokenStart = kernel::positions::updatePosition(p.tokenStart, change, Direction::forward()); | |
65 | + p.start = kernel::locations::updatePosition(p.start, change, Direction::forward()); | |
66 | + p.tokenStart = kernel::locations::updatePosition(p.tokenStart, change, Direction::forward()); | |
66 | 67 | } else if(((i + 1 < c) ? partitions_[i + 1]->start : *boost::const_end(doc.region())) <= *boost::const_end(change.erasedRegion())) { |
67 | 68 | // this partition is encompassed with the deleted region |
68 | 69 | delete partitions_[i]; |
@@ -83,8 +84,8 @@ | ||
83 | 84 | if(!boost::empty(change.insertedRegion())) { |
84 | 85 | for(std::size_t i = 1, c = partitions_.size(); i < c; ++i) { |
85 | 86 | Partition& p = *partitions_[i]; |
86 | - p.start = kernel::positions::updatePosition(p.start, change, Direction::forward()); | |
87 | - p.tokenStart = kernel::positions::updatePosition(p.tokenStart, change, Direction::forward()); | |
87 | + p.start = kernel::locations::updatePosition(p.start, change, Direction::forward()); | |
88 | + p.tokenStart = kernel::locations::updatePosition(p.tokenStart, change, Direction::forward()); | |
88 | 89 | } |
89 | 90 | } |
90 | 91 | verify(); |
@@ -190,7 +190,7 @@ | ||
190 | 190 | /// @see VisualPoint#aboutToMove |
191 | 191 | void Caret::aboutToMove(TextHit& to) { |
192 | 192 | const auto ip(insertionPosition(document(), to)); |
193 | - if(kernel::positions::isOutsideOfDocumentRegion(document(), ip)) | |
193 | + if(kernel::locations::isOutsideOfDocumentRegion(kernel::locations::makePointProxy(document(), ip))) | |
194 | 194 | throw kernel::BadPositionException(ip, "Caret tried to move outside of document."); |
195 | 195 | VisualPoint::aboutToMove(to); |
196 | 196 | } |
@@ -36,16 +36,16 @@ | ||
36 | 36 | return insertionPosition(document(p), hit(p)); |
37 | 37 | } |
38 | 38 | inline kernel::Position normalPosition(const PointProxy& p) BOOST_NOEXCEPT { |
39 | - return kernel::positions::shrinkToAccessibleRegion(document(p), position(p)); | |
39 | + return kernel::locations::shrinkToAccessibleRegion(kernel::locations::makePointProxy(document(p), position(p))); | |
40 | 40 | } |
41 | 41 | inline TextHit normalHit(const PointProxy& p) BOOST_NOEXCEPT { |
42 | - const auto np(kernel::positions::shrinkToAccessibleRegion(document(p), hit(p).characterIndex())); | |
43 | - if(np != hit(p).characterIndex() || kernel::locations::isEndOfLine(std::make_pair(std::ref(document(p)), np))) | |
42 | + const auto np(kernel::locations::shrinkToAccessibleRegion(kernel::locations::makePointProxy(document(p), hit(p).characterIndex()))); | |
43 | + if(np != hit(p).characterIndex() || kernel::locations::isEndOfLine(kernel::locations::makePointProxy(document(p), np))) | |
44 | 44 | return TextHit::leading(np); |
45 | 45 | return hit(p); |
46 | 46 | } |
47 | 47 | inline std::pair<const kernel::Document&, kernel::Position> kernelProxy(const PointProxy& p) { |
48 | - return std::make_pair(std::ref(document(p)), position(p)); | |
48 | + return kernel::locations::makePointProxy(document(p), position(p)); | |
49 | 49 | } |
50 | 50 | inline graphics::font::TextHit<> inlineHit(const TextHit& hit) BOOST_NOEXCEPT { |
51 | 51 | return graphics::font::transformTextHit(hit, [](const kernel::Position& p) { |
@@ -390,16 +390,16 @@ | ||
390 | 390 | #endif |
391 | 391 | namespace { |
392 | 392 | inline bool isOutsideOfDocumentRegion(const kernel::Document& document, const TextHit& hit) BOOST_NOEXCEPT { |
393 | - return kernel::positions::isOutsideOfDocumentRegion(document, insertionPosition(document, hit)); | |
393 | + return kernel::locations::isOutsideOfDocumentRegion(kernel::locations::makePointProxy(document, insertionPosition(document, hit))); | |
394 | 394 | } |
395 | 395 | inline TextHit shrinkToDocumentRegion(const kernel::Document& document, const TextHit& hit) BOOST_NOEXCEPT { |
396 | 396 | if(kernel::line(hit.characterIndex()) >= document.numberOfLines()) |
397 | - return TextHit::leading(kernel::locations::endOfDocument(std::make_pair(std::ref(document), hit.characterIndex()))); | |
397 | + return TextHit::leading(kernel::locations::endOfDocument(kernel::locations::makePointProxy(document, hit.characterIndex()))); | |
398 | 398 | const Index line = kernel::line(hit.characterIndex()); |
399 | 399 | if(kernel::offsetInLine(hit.characterIndex()) < document.lineLength(line)) |
400 | 400 | return hit; |
401 | 401 | else |
402 | - return TextHit::leading(kernel::locations::endOfLine(std::make_pair(std::ref(document), hit.characterIndex()))); | |
402 | + return TextHit::leading(kernel::locations::endOfLine(kernel::locations::makePointProxy(document, hit.characterIndex()))); | |
403 | 403 | } |
404 | 404 | } |
405 | 405 |