mirror of
https://github.com/ruby/ruby.git
synced 2025-08-26 22:45:03 +02:00

The problem was that we were treating heredoc bodies as part of the %W
list because we didn't push the scanning cursor past the heredoc after
lexing out the here doc. To fix this, we changed the whitespace
scanning function to quit scanning when it reaches a newline but only in
the case that a heredoc is present.
Additionally, we need to prevent double counting newlines in the case of
a heredoc. For example:
```ruby
%W(<<foo 123)
foo
```
The newline after the `)` is counted as part of scanning the heredoc, so
we added logic to prevent double counting the newline when scanning the
rest of the %W list.
eb090d8126
Co-authored-by: Jemma Issroff <jemmaissroff@gmail.com>
119 lines
3.7 KiB
C
119 lines
3.7 KiB
C
#include "yarp/util/yp_newline_list.h"
|
|
|
|
// Initialize a new newline list with the given capacity. Returns true if the
|
|
// allocation of the offsets succeeds, otherwise returns false.
|
|
bool
|
|
yp_newline_list_init(yp_newline_list_t *list, const char *start, size_t capacity) {
|
|
list->offsets = (size_t *) calloc(capacity, sizeof(size_t));
|
|
if (list->offsets == NULL) return false;
|
|
|
|
list->start = start;
|
|
|
|
// This is 1 instead of 0 because we want to include the first line of the
|
|
// file as having offset 0, which is set because of calloc.
|
|
list->size = 1;
|
|
list->capacity = capacity;
|
|
|
|
list->last_index = 0;
|
|
list->last_offset = 0;
|
|
|
|
return true;
|
|
}
|
|
|
|
// Append a new offset to the newline list. Returns true if the reallocation of
|
|
// the offsets succeeds (if one was necessary), otherwise returns false.
|
|
bool
|
|
yp_newline_list_append(yp_newline_list_t *list, const char *cursor) {
|
|
if (list->size == list->capacity) {
|
|
list->capacity = (list->capacity * 3) / 2;
|
|
list->offsets = (size_t *) realloc(list->offsets, list->capacity * sizeof(size_t));
|
|
if (list->offsets == NULL) return false;
|
|
}
|
|
|
|
assert(cursor >= list->start);
|
|
size_t newline_offset = (size_t) (cursor - list->start + 1);
|
|
assert(list->size == 0 || newline_offset > list->offsets[list->size - 1]);
|
|
list->offsets[list->size++] = newline_offset;
|
|
|
|
return true;
|
|
}
|
|
|
|
// Returns the line and column of the given offset, assuming we don't have any
|
|
// information about the previous index that we found.
|
|
static yp_line_column_t
|
|
yp_newline_list_line_column_search(yp_newline_list_t *list, size_t offset) {
|
|
size_t left = 0;
|
|
size_t right = list->size - 1;
|
|
|
|
while (left <= right) {
|
|
size_t mid = left + (right - left) / 2;
|
|
|
|
if (list->offsets[mid] == offset) {
|
|
return ((yp_line_column_t) { mid, 0 });
|
|
}
|
|
|
|
if (list->offsets[mid] < offset) {
|
|
left = mid + 1;
|
|
} else {
|
|
right = mid - 1;
|
|
}
|
|
}
|
|
|
|
return ((yp_line_column_t) { left - 1, offset - list->offsets[left - 1] });
|
|
}
|
|
|
|
// Returns the line and column of the given offset, assuming we know the last
|
|
// index that we found.
|
|
static yp_line_column_t
|
|
yp_newline_list_line_column_scan(yp_newline_list_t *list, size_t offset) {
|
|
if (offset > list->last_offset) {
|
|
size_t index = list->last_index;
|
|
while (index < list->size && list->offsets[index] < offset) {
|
|
index++;
|
|
}
|
|
|
|
if (index == list->size) {
|
|
return ((yp_line_column_t) { index - 1, offset - list->offsets[index - 1] });
|
|
}
|
|
|
|
return ((yp_line_column_t) { index, 0 });
|
|
} else {
|
|
size_t index = list->last_index;
|
|
while (index > 0 && list->offsets[index] > offset) {
|
|
index--;
|
|
}
|
|
|
|
if (index == 0) {
|
|
return ((yp_line_column_t) { 0, offset });
|
|
}
|
|
|
|
return ((yp_line_column_t) { index, offset - list->offsets[index - 1] });
|
|
}
|
|
}
|
|
|
|
// Returns the line and column of the given offset. If the offset is not in the
|
|
// list, the line and column of the closest offset less than the given offset
|
|
// are returned.
|
|
yp_line_column_t
|
|
yp_newline_list_line_column(yp_newline_list_t *list, const char *cursor) {
|
|
assert(cursor >= list->start);
|
|
size_t offset = (size_t) (cursor - list->start);
|
|
yp_line_column_t result;
|
|
|
|
if (list->last_offset == 0) {
|
|
result = yp_newline_list_line_column_search(list, offset);
|
|
} else {
|
|
result = yp_newline_list_line_column_scan(list, offset);
|
|
}
|
|
|
|
list->last_index = result.line;
|
|
list->last_offset = offset;
|
|
|
|
return result;
|
|
}
|
|
|
|
// Free the internal memory allocated for the newline list.
|
|
void
|
|
yp_newline_list_free(yp_newline_list_t *list) {
|
|
free(list->offsets);
|
|
}
|