Terminal: hyper link mode: Try more aggressively to detect a path

Consider ':' a potential path delimiter and try all combinations of
chopped off prefixes and suffixes. This makes detection in the output
of a multi-file grep work even if the found line starts with a path
character or is a path. A path in the typical colon delimited search
paths (e.g. PATH) is detected as well.
This commit is contained in:
Ingo Weinhold
2013-05-12 17:18:47 +02:00
parent a15966b3b0
commit 78a1163c7b
2 changed files with 103 additions and 42 deletions
+97 -42
View File
@@ -30,6 +30,8 @@
#include <UTF8.h> #include <UTF8.h>
#include <Window.h> #include <Window.h>
#include <Array.h>
#include "ActiveProcessInfo.h" #include "ActiveProcessInfo.h"
#include "Shell.h" #include "Shell.h"
#include "TermConst.h" #include "TermConst.h"
@@ -800,55 +802,108 @@ TermView::HyperLinkState::_GetHyperLinkAt(BPoint where, bool pathPrefixOnly,
if (text.IsEmpty()) if (text.IsEmpty())
return false; return false;
// check, whether the file exists // Collect a list of colons in the string and their respective positions in
BString actualPath; // the text buffer. We do this up-front so we can unlock the text buffer
if (_EntryExists(text, actualPath)) { // while we're doing all the entry existence tests.
_link = HyperLink(text, actualPath, HyperLink::TYPE_PATH); typedef Array<CharPosition> ColonList;
return true; ColonList colonPositions;
} TermPos searchPos = _start;
for (int32 index = 0; (index = text.FindFirst(':', index)) >= 0;) {
// As such this isn't an existing path. Try a few common alternative cases: TermPos foundStart;
// * "<path>:" TermPos foundEnd;
// * "<path>:<line>" if (!textBuffer->Find(":", searchPos, true, true, false, foundStart,
// * "<path>:<line>:" foundEnd)) {
// * "<path>:<line>:<column>"
// * "<path>:<line>:<column>:"
if (text.Length() <= 1)
return false;
if (text[text.Length() - 1] == ':') {
text.Truncate(text.Length() - 1);
if (!textBuffer->PreviousLinePos(_end))
return false; return false;
if (_EntryExists(text, actualPath)) {
_link = HyperLink(text, actualPath, HyperLink::TYPE_PATH);
return true;
} }
CharPosition colonPosition;
colonPosition.index = index;
colonPosition.position = foundStart;
if (!colonPositions.Add(colonPosition))
return false;
index++;
searchPos = foundEnd;
} }
BString path = text; textBufferLocker.Unlock();
for (int32 i = 0; i < 2; i++) { // Since we also want to consider ':' a potential path delimiter, in two
int32 colonIndex = path.FindLast(':'); // nested loops we chop off components from the beginning respective the
if (colonIndex <= 0 || colonIndex == path.Length() - 1) // end.
return false; BString originalText = text;
TermPos originalStart = _start;
TermPos originalEnd = _end;
char* numberEnd; int32 colonCount = colonPositions.Count();
strtol(path.String() + colonIndex + 1, &numberEnd, 0); for (int32 startColonIndex = -1; startColonIndex < colonCount;
if (*numberEnd != '\0') startColonIndex++) {
return false; int32 startIndex;
if (startColonIndex < 0) {
startIndex = 0;
_start = originalStart;
} else {
startIndex = colonPositions[startColonIndex].index + 1;
_start = colonPositions[startColonIndex].position;
if (_start >= pos)
break;
_start.x++;
// Note: This is potentially a non-normalized position (i.e.
// the end of a soft-wrapped line). While not that nice, it
// works anyway.
}
path.Truncate(colonIndex); for (int32 endColonIndex = colonCount; endColonIndex > startColonIndex;
if (_EntryExists(path, actualPath)) { endColonIndex--) {
BString address = path == actualPath int32 endIndex;
? text : BString(fCurrentDirectory) << '/' << text; if (endColonIndex == colonCount) {
_link = HyperLink(text, address, endIndex = originalText.Length();
i == 0 _end = originalEnd;
? HyperLink::TYPE_PATH_WITH_LINE } else {
: HyperLink::TYPE_PATH_WITH_LINE_AND_COLUMN); endIndex = colonPositions[endColonIndex].index;
return true; _end = colonPositions[endColonIndex].position;
if (_end <= pos)
break;
}
originalText.CopyInto(text, startIndex, endIndex - startIndex);
if (text.IsEmpty())
continue;
// check, whether the file exists
BString actualPath;
if (_EntryExists(text, actualPath)) {
_link = HyperLink(text, actualPath, HyperLink::TYPE_PATH);
return true;
}
// As such this isn't an existing path. We also want to recognize:
// * "<path>:<line>"
// * "<path>:<line>:<column>"
BString path = text;
for (int32 i = 0; i < 2; i++) {
int32 colonIndex = path.FindLast(':');
if (colonIndex <= 0 || colonIndex == path.Length() - 1)
break;
char* numberEnd;
strtol(path.String() + colonIndex + 1, &numberEnd, 0);
if (*numberEnd != '\0')
break;
path.Truncate(colonIndex);
if (_EntryExists(path, actualPath)) {
BString address = path == actualPath
? text : BString(fCurrentDirectory) << '/' << text;
_link = HyperLink(text, address,
i == 0
? HyperLink::TYPE_PATH_WITH_LINE
: HyperLink::TYPE_PATH_WITH_LINE_AND_COLUMN);
return true;
}
}
} }
} }
+6
View File
@@ -128,6 +128,12 @@ private:
virtual rgb_color BackgroundColor(); virtual rgb_color BackgroundColor();
virtual uint32 AdjustTextAttributes(uint32 attributes); virtual uint32 AdjustTextAttributes(uint32 attributes);
private:
struct CharPosition {
int32 index;
TermPos position;
};
private: private:
bool _GetHyperLinkAt(BPoint where, bool _GetHyperLinkAt(BPoint where,
bool pathPrefixOnly, HyperLink& _link, bool pathPrefixOnly, HyperLink& _link,