Compare commits

...

17 Commits

Author SHA1 Message Date
Jöran Karl
d976b3f170 Merge pull request #4044 from JoeKar/fix/crash-glob
Fix crash with file globbing matching micro option names
2026-03-22 15:03:11 +01:00
Jöran Karl
4f32b47075 settings: Add glob: as prefix for file globbing maps
This gives the advantage to differentiate internal options from user defined
file globs with the same name.
2026-03-19 19:35:24 +01:00
Jöran Karl
bcd6c81f50 settings: Don't return maps with ParsedSettings()
Map-typed values in the parsedSettings map do not represent settings for
individual options, they represent maps of settings for multiple options for
the given glob or ft: pattern, and their keys are not option names, they are
glob and ft: patterns. So do not expose them to the callers of ParsedSettings(),
to prevent the callers from mistakenly treating those patterns as option names,
with unpredicted consequences.

Co-authored-by: Dmytro Maluka <dmitrymaluka@gmail.com>
2026-03-18 19:46:36 +01:00
Jöran Karl
6760768b9e micro: Rearrange signal creation (#4027)
Otherwise we can't properly react upon screen events or signals created
within early plugin functions.
2026-03-17 20:45:10 +01:00
Jöran Karl
42d0ddf73d Merge pull request #4025 from injust/patch-1
Fix unescaped backslashes and expand git-config filename pattern to include `*/git/config`
2026-03-09 10:01:41 +01:00
Justin Su
5ddb05b5ec Apply suggestion from @Andriamanitra
Co-authored-by: Mikko <Andriamanitra@users.noreply.github.com>
2026-03-08 20:02:59 -04:00
Justin Su
ad43a5da99 Replace [\/] with [\\/] 2026-03-08 16:32:24 -04:00
Justin Su
dc10f6d53c Convert filename regexes to single-quoted strings 2026-03-08 16:31:24 -04:00
Kenny Wottrich
518a274980 Add detection for SWAG nginx proxy confs (#4036)
* Add detection for SWAG nginx proxy confs

These nginx config files have the format "appname.subdomain.conf" or "appname.subfolder.conf"

* Excape dot in regex

Co-authored-by: Mikko <Andriamanitra@users.noreply.github.com>

---------

Co-authored-by: Mikko <Andriamanitra@users.noreply.github.com>
2026-03-08 20:24:49 +01:00
Justin Su
184dd259e9 Match backslash too 2026-02-26 14:27:38 -05:00
Justin Su
f1a7f0d392 Expand git-config filename pattern to include */git/config 2026-02-26 06:36:51 -05:00
usfbih8u
d38f0dfe7a Fix documentation typo (#4006)
Co-authored-by: usfbih8u <>
2026-02-12 20:19:21 +01:00
Dmytro Maluka
4aa706cbc5 serialize: Don't save undo stack if saveundo=off (#4003)
Micro always saves the undo stack information into the serialized buffer
file as long as either `saveundo` or `savecursor` is enabled. Whereas in
the case when only `savecursor` is enabled, while `saveundo` is
disabled, this information is not used afterwards, so it only wastes the
disk space in `~/.config/micro/buffers`. (And given that currently micro
never automatically removes any serialized buffer files, it may
significantly contribute to the overall ever growing size of the
`~/.config/micro/buffers` directory.)

So avoid saving the undo info if `saveundo` is disabled. This makes the
size of each serialized buffer file with savecursor=on saveundo=off
small and predictable, e.g. around 600 bytes in my observations (whereas
without this fix, it may grow indefinitely big, depending on the number
of modifications the user made before saving the file).
2026-02-10 20:32:16 +01:00
Jöran Karl
1317a2deb1 Merge pull request #3983 from Neko-Box-Coder/FixRootSplitBug
Fixing missing case for handling root node for splitting
2026-02-09 18:39:01 +01:00
Neko Box Coder
fda43aff15 Adding special case for root node for flatten() 2026-02-08 21:57:50 +00:00
Neko Box Coder
7ef8ca476d Fixing missing case for handling root node for splitting, fixes #3980 2026-02-08 21:57:50 +00:00
hemmingsv
3a7403bde4 feat(textfilter): Select output if input was from selection (#3974) 2026-01-28 19:41:51 +01:00
12 changed files with 102 additions and 33 deletions

View File

@@ -363,6 +363,12 @@ func main() {
fmt.Println("Fatal: Micro could not initialize a Screen.")
exit(1)
}
util.Sigterm = make(chan os.Signal, 1)
sighup = make(chan os.Signal, 1)
signal.Notify(util.Sigterm, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGABRT)
signal.Notify(sighup, syscall.SIGHUP)
m := clipboard.SetMethod(config.GetGlobalOption("clipboard").(string))
clipErr := clipboard.Initialize(m)
@@ -400,6 +406,8 @@ func main() {
action.InitBindings()
action.InitCommands()
timerChan = make(chan func())
err = config.RunPluginFn("preinit")
if err != nil {
screen.TermMessage(err)
@@ -444,13 +452,6 @@ func main() {
screen.Events = make(chan tcell.Event)
util.Sigterm = make(chan os.Signal, 1)
sighup = make(chan os.Signal, 1)
signal.Notify(util.Sigterm, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGABRT)
signal.Notify(sighup, syscall.SIGHUP)
timerChan = make(chan func())
// Here is the event loop which runs in a separate thread
go func() {
for {

View File

@@ -141,9 +141,11 @@ func (h *BufPane) TextFilterCmd(args []string) {
InfoBar.Error("usage: textfilter arguments")
return
}
for _, c := range h.Buf.GetCursors() {
sel := c.GetSelection()
if len(sel) == 0 {
fromSelection := len(sel) > 0
if !fromSelection {
c.SelectWord()
sel = c.GetSelection()
}
@@ -158,7 +160,18 @@ func (h *BufPane) TextFilterCmd(args []string) {
return
}
c.DeleteSelection()
h.Buf.Insert(c.Loc, bout.String())
insertStart := c.Loc
insertedText := bout.String()
h.Buf.Insert(c.Loc, insertedText)
if fromSelection {
// Select the pasted output if the input was selected
charCount := util.CharacterCountInString(insertedText)
insertEnd := insertStart.Move(charCount, h.Buf)
c.SetSelectionStart(insertStart)
c.SetSelectionEnd(insertEnd)
c.Loc = insertEnd
}
}
}

View File

@@ -29,12 +29,16 @@ func (b *Buffer) Serialize() error {
return nil
}
buffer := SerializedBuffer{
Cursor: b.GetActiveCursor().Loc,
ModTime: b.ModTime,
}
if b.Settings["saveundo"].(bool) {
buffer.EventHandler = b.EventHandler
}
var buf bytes.Buffer
err := gob.NewEncoder(&buf).Encode(SerializedBuffer{
b.EventHandler,
b.GetActiveCursor().Loc,
b.ModTime,
})
err := gob.NewEncoder(&buf).Encode(buffer)
if err != nil {
return err
}
@@ -76,7 +80,7 @@ func (b *Buffer) Unserialize() error {
b.StartCursor = buffer.Cursor
}
if b.Settings["saveundo"].(bool) {
if b.Settings["saveundo"].(bool) && buffer.EventHandler != nil {
// We should only use last time's eventhandler if the file wasn't modified by someone else in the meantime
if b.ModTime == buffer.ModTime {
b.EventHandler = buffer.EventHandler

View File

@@ -186,11 +186,19 @@ func validateParsedSettings() error {
}
}
} else {
if _, e := glob.Compile(k); e != nil {
err = errors.New("Error with glob setting " + k + ": " + e.Error())
tk := strings.TrimPrefix(k, "glob:")
if _, e := glob.Compile(tk); e != nil {
err = errors.New("Error with glob setting " + tk + ": " + e.Error())
delete(parsedSettings, k)
continue
}
if !strings.HasPrefix(k, "glob:") {
// Support non-prefixed glob settings but internally convert
// them to prefixed ones for simplicity.
delete(parsedSettings, k)
k = "glob:" + k
parsedSettings[k] = v
}
for k1, v1 := range v.(map[string]any) {
if _, ok := defaults[k1]; ok {
if e := verifySetting(k1, v1, defaults[k1]); e != nil {
@@ -256,6 +264,9 @@ func ReadSettings() error {
func ParsedSettings() map[string]any {
s := make(map[string]any)
for k, v := range parsedSettings {
if strings.HasPrefix(reflect.TypeOf(v).String(), "map") {
continue
}
s[k] = v
}
return s
@@ -309,8 +320,9 @@ func InitGlobalSettings() error {
// Must be called after ReadSettings
func UpdatePathGlobLocals(settings map[string]any, path string) {
for k, v := range parsedSettings {
if strings.HasPrefix(reflect.TypeOf(v).String(), "map") && !strings.HasPrefix(k, "ft:") {
g, _ := glob.Compile(k)
if strings.HasPrefix(reflect.TypeOf(v).String(), "map") && strings.HasPrefix(k, "glob:") {
tk := strings.TrimPrefix(k, "glob:")
g, _ := glob.Compile(tk)
if g.MatchString(path) {
for k1, v1 := range v.(map[string]any) {
settings[k1] = v1

View File

@@ -79,7 +79,7 @@ func (w *BufWindow) GetView() *View {
return w.View
}
// GetView sets the view.
// SetView sets the view.
func (w *BufWindow) SetView(view *View) {
w.View = view
}

View File

@@ -413,7 +413,7 @@ func (n *Node) HSplit(bottom bool) uint64 {
if !n.IsLeaf() {
return 0
}
if n.Kind == STUndef {
if n.parent == nil {
n.Kind = STVert
}
if n.Kind == STVert {
@@ -429,13 +429,13 @@ func (n *Node) VSplit(right bool) uint64 {
if !n.IsLeaf() {
return 0
}
if n.Kind == STUndef {
if n.parent == nil {
n.Kind = STHoriz
}
if n.Kind == STVert {
return n.vVSplit(right)
if n.Kind == STHoriz {
return n.hVSplit(0, right)
}
return n.hVSplit(0, right)
return n.vVSplit(right)
}
// unsplits the child of a split
@@ -483,7 +483,20 @@ func (n *Node) Unsplit() bool {
// flattens the tree by removing unnecessary intermediate parents that have only one child
// and handles the side effect of it
func (n *Node) flatten() {
if n.parent == nil || len(n.children) != 1 {
if len(n.children) != 1 {
return
}
// Special case for root node
if n.parent == nil {
*n = *n.children[0]
n.parent = nil
for _, c := range n.children {
c.parent = n
}
if len(n.children) == 0 {
n.Kind = STUndef
}
return
}
@@ -531,11 +544,19 @@ func (n *Node) flatten() {
func (n *Node) String() string {
var strf func(n *Node, ident int) string
strf = func(n *Node, ident int) string {
marker := "|"
marker := ""
if n.Kind == STHoriz {
marker = "-"
} else if n.Kind == STVert {
marker = "|"
}
str := fmt.Sprint(strings.Repeat("\t", ident), marker, n.View, n.id)
var parentId uint64 = 0
if n.parent != nil {
parentId = n.parent.id
}
str := fmt.Sprint(strings.Repeat("\t", ident), marker, n.View, n.id, parentId)
if n.IsLeaf() {
str += "🍁"
}

View File

@@ -669,6 +669,21 @@ all files except Go files, and `tabsize` 4 for all files except Ruby files:
Or similarly you can match with globs:
```json
{
"glob:*.go": {
"tabstospaces": false
},
"glob:*.rb": {
"tabsize": 2
},
"tabstospaces": true,
"tabsize": 4
}
```
You can also omit the `glob:` prefix before globs:
```json
{
"*.go": {
@@ -681,3 +696,6 @@ Or similarly you can match with globs:
"tabsize": 4
}
```
But it is generally more recommended to use the `glob:` prefix, as it avoids
potential conflicts with option names.

View File

@@ -1,7 +1,7 @@
filetype: git-commit
detect:
filename: "^(.*[\\/])?(COMMIT_EDITMSG|TAG_EDITMSG|MERGE_MSG)$"
filename: '^(.*[\\/])?(COMMIT_EDITMSG|TAG_EDITMSG|MERGE_MSG)$'
rules:
# File changes

View File

@@ -1,7 +1,7 @@
filetype: git-config
detect:
filename: "git(config|modules)$|\\.git/config$"
filename: 'git(config|modules)$|^(.*[\\/])?\.?git[\\/]config$'
rules:
- constant: "\\<(true|false)\\>"

View File

@@ -1,7 +1,7 @@
filetype: git-rebase-todo
detect:
filename: "^(.*[\\/])?git\\-rebase\\-todo$"
filename: '^(.*[\\/])?git\-rebase\-todo$'
rules:
# Rebase commands

View File

@@ -1,7 +1,7 @@
filetype: nginx
detect:
filename: "nginx.*\\.conf$|\\.nginx$"
filename: "nginx.*\\.conf$|\\.nginx$|\\.sub(domain|folder)\\.conf$"
header: "^(server|upstream)[a-z ]*\\{$"
rules:

View File

@@ -1,7 +1,7 @@
filetype: ruby
detect:
filename: "\\.(rb|rake|gemspec)$|^(.*[\\/])?(Gemfile|config.ru|Rakefile|Capfile|Vagrantfile|Guardfile|Appfile|Fastfile|Pluginfile|Podfile|\\.?[Bb]rewfile)$"
filename: '\.(rb|rake|gemspec)$|^(.*[\\/])?(Gemfile|config\.ru|Rakefile|Capfile|Vagrantfile|Guardfile|Appfile|Fastfile|Pluginfile|Podfile|\.?[Bb]rewfile)$'
header: "^#!.*/(env +)?ruby( |$)"
rules: