summaryrefslogtreecommitdiff
path: root/.config/fish/functions/mm.fish
blob: 4c6c470d897d16ed46974d71f4ac67bc3525e7d4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
function mm --description "MakeMeFish - List all Make targets in the Makefile of the current directory"
    
    set current_pos 1 
    while test (count $argv) -ge $current_pos
        # Check if a help flag was passed to mm
        set help_flags -- -h --help
        if contains -- $argv[$current_pos] $help_flags
            echo ""
            echo "    Usage:"
            echo "      " (set_color green)"mm"(set_color normal) "will look for a Makefile in the order specified by GNU Make and list all targets in it."
            echo "      " "To filter for a specific target, just start typing and targets will be filtered as you type."
            echo "      " (set_color green)"mm <keyword>"(set_color normal) "will start MakeMeFish with an initial, editable query" (set_color green)"<keyword>"(set_color normal)
            echo "      " (set_color green)"mm -i"(set_color normal) "will start MakeMeFish in interactive mode. When a target is run, you will return to the selection menu."
            echo "      " (set_color green)"mm -f <filename>"(set_color normal) "to specify what Makefile to load."
            echo "      " "All flags can be combined in any order."
            echo ""
            return 0
        else if test $argv[$current_pos] = "-f" 
            set current_pos (math "$current_pos+1") # skip the next
            set filename $argv[$current_pos]
        else if test $argv[$current_pos] = "-i" 
            set interactive 1
        else
            if set -q initial_query
                set initial_query $initial_query $argv[$current_pos]
            else
                set initial_query $argv[$current_pos]
            end
        end
        set current_pos (math "$current_pos+1")
    end

    function __mm_get_makefile_name -a 'filename'
        if test -n "$filename"
            set makefile_filenames $filename
        else 
            set makefile_filenames 'GNUmakefile' 'makefile' 'Makefile'
        end
        for filename in $makefile_filenames 
            if test -f $filename
                echo $filename
                break
            end
        end
    end

    # Based on: 
    # https://github.com/fish-shell/fish-shell/blob/8e418f5205106b11f83fa1956076a9b20c56f0f9/share/completions/make.fish 
    # and 
    # https://stackoverflow.com/a/26339924
    function __mm_parse_makefile -a 'filename'
        # Ensure correct locale set
        set -lx LC_ALL C

        set makeflags -f $filename
        
        # first awk merges any line that ends with a backslash with the next line
        if make --version 2>/dev/null | string match -q 'GNU*'
            make $makeflags -pRrq : 2>/dev/null |
            awk '{if (sub(/\\\$/,"")) printf "%s", $0; else print $0}' |
            awk -F: '/^# Files/,/^# Finished Make data base/ {
                        if ($1 == "# Not a target") skip = 1;
                        if ($1 !~ "^[#.\t]") { 
                            if (!skip) print $1; skip=0 
                        }
                    }' 2>/dev/null
        else
            # BSD make
            make $makeflags -d g1 -rn >/dev/null 2>| awk -F, '/^#\*\*\* Input graph:/,/^$/ {if ($1 !~ "^#... ") {gsub(/# /,"",$1); print $1}}' 2>/dev/null
        end
    end

    function __mm_get_targets -a 'filename'
        set static_targets
        set file_targets
        set generated_targets 
        
        set parsed_makefile (__mm_parse_makefile $filename | sort -f)
        for row in $parsed_makefile  # Loop over all rows in the Makefile
            set row (string trim $row)
            if test -n "$row"  # No blanks plz
                if test (string match -r '.\.|\/' $row)  # this is a file or path
                    set file_targets $file_targets $row
                else  # grep the target and see if it's generated by a function or a true target 
                    set found_in_file (grep "$row:" $filename)
                    if test -n "$found_in_file"
                        set static_targets $static_targets $row  # true target
                    else
                        set generated_targets $generated_targets $row  # generated by function
                    end
                end
            end
        end
    
        string split " " $static_targets $file_targets $generated_targets
    end

    function __mm_fzf_command -a 'filename' -a 'interactive' -a 'make_command' -a 'query'
        if [ $interactive -eq 1 ]
            set fzf_interactive "--bind \"enter:execute:$make_command {}; echo; echo 'Done'; sleep 1\""
        end

        if test -n "$query"
            set fzf_query "--query=$query"
        end
        set fzf_opts "--read0 
                      $fzf_query 
                      $fzf_interactive
                      --height 60% 
                      --layout=reverse 
                      --border 
                      --preview-window='right:60%' 
                      --preview='grep 
                      --color=always -A 10 -B 1 \^{}: $filename; or echo -GENERATED TARGET-'"
        
        set -q FZF_TMUX; or set FZF_TMUX 0
        set -q FZF_TMUX_HEIGHT; or set FZF_TMUX_HEIGHT 60%
        if [ $FZF_TMUX -eq 1 ]
            echo "fzf-tmux -d$FZF_TMUX_HEIGHT $fzf_opts"
        else
            echo "fzf $fzf_opts"
        end
    end

    if __mm_dependencies
        set custom_filename $filename
        set filename (__mm_get_makefile_name $filename)
        if test -z "$filename"
            echo 'No makefile found in the current working directory'
        else
            set targets (__mm_get_targets $filename)
            if test -n "$targets"
                if test -n "$custom_filename"
                    set make_command "make -f $filename"
                else
                    set make_command "make"
                end
                # Interactive?
                if test -n "$interactive"; and test $interactive -eq 1
                    string join0 -- $targets | eval (__mm_fzf_command $filename 1 $make_command $initial_query)
                else
                    string join0 -- $targets | eval (__mm_fzf_command $filename 0 $make_command $initial_query) | read -lz result  # print targets as a list, pipe them to fzf, put the chosen command in $result
                    set result (string trim -- $result)  # Trim newlines and whitespace from the command
                    and commandline -- "$make_command $result"  # Prepend the make command
                    commandline -f repaint  # Repaint command line
                end
            else
                echo "No targets found in $filename"
            end
        end
    end

end