aboutsummaryrefslogtreecommitdiff
path: root/effcee/cursor.h
diff options
context:
space:
mode:
Diffstat (limited to 'effcee/cursor.h')
-rw-r--r--effcee/cursor.h97
1 files changed, 97 insertions, 0 deletions
diff --git a/effcee/cursor.h b/effcee/cursor.h
new file mode 100644
index 0000000..82133f9
--- /dev/null
+++ b/effcee/cursor.h
@@ -0,0 +1,97 @@
+// Copyright 2017 The Effcee Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef EFFCEE_CURSOR_H
+#define EFFCEE_CURSOR_H
+
+#include <sstream>
+#include <string>
+
+#include "re2/stringpiece.h"
+
+namespace effcee {
+
+using StringPiece = re2::StringPiece;
+
+// Represents a position in a StringPiece, while tracking line number.
+class Cursor {
+
+ public:
+ explicit Cursor(StringPiece str)
+ : remaining_(str), line_num_(1) {}
+
+ StringPiece remaining() const { return remaining_; }
+ // Returns the current 1-based line number.
+ int line_num() const { return line_num_; }
+
+ // Returns true if the remaining text is empty.
+ bool Exhausted() const { return remaining_.empty(); }
+
+ // Returns a string piece from the current position until the end of the line
+ // or the end of input, up to and including the newline.
+ StringPiece RestOfLine() const {
+ const auto newline_pos = remaining_.find('\n');
+ return remaining_.substr(0, newline_pos + (newline_pos != StringPiece::npos));
+ }
+
+ // Advance |n| characters. Does not adjust line count. The next |n|
+ // characters should not contain newlines if line numbering is to remain
+ // up to date. Returns this object.
+ Cursor& Advance(size_t n) { remaining_.remove_prefix(n); return *this; }
+
+ // Advances the cursor by a line. If no text remains, then does nothing.
+ // Otherwise removes the first line (including newline) and increments the
+ // line count. If there is no newline then the remaining string becomes
+ // empty. Returns this object.
+ Cursor& AdvanceLine() {
+ if (remaining_.size()) {
+ Advance(RestOfLine().size());
+ ++line_num_;
+ }
+ return *this;
+ };
+
+ private:
+ // The remaining text, after all previous advancements. References the
+ // original string storage.
+ StringPiece remaining_;
+ // The current 1-based line number.
+ int line_num_;
+};
+
+// Returns string containing a description of the line containing a given
+// subtext, with a message, and a caret displaying the subtext position.
+// Assumes subtext does not contain a newline.
+inline std::string LineMessage(StringPiece text, StringPiece subtext,
+ StringPiece message) {
+ Cursor c(text);
+ StringPiece full_line = c.RestOfLine();
+ while (subtext.end() - full_line.end() > 0) {
+ c.AdvanceLine();
+ full_line = c.RestOfLine();
+ }
+ const char* full_line_newline =
+ full_line.find('\n') == StringPiece::npos ? "\n" : "";
+ const size_t column = subtext.begin() - full_line.begin();
+
+ std::ostringstream out;
+ out << ":" << c.line_num() << ":" << (1 + column) << ": " << message << "\n"
+ << full_line << full_line_newline << std::string(column, ' ') << "^\n";
+
+ return out.str();
+}
+
+} // namespace effcee
+
+#endif