From b1d073cdeb03c69c8a7573976f7b56f87715c968 Mon Sep 17 00:00:00 2001
From: j-krl <63491353+j-krl@users.noreply.github.com>
Date: Sat, 23 May 2020 21:09:22 -0600
Subject: [PATCH] next/prev link index is buggy on page change

---
 CliClient/app/LinkSelector.js | 133 +++++++++++++++++++++++-----------
 CliClient/app/app-gui.js      |  97 ++++++-------------------
 2 files changed, 112 insertions(+), 118 deletions(-)

diff --git a/CliClient/app/LinkSelector.js b/CliClient/app/LinkSelector.js
index 1eb590a2b5..823a287711 100644
--- a/CliClient/app/LinkSelector.js
+++ b/CliClient/app/LinkSelector.js
@@ -6,74 +6,123 @@ class LinkSelector {
 		this.noteId_ = null; // the note id
 		this.scrollTop_ = null; // units from the top of the scroll window
 		this.renderedText_ = null; // rendered text string with correct newline chars
-		this.currentLink_ = null; // currently selected link. obj with index, link, x, y
+		this.currentLinkIndex_ = null; // currently selected link index from linkStore_
 		this.linkStore_ = null; // object of all link objects in the text
-		this.linkRegex_ = /http:\/\/[0-9.]+:[0-9]+\/[0-9]+/g; // link regex being searched for
+		// this.linkRegex_ = /\\x1B[^\s]+http:\/\/[0-9.]+:[0-9]+\/[0-9]+/g; // link regex being searched for
+		this.linkRegex_ = /http:\/\/[0-9.]+:[0-9]+\/[0-9]+/g;
+		// const link = /\\x1B\[[0-9]{2}m\\x1B\[[0-9]mhttp:\/\/[0-9.]+:[0-9]+\/[0-9]+\\x1B\[[0-9]{2}m\\x1B\[[0-9]{2}m/g;
 	}
 
-	// static findLinks(renderedText) {
-	// 	const newLinkStore = [];
-	// 	const lines = renderedText.split('\n');
-	// 	// find them links
-	// 	return newLinkStore;
-	// }
+	get link() {
+		if (this.currentLinkIndex_ === null) return null;
+		return this.linkStore_[this.currentLinkIndex_].link;
+	}
 
-	static updateText(renderedText) {
-		this.currentLink_ = null;
+	get noteX() {
+		if (this.currentLinkIndex_ === null) return null;
+		return this.linkStore_[this.currentLinkIndex_].noteX;
+	}
+
+	get noteY() {
+		if (this.currentLinkIndex_ === null) return null;
+		return this.linkStore_[this.currentLinkIndex_].noteY;
+	}
+
+	get scrollTop() {
+		return this.scrollTop_;
+	}
+
+	findLinks(renderedText) {
+		const newLinkStore = [];
+		const lines = renderedText.split('\n');
+		for (let i = 0; i < lines.length; i++) {
+			const matches = [...lines[i].matchAll(this.linkRegex_)];
+			matches.forEach((e, n) => {
+				newLinkStore.push(
+					{
+						link: matches[n][0],
+						noteX: matches[n].index,
+						noteY: i,
+					}
+				);
+			});
+		}
+		return newLinkStore;
+	}
+
+	updateText(renderedText) {
+		this.currentLinkIndex_ = null;
 		this.renderedText_ = renderedText;
 		this.linkStore_ = this.findLinks(this.renderedText_);
 	}
 
-	static updateNote(textWidget) {
-		this.noteId = textWidget.noteId;
-		this.scrollTop_ = 0;
-		this.updateText(textWidget);
+	updateNote(textWidget) {
+		this.noteId_ = textWidget.noteId;
+		this.scrollTop_ = textWidget.scrollTop_;
+		this.updateText(textWidget.renderedText_);
 	}
 
-	selectLink(textWidget, offset) {
+	scrollWidget(textWidget) {
+		const noteY = this.linkStore_[this.currentLinkIndex_].noteY;
+
+		let viewBoxMin = textWidget.scrollTop_;
+		let viewBoxMax = viewBoxMin + textWidget.innerHeight;
+
+		if (noteY < viewBoxMin) {
+			for (; noteY < viewBoxMin; textWidget.pageUp()) {
+				viewBoxMin = textWidget.scrollTop_;
+				viewBoxMax = viewBoxMin + textWidget.innerHeight;
+			}
+			return;
+
+		} else if (noteY > viewBoxMax) {
+			for (; noteY > viewBoxMax; textWidget.pageDown()) {
+				viewBoxMin = textWidget.scrollTop_;
+				viewBoxMax = viewBoxMin + textWidget.innerHeight;
+			}
+			return;
+		}
+		return;
+	}
+
+	changeLink(textWidget, offset) {
 		if (textWidget.noteId !== this.noteId_) {
 			this.updateNote(textWidget);
-			this.selectLink(textWidget, offset);
+			this.changeLink(textWidget, offset);
 			return;
 		}
-		if (textWidget.renderedText !== this.renderedText_) {
-			this.updateText(textWidget.renderedText);
-			this.selectLink(textWidget, offset);
+		if (textWidget.renderedText_ !== this.renderedText_) {
+			this.updateText(textWidget.renderedText_);
+			this.changeLink(textWidget, offset);
 			return;
 		}
-		if (textWidget.scrollTop !== this.scrollTop_) {
-			this.selectLink(textWidget, 0);
+		if (textWidget.scrollTop_ !== this.scrollTop_) {
+			this.scrollTop_ = textWidget.scrollTop_;
+			this.changeLink(textWidget, 0);
 			return;
 		}
 
-		const offsetMod = offset % this.currentLink_.index;
+		if (!this.linkStore_.length) return null;
 
-		if (!this.currentLink_) {
-			if (offsetMod < 0) {
-				this.currentLink_ = this.linkStore_[this.linkStore_.length + offsetMod];
-				this.currentLink_.index = this.linkStore_.length + offsetMod;
-			} else if (!offsetMod) {
-				this.currentLink_ = this.linkStore_[0];
-				this.currentLink_.index = 0;
-			} else {
-				this.currentLink_ = this.linkStore_[offsetMod - 1];
-				this.currentLink_.index = offsetMod - 1;
-			}
-			return this.currentLink_;
+		let offsetMod = (offset + this.currentLinkIndex_) % this.linkStore_.length;
+		if (offsetMod < 0) offsetMod = this.linkStore_.length + offsetMod;
+
+		if (this.currentLinkIndex_ === null) {
+			if (offsetMod < 0) this.currentLinkIndex_ = this.linkStore_.length + offsetMod;
+			else if (!offsetMod) this.currentLinkIndex_ = 0;
+			else this.currentLinkIndex_ = offsetMod - 1;
+			return;
 		}
 
-		this.currentLink_ = this.linkStore_[offsetMod];
-		this.currentLink_.index = offsetMod;
-
-		return this.currentLink_;
+		this.currentLinkIndex_ = offsetMod;
+		return;
 	}
 
 	openLink(textWidget) {
 		if (textWidget.noteId !== this.noteId_) return;
-		if (textWidget.scrollTop !== this.scrollTop_) {
-			return this.selectLink(textWidget, 0);
-		}
-		open(this.currentLink_);
+		if (textWidget.renderedText_ !== this.renderedText_) return;
+		if (textWidget.scrollTop_ !== this.scrollTop_) return;
+		open(this.linkStore_[this.currentLinkIndex_].link);
 	}
 
 }
diff --git a/CliClient/app/app-gui.js b/CliClient/app/app-gui.js
index e199299587..35a6d76709 100644
--- a/CliClient/app/app-gui.js
+++ b/CliClient/app/app-gui.js
@@ -56,7 +56,6 @@ const NoteListWidget = require('./gui/NoteListWidget.js');
 const StatusBarWidget = require('./gui/StatusBarWidget.js');
 const ConsoleWidget = require('./gui/ConsoleWidget.js');
 const LinkSelector = require('./LinkSelector.js');
-// const linkSelector = new LinkSelector();
 
 
 class AppGui {
@@ -484,88 +483,34 @@ class AppGui {
 			}
 
 		// NOTE: MY SHORTCUTS
-		} else if (cmd === 'next_link' || cmd === 'previous_link' || cmd === 'open_link') {
+		} else if (cmd === 'next_link' || cmd === 'previous_link') {
 			const noteText = this.widget('noteText');
-			const mainWindow = this.widget('mainWindow');
-			// const lines = noteText.renderedText_.split('\n');
-			// console.log(lines)
-
 			if (noteText.hasFocus) {
+
 				noteText.render();
 
-				const lines = noteText.renderedText_.split('\n');
-				// const link = /\\x1B\[[0-9]{2}m\\x1B\[[0-9]mhttp:\/\/[0-9.]+:[0-9]+\/[0-9]+\\x1B\[[0-9]{2}m\\x1B\[[0-9]{2}m/g;
-				const link = /http:\/\/[0-9.]+:[0-9]+\/[0-9]+/g;
+				if (cmd === 'next_link') {
+					this.linkSelector_.changeLink(noteText, 1);
+				} else {
+					this.linkSelector_.changeLink(noteText, -1);
+				}
 
-				// this.term_.moveTo(mainWindow.width - noteText.innerWidth + 1, 1);
-				this.term_.showCursor();
+				this.linkSelector_.scrollWidget(noteText);
 
-				this.term_.term().getCursorLocation((error, x, y) => {
-					if (error) throw new Error('Could not get cursor index');
+				const cursorOffsetX = this.widget('mainWindow').width - noteText.innerWidth - 8;
+				const cursorOffsetY = 1 - noteText.scrollTop_;
 
-					const cursorOffset = mainWindow.width - noteText.innerWidth + 1;
-					const innerX = x - cursorOffset;
-					const innerY = y;
-					const scrollHeight = noteText.scrollableHeight_ - 1;
-
-					const beginStr = lines[innerY].substr(0, innerX);
-					const endStr = lines[innerY].substr(innerX, lines[innerY].length - 1);
-
-					if (cmd !== 'previous_link') {
-						const matchesNext = [...beginStr.matchAll(link)];
-
-						if (cmd === 'open_link' && matchesNext.length) {
-
-							if (matchesNext[0].index  === innerX) {
-								console.log(matchesNext[0][0]);
-								open(matchesNext[0][0]);
-								return;
-							}
-
-						} else if (cmd === 'next_link' && matchesNext.length > 1) {
-							this.term_.term().moveTo(cursorOffset + matchesNext[1].index - 9, innerY);
-							this.term_.term().inverse(matchesNext[1][0]);
-							this.term_.term().move(-matchesNext[1][0].length, 0);
-							return;
-						}
-
-						if (cmd === 'open_link') return;
-
-					} else {
-						const matchesPrev = [...endStr.matchAll(link)];
-						if (matchesPrev.length) {
-							this.term_.moveTo(cursorOffset + matchesPrev[matchesPrev.length - 1].index - 9, innerY);
-							this.term_.term().inverse(matchesPrev[matchesPrev.length - 1][0]);
-							this.term_.move(-matchesPrev[matchesPrev.length - 1][0].length, 0);
-							return;
-						}
-					}
-
-					let i;
-					if (cmd === 'next_link') i === scrollHeight ? i = 0 : i = innerY + 1;
-					else i === 0 ? i = scrollHeight : i = innerY - 1;
-					for (; i !== innerY; (cmd === 'next_link' ? i++ : i--)) {
-						const matches = [...lines[i].matchAll(link)];
-
-						if (cmd === 'next_link') {
-							if (i === scrollHeight) i = 0;
-							if (matches.length) {
-								this.term_.term().moveTo(cursorOffset + matches[0].index - 9, i + 1);
-								this.term_.term().inverse(matches[0][0]);
-								this.term_.term().move(-matches[0][0].length, 0);
-								return;
-							}
-						} else {
-							if (i === 0) i = scrollHeight;
-							if (matches.length) {
-								this.term_.term().moveTo(cursorOffset + matches[matches.length - 1].index - 9, i + 1);
-								this.term_.term().inverse(matches[matches.length - 1][0]);
-								this.term_.term().move(-matches[matches.length - 1][0].length, 0);
-								return;
-							}
-						}
-					}
-				});
+				if (this.linkSelector_.link) {
+					this.term_.moveTo(
+						this.linkSelector_.noteX + cursorOffsetX,
+						this.linkSelector_.noteY + cursorOffsetY
+					);
+					this.term_.term().inverse(this.linkSelector_.link);
+				}
+			}
+		} else if (cmd === 'open_link') {
+			if (this.widget('noteText').hasFocus) {
+				this.linkSelector_.openLink(this.widget('noteText'));
 			}
 		} else if (cmd === 'toggle_console') {
 			if (!this.consoleIsShown()) {