bunmicro 0.9.22 → 0.9.23
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 +7 -0
- package/package.json +1 -1
- package/src/highlight/parser.js +15 -10
- package/src/index.js +44 -18
- package/src/runtime/registry.js +10 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.9.23] - 2026-06-10
|
|
4
|
+
- Fixed mouse close prompt cursor move
|
|
5
|
+
- Fixed set filetype doesn't apply instantly
|
|
6
|
+
- Added syntax highlighting fallback if user ~/.config/micro yaml fails
|
|
7
|
+
- Redetect highlighting syntax when unknown filetype saves
|
|
8
|
+
- Warning for dos(CRLF) shell scripts
|
|
9
|
+
|
|
3
10
|
## [0.9.22] - 2026-06-09
|
|
4
11
|
- Clicking on icons toggles prompts
|
|
5
12
|
- Unsaved star triggers save cmd
|
package/package.json
CHANGED
package/src/highlight/parser.js
CHANGED
|
@@ -47,25 +47,30 @@ export async function loadSyntaxDefinitions(runtime) {
|
|
|
47
47
|
const definitions = [];
|
|
48
48
|
for (const file of runtime.list(1)) {
|
|
49
49
|
let text = "";
|
|
50
|
+
let activeFile = file;
|
|
51
|
+
let source = null;
|
|
50
52
|
try {
|
|
51
53
|
text = await file.text();
|
|
54
|
+
source = Bun.YAML.parse(text);
|
|
52
55
|
} catch (e) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
const fallback = file.real ? runtime.fallback?.(1, file.name) : null;
|
|
57
|
+
if (fallback) {
|
|
58
|
+
try {
|
|
59
|
+
text = await fallback.text();
|
|
60
|
+
source = Bun.YAML.parse(text);
|
|
61
|
+
activeFile = fallback;
|
|
62
|
+
console.error("Failed to load user syntax yaml, using built-in fallback:", file.name);
|
|
63
|
+
} catch {}
|
|
64
|
+
}
|
|
57
65
|
}
|
|
58
66
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
source = Bun.YAML.parse(text);
|
|
62
|
-
} catch (e) {
|
|
63
|
-
console.error("Failed to parse syntax yaml:", file.name);
|
|
67
|
+
if (!source) {
|
|
68
|
+
console.error("Failed to load syntax yaml:", file.name);
|
|
64
69
|
console.error(" Will not highlight this kind of file");
|
|
65
70
|
console.error(" @ loadSyntaxDefinitions ");
|
|
66
71
|
}
|
|
67
72
|
|
|
68
|
-
const header = headers.get(
|
|
73
|
+
const header = headers.get(activeFile.name) ?? (source ? parseHeaderYaml(source) : parseHeaderTextFallback(text, activeFile.name));
|
|
69
74
|
definitions.push(new SyntaxDefinition(header, source ?? { rules: [] }, text));
|
|
70
75
|
}
|
|
71
76
|
return definitions;
|
package/src/index.js
CHANGED
|
@@ -1092,6 +1092,7 @@ class BufferModel {
|
|
|
1092
1092
|
}
|
|
1093
1093
|
async save(path = this.path) {
|
|
1094
1094
|
if (!path) throw new Error("No filename");
|
|
1095
|
+
const detectSyntaxAfterSave = this.filetype === "unknown";
|
|
1095
1096
|
let text = this.lines.join("\n");
|
|
1096
1097
|
if (this.encoding === "hex3") {
|
|
1097
1098
|
await Bun.write(path, decodeBinaryBytes(Buffer.from(text, "latin1")));
|
|
@@ -1106,6 +1107,7 @@ class BufferModel {
|
|
|
1106
1107
|
this._savedSerial = this._undoSerial ?? 0;
|
|
1107
1108
|
this.modified = false;
|
|
1108
1109
|
this.message = `Saved ${path}`;
|
|
1110
|
+
if (detectSyntaxAfterSave && this._syntaxContext) attachSyntax(this, this._syntaxContext, path, text);
|
|
1109
1111
|
return;
|
|
1110
1112
|
}
|
|
1111
1113
|
if ((this.Settings.eofnewline ?? DEFAULT_SETTINGS.eofnewline) && !text.endsWith("\n")) text += "\n";
|
|
@@ -1123,6 +1125,7 @@ class BufferModel {
|
|
|
1123
1125
|
this._savedSerial = this._undoSerial ?? 0;
|
|
1124
1126
|
this.modified = false;
|
|
1125
1127
|
this.message = `Saved ${path}`;
|
|
1128
|
+
if (detectSyntaxAfterSave && this._syntaxContext) attachSyntax(this, this._syntaxContext, path, text);
|
|
1126
1129
|
}
|
|
1127
1130
|
|
|
1128
1131
|
// --- Autocomplete (BufferComplete) ---
|
|
@@ -1788,7 +1791,10 @@ class App {
|
|
|
1788
1791
|
const keymenuHeight = this.keymenu ? KEYDISPLAY.length : 0;
|
|
1789
1792
|
const activeSuggestions = this._activeSuggestions();
|
|
1790
1793
|
const activeSuggestionIdx = this._activeSuggestionIdx();
|
|
1791
|
-
const
|
|
1794
|
+
const formatWarning = this.buffer?.filetype === "shell" && this.buffer?.fileformat === "dos"
|
|
1795
|
+
? "dos(CRLF fileformat) invalid for shell scripts!"
|
|
1796
|
+
: "";
|
|
1797
|
+
const activeMessage = this.message || this.buffer?.message || formatWarning;
|
|
1792
1798
|
if (activeSuggestions.length === 0) this._acHScroll = 0;
|
|
1793
1799
|
const suggestionsHeight = activeSuggestions.length > 1 ? 1 : 0;
|
|
1794
1800
|
const messageHeight = suggestionsHeight ? 0 : activeMessage ? 1 : 0;
|
|
@@ -3517,18 +3523,7 @@ class App {
|
|
|
3517
3523
|
}
|
|
3518
3524
|
break;
|
|
3519
3525
|
case "ft": {
|
|
3520
|
-
|
|
3521
|
-
const filetypes = defs.map(d => d.filetype).filter(Boolean).sort();
|
|
3522
|
-
const ftComplete = (partial) => filetypes.filter(f => f.startsWith(partial));
|
|
3523
|
-
this.openPrompt("Set filetype: ", (value) => {
|
|
3524
|
-
if (!value || !buf) return;
|
|
3525
|
-
buf.filetype = value;
|
|
3526
|
-
buf.Settings.filetype = value;
|
|
3527
|
-
const def = defs.find(d => d.filetype === value);
|
|
3528
|
-
buf.syntaxDefinition = def ?? null;
|
|
3529
|
-
buf.highlighter = def ? new Highlighter(def, defs) : null;
|
|
3530
|
-
buf._highlightCache = null;
|
|
3531
|
-
}, { completer: ftComplete, initial: buf?.filetype ?? "" });
|
|
3526
|
+
this.openCommandMode("set filetype ");
|
|
3532
3527
|
break;
|
|
3533
3528
|
}
|
|
3534
3529
|
case "fmt":
|
|
@@ -3552,9 +3547,11 @@ class App {
|
|
|
3552
3547
|
await this.addTab();
|
|
3553
3548
|
break;
|
|
3554
3549
|
case "cmdmode":
|
|
3550
|
+
if (this.prompt?.type === "Command") this._suppressMouseUntilUp = true;
|
|
3555
3551
|
await this.togglePromptMode("Command");
|
|
3556
3552
|
break;
|
|
3557
3553
|
case "shellmode":
|
|
3554
|
+
if (this.prompt?.type === "Shell") this._suppressMouseUntilUp = true;
|
|
3558
3555
|
await this.togglePromptMode("Shell");
|
|
3559
3556
|
break;
|
|
3560
3557
|
}
|
|
@@ -5149,6 +5146,18 @@ function completeOptionValue(cmd, option, partial, context) {
|
|
|
5149
5146
|
const optVal = allSettings[option];
|
|
5150
5147
|
const suggestions = [];
|
|
5151
5148
|
|
|
5149
|
+
if (option === "filetype") {
|
|
5150
|
+
const filetypes = [
|
|
5151
|
+
"off",
|
|
5152
|
+
"unknown",
|
|
5153
|
+
...(context?.syntaxDefinitions ?? []).map((definition) => definition.filetype),
|
|
5154
|
+
];
|
|
5155
|
+
return [...new Set(filetypes)]
|
|
5156
|
+
.filter((filetype) => filetype && filetype.startsWith(partial))
|
|
5157
|
+
.sort()
|
|
5158
|
+
.map((filetype) => ({ value: `${cmd} ${option} ${filetype}`, label: filetype }));
|
|
5159
|
+
}
|
|
5160
|
+
|
|
5152
5161
|
if (typeof optVal === "boolean") {
|
|
5153
5162
|
if ("on".startsWith(partial)) suggestions.push("on");
|
|
5154
5163
|
else if ("true".startsWith(partial)) suggestions.push("true");
|
|
@@ -6229,9 +6238,14 @@ async function loadBuffers(files, command) {
|
|
|
6229
6238
|
if (loadBuffers.context) attachSyntax(stdinBuf, loadBuffers.context, "", stdinText);
|
|
6230
6239
|
buffers.push(stdinBuf);
|
|
6231
6240
|
} else {
|
|
6232
|
-
|
|
6241
|
+
const buffer = new BufferModel({ command });
|
|
6242
|
+
if (loadBuffers.context) attachSyntax(buffer, loadBuffers.context, "", "");
|
|
6243
|
+
buffers.push(buffer);
|
|
6233
6244
|
}
|
|
6234
|
-
|
|
6245
|
+
if (buffers.length > 0) return buffers;
|
|
6246
|
+
const buffer = new BufferModel({ command });
|
|
6247
|
+
if (loadBuffers.context) attachSyntax(buffer, loadBuffers.context, "", "");
|
|
6248
|
+
return [buffer];
|
|
6235
6249
|
}
|
|
6236
6250
|
|
|
6237
6251
|
async function printReadmeDocs() {
|
|
@@ -6666,6 +6680,7 @@ function getSelectionText(buf, selection) {
|
|
|
6666
6680
|
}
|
|
6667
6681
|
|
|
6668
6682
|
function attachSyntax(buffer, context, path, text) {
|
|
6683
|
+
buffer._syntaxContext = context;
|
|
6669
6684
|
const def = detectBufferSyntax(context.syntaxDefinitions, path, text);
|
|
6670
6685
|
buffer.syntaxDefinition = def;
|
|
6671
6686
|
buffer.filetype = def?.filetype ?? "unknown";
|
|
@@ -6673,21 +6688,32 @@ function attachSyntax(buffer, context, path, text) {
|
|
|
6673
6688
|
buffer.highlighter = def ? new Highlighter(def, context.syntaxDefinitions ?? []) : null;
|
|
6674
6689
|
buffer._highlightCache = null;
|
|
6675
6690
|
buffer._onOptionChange = (option, oldVal, newVal) => {
|
|
6691
|
+
if (option === "filetype") setBufferFiletype(buffer, context, newVal);
|
|
6676
6692
|
const ba = makeBufferAdapter(buffer);
|
|
6677
6693
|
context.plugins?.run("onBufferOptionChanged", ba, option, oldVal, newVal);
|
|
6678
6694
|
context.jsPlugins?.run("onBufferOptionChanged", ba, option, oldVal, newVal);
|
|
6679
6695
|
};
|
|
6680
6696
|
}
|
|
6681
6697
|
|
|
6698
|
+
function setBufferFiletype(buffer, context, filetype) {
|
|
6699
|
+
const value = String(filetype);
|
|
6700
|
+
const definitions = context?.syntaxDefinitions ?? [];
|
|
6701
|
+
const def = definitions.find((candidate) => candidate.filetype === value) ?? null;
|
|
6702
|
+
buffer.filetype = value;
|
|
6703
|
+
buffer.Settings.filetype = value;
|
|
6704
|
+
buffer.syntaxDefinition = def;
|
|
6705
|
+
buffer.highlighter = def ? new Highlighter(def, definitions) : null;
|
|
6706
|
+
buffer._highlightCache = null;
|
|
6707
|
+
}
|
|
6708
|
+
|
|
6682
6709
|
function detectBufferSyntax(definitions, path, text) {
|
|
6683
6710
|
if (!definitions) return null;
|
|
6684
|
-
const lines =
|
|
6711
|
+
const lines = normalizeBufferText(text).split("\n").slice(0, 50);
|
|
6685
6712
|
return detectSyntax(definitions, { path, firstLine: lines[0] ?? "", lines });
|
|
6686
6713
|
}
|
|
6687
6714
|
|
|
6688
6715
|
function detectBufferFiletype(definitions, path, text) {
|
|
6689
6716
|
if (!definitions) return "unknown";
|
|
6690
|
-
const lines = String(text).split("\n").slice(0, 50);
|
|
6691
6717
|
return detectBufferSyntax(definitions, path, text)?.filetype ?? "unknown";
|
|
6692
6718
|
}
|
|
6693
6719
|
|
|
@@ -6761,7 +6787,7 @@ async function catFiles(files, colorscheme, syntaxDefinitions, encoding = DEFAUL
|
|
|
6761
6787
|
);
|
|
6762
6788
|
continue;
|
|
6763
6789
|
}
|
|
6764
|
-
const lines = content.split("\n");
|
|
6790
|
+
const lines = normalizeBufferText(content).split("\n");
|
|
6765
6791
|
const def = detectSyntax(syntaxDefinitions, {
|
|
6766
6792
|
path: effectivePath ?? "",
|
|
6767
6793
|
firstLine: lines[0] ?? "",
|
package/src/runtime/registry.js
CHANGED
|
@@ -14,11 +14,13 @@ export class RuntimeRegistry {
|
|
|
14
14
|
this.configDir = configDir;
|
|
15
15
|
this.files = [[], [], [], [], []];
|
|
16
16
|
this.realFiles = [[], [], [], [], []];
|
|
17
|
+
this.fallbackFiles = [[], [], [], [], []];
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
async init({ user = true } = {}) {
|
|
20
21
|
this.files = [[], [], [], [], []];
|
|
21
22
|
this.realFiles = [[], [], [], [], []];
|
|
23
|
+
this.fallbackFiles = [[], [], [], [], []];
|
|
22
24
|
await this.addRuntimeKind(RTColorscheme, "colorschemes", ".micro", user);
|
|
23
25
|
await this.addRuntimeKind(RTSyntax, "syntax", ".yaml", user);
|
|
24
26
|
await this.addRuntimeKind(RTSyntaxHeader, "syntax", ".hdr", user);
|
|
@@ -36,7 +38,10 @@ export class RuntimeRegistry {
|
|
|
36
38
|
for (const entry of entries) {
|
|
37
39
|
if (entry.isDirectory() || !entry.name.endsWith(extension)) continue;
|
|
38
40
|
const file = new RuntimeFile(join(dir, entry.name), real);
|
|
39
|
-
if (!real && this.realFiles[kind].some((f) => f.name === file.name))
|
|
41
|
+
if (!real && this.realFiles[kind].some((f) => f.name === file.name)) {
|
|
42
|
+
this.fallbackFiles[kind].push(file);
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
40
45
|
this.files[kind].push(file);
|
|
41
46
|
if (real) this.realFiles[kind].push(file);
|
|
42
47
|
}
|
|
@@ -53,6 +58,10 @@ export class RuntimeRegistry {
|
|
|
53
58
|
find(kind, name) {
|
|
54
59
|
return this.list(kind).find((file) => file.name === name) ?? null;
|
|
55
60
|
}
|
|
61
|
+
|
|
62
|
+
fallback(kind, name) {
|
|
63
|
+
return this.fallbackFiles[kind]?.find((file) => file.name === name) ?? null;
|
|
64
|
+
}
|
|
56
65
|
}
|
|
57
66
|
|
|
58
67
|
class RuntimeFile {
|