tmux-fuzzy-motion 0.0.7 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +89 -19
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -6,6 +6,7 @@ import { dirname, join, resolve } from "node:path";
|
|
|
6
6
|
import { createConnection, createServer } from "node:net";
|
|
7
7
|
import { tmpdir } from "node:os";
|
|
8
8
|
import { createHash } from "node:crypto";
|
|
9
|
+
import { statSync } from "node:fs";
|
|
9
10
|
//#endregion
|
|
10
11
|
//#region src/infra/process.ts
|
|
11
12
|
const runProcess = async (command, args) => new Promise((resolve, reject) => {
|
|
@@ -4726,7 +4727,7 @@ const capturePane = async (tmux, paneId) => {
|
|
|
4726
4727
|
};
|
|
4727
4728
|
//#endregion
|
|
4728
4729
|
//#region src/core/width.ts
|
|
4729
|
-
const ANSI_PATTERN = /\u001B\[[0-9;]*m/gu;
|
|
4730
|
+
const ANSI_PATTERN$1 = /\u001B\[[0-9;]*m/gu;
|
|
4730
4731
|
const RESET$2 = "\x1B[0m";
|
|
4731
4732
|
const displayWidth = (value) => stringWidth(value);
|
|
4732
4733
|
const codeUnitIndexToColumn = (value, index) => stringWidth(value.slice(0, index));
|
|
@@ -4764,11 +4765,52 @@ const codeUnitRangeToColumns = (value, start, end) => {
|
|
|
4764
4765
|
}
|
|
4765
4766
|
return columns;
|
|
4766
4767
|
};
|
|
4768
|
+
const createCompactStyledDisplayCells = (value) => {
|
|
4769
|
+
const cells = [];
|
|
4770
|
+
let activeStyle = "";
|
|
4771
|
+
let pendingStyle = "";
|
|
4772
|
+
let styleOpen = false;
|
|
4773
|
+
let lastVisibleCellIndex = -1;
|
|
4774
|
+
let lastIndex = 0;
|
|
4775
|
+
const appendChunk = (chunk) => {
|
|
4776
|
+
for (const char of chunk) {
|
|
4777
|
+
const width = Math.max(1, stringWidth(char));
|
|
4778
|
+
const prefix = pendingStyle.length > 0 ? pendingStyle : !styleOpen && activeStyle.length > 0 ? activeStyle : "";
|
|
4779
|
+
cells.push(`${prefix}${char}`);
|
|
4780
|
+
if (prefix.length > 0) {
|
|
4781
|
+
pendingStyle = "";
|
|
4782
|
+
styleOpen = true;
|
|
4783
|
+
}
|
|
4784
|
+
lastVisibleCellIndex = cells.length - 1;
|
|
4785
|
+
for (let offset = 1; offset < width; offset += 1) cells.push("");
|
|
4786
|
+
}
|
|
4787
|
+
};
|
|
4788
|
+
const closeStyle = () => {
|
|
4789
|
+
if (styleOpen && lastVisibleCellIndex >= 0) cells[lastVisibleCellIndex] = `${cells[lastVisibleCellIndex]}${RESET$2}`;
|
|
4790
|
+
activeStyle = "";
|
|
4791
|
+
pendingStyle = "";
|
|
4792
|
+
styleOpen = false;
|
|
4793
|
+
};
|
|
4794
|
+
for (const match of value.matchAll(ANSI_PATTERN$1)) {
|
|
4795
|
+
const start = match.index ?? 0;
|
|
4796
|
+
appendChunk(value.slice(lastIndex, start));
|
|
4797
|
+
const sequence = match[0];
|
|
4798
|
+
if (sequence === RESET$2) closeStyle();
|
|
4799
|
+
else {
|
|
4800
|
+
activeStyle = `${activeStyle}${sequence}`;
|
|
4801
|
+
pendingStyle = `${pendingStyle}${sequence}`;
|
|
4802
|
+
}
|
|
4803
|
+
lastIndex = start + sequence.length;
|
|
4804
|
+
}
|
|
4805
|
+
appendChunk(value.slice(lastIndex));
|
|
4806
|
+
if (styleOpen) closeStyle();
|
|
4807
|
+
return cells;
|
|
4808
|
+
};
|
|
4767
4809
|
const createStyledDisplayCells = (value) => {
|
|
4768
4810
|
const cells = [];
|
|
4769
4811
|
let activeStyle = "";
|
|
4770
4812
|
let lastIndex = 0;
|
|
4771
|
-
for (const match of value.matchAll(ANSI_PATTERN)) {
|
|
4813
|
+
for (const match of value.matchAll(ANSI_PATTERN$1)) {
|
|
4772
4814
|
const start = match.index ?? 0;
|
|
4773
4815
|
const chunk = value.slice(lastIndex, start);
|
|
4774
4816
|
for (const char of chunk) {
|
|
@@ -4815,9 +4857,19 @@ const PATTERNS = [
|
|
|
4815
4857
|
}
|
|
4816
4858
|
];
|
|
4817
4859
|
const isContainedByHigherPriority = (current, others) => others.some((candidate) => candidate.line === current.line && candidate.priority < current.priority && candidate.startIndex <= current.startIndex && candidate.endIndex >= current.endIndex);
|
|
4860
|
+
const dedupeLineCandidates = (candidates) => {
|
|
4861
|
+
const byStartIndex = /* @__PURE__ */ new Map();
|
|
4862
|
+
for (const candidate of candidates) {
|
|
4863
|
+
const current = byStartIndex.get(candidate.startIndex);
|
|
4864
|
+
if (!current || candidate.priority < current.priority) byStartIndex.set(candidate.startIndex, candidate);
|
|
4865
|
+
}
|
|
4866
|
+
const deduped = [...byStartIndex.values()];
|
|
4867
|
+
return deduped.filter((candidate) => !isContainedByHigherPriority(candidate, deduped));
|
|
4868
|
+
};
|
|
4818
4869
|
const extractCandidates = (lines) => {
|
|
4819
4870
|
const collected = [];
|
|
4820
4871
|
lines.forEach((lineText, lineIndex) => {
|
|
4872
|
+
const lineCandidates = [];
|
|
4821
4873
|
PATTERNS.forEach(({ kind, pattern }, priority) => {
|
|
4822
4874
|
const regex = new RegExp(pattern.source, pattern.flags);
|
|
4823
4875
|
for (const match of lineText.matchAll(regex)) {
|
|
@@ -4828,7 +4880,7 @@ const extractCandidates = (lines) => {
|
|
|
4828
4880
|
if (width === 1 && kind !== "word") continue;
|
|
4829
4881
|
const col = codeUnitIndexToColumn(lineText, startIndex);
|
|
4830
4882
|
const charCol = codeUnitIndexToCharacterIndex(lineText, startIndex);
|
|
4831
|
-
|
|
4883
|
+
lineCandidates.push({
|
|
4832
4884
|
kind,
|
|
4833
4885
|
text,
|
|
4834
4886
|
line: lineIndex + 1,
|
|
@@ -4841,10 +4893,9 @@ const extractCandidates = (lines) => {
|
|
|
4841
4893
|
});
|
|
4842
4894
|
}
|
|
4843
4895
|
});
|
|
4896
|
+
collected.push(...dedupeLineCandidates(lineCandidates));
|
|
4844
4897
|
});
|
|
4845
|
-
return collected.
|
|
4846
|
-
return source.find((other) => other.line === candidate.line && other.startIndex === candidate.startIndex && other.priority <= candidate.priority) === candidate;
|
|
4847
|
-
}).filter((candidate, _, source) => !isContainedByHigherPriority(candidate, source)).sort((left, right) => left.line - right.line || left.col - right.col || left.priority - right.priority).map((candidate) => ({
|
|
4898
|
+
return collected.sort((left, right) => left.line - right.line || left.col - right.col || left.priority - right.priority).map((candidate) => ({
|
|
4848
4899
|
kind: candidate.kind,
|
|
4849
4900
|
text: candidate.text,
|
|
4850
4901
|
line: candidate.line,
|
|
@@ -5749,7 +5800,17 @@ const createMatcher = (candidates, migemo) => {
|
|
|
5749
5800
|
//#region src/commands/runtime.ts
|
|
5750
5801
|
const sleep = async (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
5751
5802
|
const resolveCliEntrypoint = () => process.argv[1] ?? resolve(process.cwd(), "dist/cli.js");
|
|
5752
|
-
const
|
|
5803
|
+
const createDaemonIdentity = () => {
|
|
5804
|
+
const entrypoint = resolveCliEntrypoint();
|
|
5805
|
+
const entrypointMtimeMs = statSync(entrypoint).mtimeMs;
|
|
5806
|
+
return [
|
|
5807
|
+
process.env.TMUX ?? "tmux-fuzzy-motion",
|
|
5808
|
+
process.execPath,
|
|
5809
|
+
entrypoint,
|
|
5810
|
+
String(entrypointMtimeMs)
|
|
5811
|
+
].join("\0");
|
|
5812
|
+
};
|
|
5813
|
+
const createDaemonSocketPath = () => join("/tmp", `tfm-${createHash("sha1").update(createDaemonIdentity()).digest("hex")}.sock`);
|
|
5753
5814
|
const pathExists = async (path) => {
|
|
5754
5815
|
try {
|
|
5755
5816
|
await access(path);
|
|
@@ -5798,7 +5859,7 @@ const isDaemonHealthy = async (socketPath) => new Promise((resolve) => {
|
|
|
5798
5859
|
});
|
|
5799
5860
|
});
|
|
5800
5861
|
const waitForDaemon = async (socketPath) => {
|
|
5801
|
-
for (let attempt = 0; attempt <
|
|
5862
|
+
for (let attempt = 0; attempt < 200; attempt += 1) {
|
|
5802
5863
|
if (await isDaemonHealthy(socketPath)) return;
|
|
5803
5864
|
await sleep(25);
|
|
5804
5865
|
}
|
|
@@ -5950,6 +6011,7 @@ const withRawMode = async (input, output, callback) => {
|
|
|
5950
6011
|
//#region src/commands/input.ts
|
|
5951
6012
|
const QUERY_STYLE = "\x1B[48;5;236;38;5;252m";
|
|
5952
6013
|
const RESET = "\x1B[0m";
|
|
6014
|
+
const ANSI_PATTERN = /\u001B\[[0-9;]*m/u;
|
|
5953
6015
|
const HINT_CHARS = /* @__PURE__ */ new Set("ASDFGHJKLQWERTYUIOPZXCVBNM");
|
|
5954
6016
|
const WORD_CHAR_PATTERN = /[a-z0-9_-]/u;
|
|
5955
6017
|
const parsePopupArgs = (args) => {
|
|
@@ -6025,9 +6087,14 @@ const fitBodyToHeight = (lines, height) => {
|
|
|
6025
6087
|
return next;
|
|
6026
6088
|
};
|
|
6027
6089
|
const renderQueryOnBottomLine = (line, width, query) => {
|
|
6090
|
+
if (query.length === 0) {
|
|
6091
|
+
const cells = createCompactStyledDisplayCells(line);
|
|
6092
|
+
while (cells.length < width) cells.push(" ");
|
|
6093
|
+
const rendered = cells.slice(0, width).join("");
|
|
6094
|
+
return ANSI_PATTERN.test(rendered) && !rendered.endsWith(RESET) ? `${rendered}${RESET}` : rendered;
|
|
6095
|
+
}
|
|
6028
6096
|
const cells = createStyledDisplayCells(line);
|
|
6029
6097
|
while (cells.length < width) cells.push(" ");
|
|
6030
|
-
if (query.length === 0) return cells.slice(0, width).join("");
|
|
6031
6098
|
const queryWidth = Math.min(width, stringWidth(query));
|
|
6032
6099
|
const start = Math.max(0, width - queryWidth);
|
|
6033
6100
|
const content = Array.from(query);
|
|
@@ -6044,7 +6111,7 @@ const renderQueryOnBottomLine = (line, width, query) => {
|
|
|
6044
6111
|
return cells.slice(0, width).join("");
|
|
6045
6112
|
};
|
|
6046
6113
|
const createFrame = (state, query, matches, renderOverlay) => {
|
|
6047
|
-
const body = fitBodyToHeight(query.length > 0 && matches.length > 0 ? renderOverlay(matches) : state.displayLines, state.height);
|
|
6114
|
+
const body = fitBodyToHeight(query.length > 0 && matches.length > 0 && renderOverlay ? renderOverlay(matches) : state.displayLines, state.height);
|
|
6048
6115
|
const lastLineIndex = Math.max(0, body.length - 1);
|
|
6049
6116
|
body[lastLineIndex] = renderQueryOnBottomLine(body[lastLineIndex] ?? "", state.width, query);
|
|
6050
6117
|
return { body };
|
|
@@ -6162,7 +6229,11 @@ const expectResponse = (response, type) => {
|
|
|
6162
6229
|
return response;
|
|
6163
6230
|
};
|
|
6164
6231
|
const runPopupJob = async (state, options) => {
|
|
6165
|
-
|
|
6232
|
+
let overlayRenderer = null;
|
|
6233
|
+
const getOverlayRenderer = () => {
|
|
6234
|
+
overlayRenderer ??= createOverlayRenderer(state.displayLines);
|
|
6235
|
+
return overlayRenderer;
|
|
6236
|
+
};
|
|
6166
6237
|
let query = "";
|
|
6167
6238
|
let previousHints = /* @__PURE__ */ new Map();
|
|
6168
6239
|
let matches = [];
|
|
@@ -6170,7 +6241,7 @@ const runPopupJob = async (state, options) => {
|
|
|
6170
6241
|
const output = process.stdout;
|
|
6171
6242
|
await withRawMode(input, output, async () => {
|
|
6172
6243
|
const reader = createByteReader(input);
|
|
6173
|
-
let previousFrame = renderFrame(output, createFrame(state, query, matches
|
|
6244
|
+
let previousFrame = renderFrame(output, createFrame(state, query, matches));
|
|
6174
6245
|
try {
|
|
6175
6246
|
while (true) {
|
|
6176
6247
|
const value = await reader.nextByte();
|
|
@@ -6213,7 +6284,7 @@ const runPopupJob = async (state, options) => {
|
|
|
6213
6284
|
}
|
|
6214
6285
|
matches = query.length === 0 ? [] : await options.onMatch(query, previousHints);
|
|
6215
6286
|
previousHints = new Map(matches.map((target) => [createTargetKey(target), target.hint]));
|
|
6216
|
-
previousFrame = renderFrame(output, createFrame(state, query, matches,
|
|
6287
|
+
previousFrame = renderFrame(output, createFrame(state, query, matches, matches.length > 0 ? getOverlayRenderer() : void 0), previousFrame);
|
|
6217
6288
|
}
|
|
6218
6289
|
} finally {
|
|
6219
6290
|
reader.close();
|
|
@@ -6531,7 +6602,7 @@ const composeDisplayLines = (panes, width, height, borderLines) => {
|
|
|
6531
6602
|
for (const pane of panes) pane.displayLines.forEach((line, lineIndex) => {
|
|
6532
6603
|
const row = rows[pane.top + lineIndex];
|
|
6533
6604
|
if (!row) return;
|
|
6534
|
-
|
|
6605
|
+
createCompactStyledDisplayCells(line).forEach((cell, cellIndex) => {
|
|
6535
6606
|
const column = pane.left + cellIndex;
|
|
6536
6607
|
if (column < 0 || column >= row.length) return;
|
|
6537
6608
|
row[column] = cell;
|
|
@@ -6570,10 +6641,9 @@ const buildAllPaneState = async (tmux, pane, paneId, clientTty) => {
|
|
|
6570
6641
|
right: Number.NEGATIVE_INFINITY,
|
|
6571
6642
|
bottom: Number.NEGATIVE_INFINITY
|
|
6572
6643
|
});
|
|
6573
|
-
const snapshots =
|
|
6574
|
-
for (const item of panes) {
|
|
6644
|
+
const snapshots = await Promise.all(panes.map(async (item) => {
|
|
6575
6645
|
const capture = fitCaptureToHeight(await capturePane(tmux, item.paneId), item.height);
|
|
6576
|
-
|
|
6646
|
+
return {
|
|
6577
6647
|
paneId: item.paneId,
|
|
6578
6648
|
inCopyMode: item.inCopyMode,
|
|
6579
6649
|
width: item.width,
|
|
@@ -6582,8 +6652,8 @@ const buildAllPaneState = async (tmux, pane, paneId, clientTty) => {
|
|
|
6582
6652
|
top: item.top - bounds.top,
|
|
6583
6653
|
plainLines: capture.lines,
|
|
6584
6654
|
displayLines: capture.displayLines
|
|
6585
|
-
}
|
|
6586
|
-
}
|
|
6655
|
+
};
|
|
6656
|
+
}));
|
|
6587
6657
|
const width = Math.max(0, bounds.right - bounds.left);
|
|
6588
6658
|
const height = Math.max(0, bounds.bottom - bounds.top);
|
|
6589
6659
|
const x = buildPopupRelativePosition("x", bounds.left);
|