bunmicro 0.9.10 → 0.9.19

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/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.9.19] - 2026-06-06
4
+ - Fixed Windows clipboard
5
+ - Fixed cli encoding help readme
6
+ - Added cli clean config dir
7
+ - Added softwrap subrow line number
8
+ * goto line
9
+ * show subrow line number in showpath
10
+ * pageup/down respects softwrap
11
+ - Fixed CRLF being overwritten
12
+ - Added primary clipboard
13
+ - Fixed clipboard cmd / action
14
+ * Ctrl+K should accumulate
15
+ - Clipboard backend priority
16
+ - Added OSC 52 clipboard copy
17
+ * Use by set clipboard terminal
18
+
3
19
  ## [0.9.10] - 2026-06-04
4
20
  - Up/Down key auto-complete selection in editor
5
21
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bunmicro",
3
- "version": "0.9.10",
3
+ "version": "0.9.19",
4
4
  "description": "Bun JavaScript rewrite of the micro editor originally in Golang",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -61,7 +61,9 @@ micro.on("init", () => {
61
61
  //micro.TermMessage("Hello from JS plugin! Args: " + args.join(", "));
62
62
 
63
63
  //await micro.alert(micro.getLine())
64
- await micro.alert(bp?.Buf?.Path || "(no path)")
64
+ const path = bp?.Buf?.Path || "(no path)";
65
+ const loc = bp?.CursorLocation?.() || "+1.0:1";
66
+ await micro.alert(`${path}\n${loc}`)
65
67
 
66
68
  });
67
69
 
@@ -0,0 +1,172 @@
1
+ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
2
+ import { rm } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { createInterface } from "node:readline/promises";
5
+ import { defaultAllSettings, LOCAL_SETTINGS } from "./defaults.js";
6
+
7
+ function settingsEqual(a, b) {
8
+ return JSON.stringify(a) === JSON.stringify(b);
9
+ }
10
+
11
+ async function createCleanPrompt() {
12
+ if (process.stdin.isTTY) {
13
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
14
+ return {
15
+ ask: async () => {
16
+ const text = (await rl.question("Continue [Y/n]: ")).trim();
17
+ return text.length === 0 || text.toLowerCase().startsWith("y");
18
+ },
19
+ close: () => rl.close(),
20
+ };
21
+ }
22
+
23
+ const answers = (await Bun.stdin.text()).split(/\r?\n/);
24
+ let index = 0;
25
+ return {
26
+ ask: async () => {
27
+ process.stdout.write("Continue [Y/n]: ");
28
+ const text = (answers[index++] ?? "").trim();
29
+ return text.length === 0 || text.toLowerCase().startsWith("y");
30
+ },
31
+ close: () => {},
32
+ };
33
+ }
34
+
35
+ function cleanDefaultSettings(config) {
36
+ const defaults = defaultAllSettings();
37
+ const cleaned = { ...config.parsedSettings };
38
+ for (const [key, value] of Object.entries(cleaned)) {
39
+ if (LOCAL_SETTINGS.has(key)) {
40
+ delete cleaned[key];
41
+ continue;
42
+ }
43
+ if (key in defaults && settingsEqual(config.globalSettings[key], defaults[key]) && settingsEqual(value, defaults[key])) {
44
+ delete cleaned[key];
45
+ }
46
+ }
47
+ return cleaned;
48
+ }
49
+
50
+ function findUnusedOptions(settings, pluginNames) {
51
+ const defaults = defaultAllSettings();
52
+ const unused = [];
53
+ for (const key of Object.keys(settings)) {
54
+ if (key.startsWith("ft:") || key.startsWith("glob:")) continue;
55
+ if (key in defaults) continue;
56
+ let valid = false;
57
+ for (const name of pluginNames) {
58
+ if (key === name || key.startsWith(`${name}.`)) {
59
+ valid = true;
60
+ break;
61
+ }
62
+ }
63
+ if (!valid) unused.push(key);
64
+ }
65
+ return unused.sort();
66
+ }
67
+
68
+ async function writeCleanSettings(config, settings) {
69
+ const settingsFile = join(config.configDir, "settings.json");
70
+ await Bun.write(settingsFile, JSON.stringify(settings, null, " ") + "\n");
71
+ }
72
+
73
+ function invalidBunBufferStateFile(path, name) {
74
+ if (name !== "history.json" && name !== "cursor_state.json") return false;
75
+ try {
76
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
77
+ return !parsed || typeof parsed !== "object" || Array.isArray(parsed);
78
+ } catch {
79
+ return true;
80
+ }
81
+ }
82
+
83
+ export async function cleanConfig(config, plugins) {
84
+ const prompt = await createCleanPrompt();
85
+ try {
86
+ console.log("Cleaning your configuration directory at", config.configDir);
87
+ console.log(`Please consider backing up ${config.configDir} before continuing`);
88
+
89
+ if (!(await prompt.ask())) {
90
+ console.log("Stopping early");
91
+ return;
92
+ }
93
+
94
+ console.log("Cleaning default settings");
95
+ let cleanedSettings = cleanDefaultSettings(config);
96
+ try {
97
+ await writeCleanSettings(config, cleanedSettings);
98
+ } catch (err) {
99
+ console.log(`Error writing settings.json file: ${err.message}`);
100
+ }
101
+
102
+ const pluginNames = new Set(plugins.list().map((plugin) => plugin.name));
103
+ const unusedOptions = findUnusedOptions(cleanedSettings, pluginNames);
104
+ if (unusedOptions.length > 0) {
105
+ const settingsFile = join(config.configDir, "settings.json");
106
+ console.log("The following options are unused:");
107
+ for (const option of unusedOptions) console.log(`${option} (value: ${JSON.stringify(cleanedSettings[option])})`);
108
+ console.log(`These options will be removed from ${settingsFile}`);
109
+
110
+ if (await prompt.ask()) {
111
+ for (const option of unusedOptions) delete cleanedSettings[option];
112
+ try {
113
+ await writeCleanSettings(config, cleanedSettings);
114
+ console.log("Removed unused options");
115
+ console.log("\n");
116
+ } catch (err) {
117
+ console.log(`Error overwriting settings.json file: ${err.message}`);
118
+ }
119
+ }
120
+ }
121
+
122
+ const buffersPath = join(config.configDir, "buffers");
123
+ if (existsSync(buffersPath)) {
124
+ const badFiles = [];
125
+ for (const entry of readdirSync(buffersPath, { withFileTypes: true })) {
126
+ if (!entry.isFile()) continue;
127
+ const path = join(buffersPath, entry.name);
128
+ if (invalidBunBufferStateFile(path, entry.name)) badFiles.push(path);
129
+ }
130
+ if (badFiles.length > 0) {
131
+ console.log(`Detected ${badFiles.length} files with an invalid format in ${buffersPath}`);
132
+ console.log("These files store cursor and undo history.");
133
+ console.log(`Removing badly formatted files in ${buffersPath}`);
134
+
135
+ if (await prompt.ask()) {
136
+ let removed = 0;
137
+ for (const file of badFiles) {
138
+ try {
139
+ await rm(file);
140
+ removed++;
141
+ } catch (err) {
142
+ console.log(err.message);
143
+ }
144
+ }
145
+ if (removed === 0) console.log("Failed to remove files");
146
+ else console.log(`Removed ${removed} badly formatted files`);
147
+ console.log("\n");
148
+ }
149
+ }
150
+ }
151
+
152
+ const oldPluginsDir = join(config.configDir, "plugins");
153
+ if (existsSync(oldPluginsDir) && statSync(oldPluginsDir).isDirectory()) {
154
+ console.log(`Found directory ${oldPluginsDir}`);
155
+ console.log(`Plugins should now be stored in ${join(config.configDir, "plug")}`);
156
+ console.log(`Removing ${oldPluginsDir}`);
157
+
158
+ if (await prompt.ask()) {
159
+ try {
160
+ await rm(oldPluginsDir, { recursive: true, force: true });
161
+ } catch (err) {
162
+ console.log(err.message);
163
+ }
164
+ }
165
+ console.log("\n");
166
+ }
167
+
168
+ console.log("Done cleaning");
169
+ } finally {
170
+ prompt.close();
171
+ }
172
+ }
@@ -104,7 +104,9 @@ function normalizeSetting(key, value) {
104
104
 
105
105
  function validateOption(option, value) {
106
106
  if (option === "encoding") {
107
- try { new TextDecoder(String(value || "utf-8")); }
107
+ const encoding = String(value || "utf-8");
108
+ if (encoding === "hex3") return;
109
+ try { new TextDecoder(encoding); }
108
110
  catch { throw new Error(`Invalid encoding: ${value}`); }
109
111
  }
110
112
  const choices = OPTION_CHOICES[option];
@@ -91,7 +91,7 @@ export const OPTION_CHOICES = {
91
91
 
92
92
  // Settings that are buffer-local only and must never be written to the global config file.
93
93
  // Mirrors Go micro's config.LocalSettings.
94
- export const LOCAL_SETTINGS = new Set(["readonly", "filetype"]);
94
+ export const LOCAL_SETTINGS = new Set(["readonly", "filetype", "fileformat"]);
95
95
 
96
96
  export function defaultAllSettings() {
97
97
  return { ...DEFAULT_COMMON_SETTINGS, ...DEFAULT_GLOBAL_ONLY_SETTINGS };