mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-11-14 10:39:32 +01:00
fix(links): scope comma handling to parenthesised paths
This commit is contained in:
1 parent
fd0e52496d
commit
168c15ae08
7 files changed
+70
-12
No files matched your search
@@ -2210,7 +2210,7 @@ function findFilePathLinks(text) {
|
||||
// Parentheses bound an inline path just as a whole line bounds a standalone path.
|
||||
const parenthesised = /\(([^()\r\n]{1,1000})\)/g;
|
||||
while ((match = parenthesised.exec(line)) !== null) {
|
||||
reserve(spacedFilePathOnLine(match[1]), lineStart + match.index + 1);
|
||||
reserve(spacedFilePathOnLine(match[1], { parenthesised: true }), lineStart + match.index + 1);
|
||||
}
|
||||
lineStart += line.length + 1;
|
||||
}
|
||||
@@ -2263,7 +2263,7 @@ const SPACED_FILE_PATH_LABEL_PATTERN =
|
||||
* it ends on a known extension, a rooted one has a directory before its first
|
||||
* space, no earlier word holds an extension (`a.md,`, `a.ts#L12`) or ends in `/`,
|
||||
* and none holds a `:` and a digit (`a.ts:42`, a line suffix ends the path). Later words cannot start a new
|
||||
* `/`, `~/`, `./` or `../` path, no earlier word ends in `:` or `,`, is an arrow or has
|
||||
* `/`, `~/`, `./` or `../` path, no earlier word ends in `:` (or `,` inside parentheses), is an arrow or has
|
||||
* no letter or digit (a prompt glyph such as `❯` or `$`; a bare `-` is kept), and
|
||||
* a run of spaces (a column gap) is refused, so two paths on a line are never
|
||||
* merged. A relative candidate whose only `/` word is its last is prose after the
|
||||
@@ -2285,9 +2285,10 @@ const SPACED_FILE_PATH_LABEL_PATTERN =
|
||||
* Linear: one bounded prefix match, then string scans.
|
||||
*
|
||||
* @param {string} line One line, no newline.
|
||||
* @param {{parenthesised?: boolean}} [options] Refuse comma-separated lists inside parentheses only.
|
||||
* @returns {{path: string, index: number, link: boolean} | null}
|
||||
*/
|
||||
function spacedFilePathOnLine(line) {
|
||||
function spacedFilePathOnLine(line, options = {}) {
|
||||
const text = String(line || '');
|
||||
const prefix = /^[ \t]*((?:\d{1,3}[.)]|[-*•])[ \t]+)?((?:[A-Za-z][\w-]{0,30}(?: [\w-]{1,30}){0,3}):[ \t]+)?/.exec(text);
|
||||
const index = prefix[0].length;
|
||||
@@ -2310,7 +2311,8 @@ function spacedFilePathOnLine(line) {
|
||||
return null;
|
||||
}
|
||||
for (const word of words.slice(0, -1)) {
|
||||
if (word.endsWith('/') || /[,:]$/.test(word) || /^(?:—|–|→|=>|->|<-|=)$/.test(word)) return null;
|
||||
if (word.endsWith('/') || word.endsWith(':') || /^(?:—|–|→|=>|->|<-|=)$/.test(word)) return null;
|
||||
if (options.parenthesised && word.endsWith(',')) return null;
|
||||
// A shell prompt that prints its working directory in full (`/home/me/proj$ cat
|
||||
// notes.md`; WSL's `/mnt/c/...` is outside `$HOME`) is a command line too, and so is
|
||||
// one whose directory has a space in it (`/mnt/c/My Docs$ ls notes.md`): the `$`
|
||||
@@ -2633,7 +2635,7 @@ function terminalPathLine(buffer, row, cols, maxRows) {
|
||||
if (opening >= 0 && closing > 0) {
|
||||
const before = text.slice(opening + 1);
|
||||
const after = next.text.slice(0, closing).trimStart();
|
||||
const spaced = spacedFilePathOnLine(`${before} ${after}`);
|
||||
const spaced = spacedFilePathOnLine(`${before} ${after}`, { parenthesised: true });
|
||||
if (!before.includes(')') && !/[/-]$/.test(before) && !after.includes('/') && spaced?.link) {
|
||||
alternatives.push({ path: before + after, spacedPath: spaced.path });
|
||||
}
|
||||
|
||||
@@ -4195,7 +4195,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
},
|
||||
|
||||
/**
|
||||
* Register an out-of-workspace path as a live external attachment and return
|
||||
* Register a file path as a live external attachment and return
|
||||
* its id, so the preview can render it through the by-id attachment routes.
|
||||
*
|
||||
* `notify: false` keeps this quiet: the caller is already opening the file in
|
||||
@@ -4220,12 +4220,15 @@ Object.assign(CodemanApp.prototype, {
|
||||
const reason = result?.error || `Cannot open this file (HTTP ${res.status})`;
|
||||
// The registry's type answer is a policy term, not an explanation, and the
|
||||
// user just clicked a file they can see on disk. Say what IS previewable
|
||||
// from outside the workspace instead.
|
||||
// through this lookup instead, using the path's actual workspace scope.
|
||||
if (/unsupported/i.test(reason)) {
|
||||
const ext = (filePath.split('.').pop() || '').toLowerCase();
|
||||
const target = this._isExternalPreviewPath(filePath, sessionId)
|
||||
? `.${ext} from outside the session workspace`
|
||||
: `"${filePath}" through the attachment lookup`;
|
||||
return {
|
||||
status: res.status,
|
||||
error: `Cannot preview .${ext} from outside the session workspace (images, video, audio, PDF, Office documents and text files only).`,
|
||||
error: `Cannot preview ${target} (images, video, audio, PDF, Office documents and text files only).`,
|
||||
};
|
||||
}
|
||||
return { error: reason, status: res.status };
|
||||
|
||||
Reference in new issue
Block a user