14
14
15
15
use phpDocumentor \Reflection \Types \Context ;
16
16
17
+ /**
18
+ * Creates a new Description object given a body of text.
19
+ *
20
+ * Descriptions in phpDocumentor are somewhat complex entities as they can contain one or more tags inside their
21
+ * body that can be replaced with a readable output. The replacing is done by passing a Formatter object to the
22
+ * Description object's `render` method.
23
+ *
24
+ * In addition to the above does a Description support two types of escape sequences:
25
+ *
26
+ * 1. `{@}` to escape the `@` character to prevent it from being interpreted as part of a tag, i.e. `{{@}link}`
27
+ * 2. `{}` to escape the `}` character, this can be used if you want to use the `}` character in the description
28
+ * of an inline tag.
29
+ *
30
+ * If a body consists of multiple lines then this factory will also remove any superfluous whitespace at the beginning
31
+ * of each line while maintaining any indentation that is used. This will prevent formatting parsers from tripping
32
+ * over unexpected spaces as can be observed with tag descriptions.
33
+ */
17
34
class DescriptionFactory
18
35
{
19
36
/** @var TagFactory */
@@ -45,11 +62,16 @@ public function create($contents, Context $context = null)
45
62
}
46
63
47
64
/**
48
- * @param $contents
49
- * @return array
65
+ * Strips the contents from superfluous whitespace and splits the description into a series of tokens.
66
+ *
67
+ * @param string $contents
68
+ *
69
+ * @return string[] A series of tokens of which the description text is composed.
50
70
*/
51
71
private function lex ($ contents )
52
72
{
73
+ $ contents = $ this ->removeSuperfluousStartingWhitespace ($ contents );
74
+
53
75
// performance optimalization; if there is no inline tag, don't bother splitting it up.
54
76
if (strpos ($ contents , '{@ ' ) === false ) {
55
77
return [$ contents ];
@@ -98,7 +120,8 @@ private function parse($tokens, Context $context)
98
120
{
99
121
$ count = count ($ tokens );
100
122
$ tagCount = 0 ;
101
- $ tags = [];
123
+ $ tags = [];
124
+
102
125
for ($ i = 1 ; $ i < $ count ; $ i += 2 ) {
103
126
$ tags [] = $ this ->tagFactory ->create ($ tokens [$ i ], $ context );
104
127
$ tokens [$ i ] = '% ' . ++$ tagCount . '$s ' ;
@@ -114,4 +137,55 @@ private function parse($tokens, Context $context)
114
137
return [implode ('' , $ tokens ), $ tags ];
115
138
}
116
139
140
+ /**
141
+ * Removes the superfluous from a multi-line description.
142
+ *
143
+ * When a description has more than one line then it can happen that the second and subsequent lines have an
144
+ * additional indentation. This is commonly in use with tags like this:
145
+ *
146
+ * {@}since 1.1.0 This is an example
147
+ * description where we have an
148
+ * indentation in the second and
149
+ * subsequent lines.
150
+ *
151
+ * If we do not normalize the indentation then we have superfluous whitespace on the second and subsequent
152
+ * lines and this may cause rendering issues when, for example, using a Markdown converter.
153
+ *
154
+ * @param string $contents
155
+ *
156
+ * @return string
157
+ */
158
+ private function removeSuperfluousStartingWhitespace ($ contents )
159
+ {
160
+ $ lines = explode ("\n" , $ contents );
161
+
162
+ // if there is only one line then we don't have lines with superfluous whitespace and
163
+ // can use the contents as-is
164
+ if (count ($ lines ) <= 1 ) {
165
+ return $ contents ;
166
+ }
167
+
168
+ // determine how many whitespace characters need to be stripped
169
+ $ startingSpaceCount = 9999999 ;
170
+ for ($ i = 1 ; $ i < count ($ lines ); $ i ++) {
171
+ // lines with a no length do not count as they are not indented at all
172
+ if (strlen (trim ($ lines [$ i ])) === 0 ) {
173
+ continue ;
174
+ }
175
+
176
+ // determine the number of prefixing spaces by checking the difference in line length before and after
177
+ // an ltrim
178
+ $ startingSpaceCount = min ($ startingSpaceCount , strlen ($ lines [$ i ]) - strlen (ltrim ($ lines [$ i ])));
179
+ }
180
+
181
+ // strip the number of spaces from each line
182
+ if ($ startingSpaceCount > 0 ) {
183
+ for ($ i = 1 ; $ i < count ($ lines ); $ i ++) {
184
+ $ lines [$ i ] = substr ($ lines [$ i ], $ startingSpaceCount );
185
+ }
186
+ }
187
+
188
+ return implode ("\n" , $ lines );
189
+ }
190
+
117
191
}
0 commit comments