line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Text::FindIndent; |
2
|
|
|
|
|
|
|
# -*- mode: Perl -*- |
3
|
|
|
|
|
|
|
# Emacs mode is necessary for https://github.com/github/linguist/issues/2458 |
4
|
|
|
|
|
|
|
|
5
|
2
|
|
|
2
|
|
24927
|
use 5.00503; |
|
2
|
|
|
|
|
7
|
|
6
|
2
|
|
|
2
|
|
12
|
use strict; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
53
|
|
7
|
|
|
|
|
|
|
|
8
|
2
|
|
|
2
|
|
18
|
use vars qw{$VERSION}; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
109
|
|
9
|
|
|
|
|
|
|
BEGIN { |
10
|
2
|
|
|
2
|
|
40
|
$VERSION = '0.11'; |
11
|
|
|
|
|
|
|
} |
12
|
|
|
|
|
|
|
|
13
|
2
|
|
|
2
|
|
9
|
use constant MAX_LINES => 500; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
7059
|
|
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
sub parse { |
16
|
21
|
|
|
21
|
1
|
23595
|
my $class = shift; |
17
|
21
|
|
|
|
|
34
|
my $text = shift; |
18
|
|
|
|
|
|
|
|
19
|
21
|
|
|
|
|
62
|
my %opts = @_; |
20
|
21
|
50
|
|
|
|
62
|
my $textref = ref($text) ? $text : \$text; # accept references, too |
21
|
|
|
|
|
|
|
|
22
|
21
|
|
|
|
|
37
|
my $skip_pod = $opts{skip_pod}; |
23
|
21
|
50
|
|
|
|
52
|
my $first_level_indent_only = $opts{first_level_indent_only}?1:0; |
24
|
|
|
|
|
|
|
|
25
|
21
|
|
|
|
|
32
|
my %modeline_settings; |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
my %indentdiffs; |
28
|
21
|
|
|
|
|
28
|
my $lines = 0; |
29
|
21
|
|
|
|
|
30
|
my $prev_indent = undef; |
30
|
21
|
|
|
|
|
26
|
my $skip = 0; |
31
|
21
|
|
|
|
|
27
|
my $in_pod = 0; |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
# Do we have emacs smart comments? |
34
|
21
|
|
|
|
|
68
|
$class->_check_emacs_local_variables_at_file_end($textref, \%modeline_settings); |
35
|
21
|
50
|
33
|
|
|
160
|
if (exists $modeline_settings{softtabstop} and exists $modeline_settings{usetabs}) { |
|
|
50
|
66
|
|
|
|
|
|
|
100
|
66
|
|
|
|
|
36
|
|
|
|
|
|
|
$modeline_settings{mixedmode} = $modeline_settings{usetabs} |
37
|
0
|
0
|
|
|
|
0
|
if not defined $modeline_settings{mixedmode}; |
38
|
|
|
|
|
|
|
return( |
39
|
|
|
|
|
|
|
($modeline_settings{mixedmode} ? "m" : "s") |
40
|
|
|
|
|
|
|
. $modeline_settings{softtabstop} |
41
|
0
|
0
|
|
|
|
0
|
); |
42
|
|
|
|
|
|
|
} |
43
|
|
|
|
|
|
|
elsif (exists $modeline_settings{tabstop} and $modeline_settings{usetabs}) { |
44
|
0
|
0
|
|
|
|
0
|
return( ($modeline_settings{mixedmode} ? "m" : "t") . $modeline_settings{tabstop} ); |
45
|
|
|
|
|
|
|
} |
46
|
|
|
|
|
|
|
elsif (exists $modeline_settings{tabstop} and exists $modeline_settings{usetabs}) { |
47
|
1
|
|
|
|
|
6
|
return( "s" . $modeline_settings{tabstop} ); |
48
|
|
|
|
|
|
|
} |
49
|
|
|
|
|
|
|
|
50
|
20
|
|
|
|
|
25
|
my $next_line_braces_pos_plus_1; |
51
|
20
|
|
|
|
|
23
|
my $prev_indent_type = undef; |
52
|
20
|
|
|
|
|
91
|
while ($$textref =~ /\G([ \t]*)([^\r\n]*)[\r\n]+/cgs) { |
53
|
404
|
|
|
|
|
728
|
my $ws = $1; |
54
|
404
|
|
|
|
|
623
|
my $rest = $2; |
55
|
404
|
|
|
|
|
603
|
my $fullline = "$ws$rest"; |
56
|
404
|
|
|
|
|
444
|
$lines++; |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
# check emacs start line stuff with some slack (shebang) |
59
|
404
|
|
|
|
|
434
|
my $changed_modelines; |
60
|
404
|
100
|
|
|
|
811
|
if ($lines < 3) { |
61
|
37
|
|
|
|
|
102
|
$changed_modelines = $class->_check_emacs_local_variables_first_line($fullline, \%modeline_settings); |
62
|
|
|
|
|
|
|
} |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
# Do we have emacs smart comments? |
65
|
|
|
|
|
|
|
# ==> Done once at start |
66
|
|
|
|
|
|
|
#$class->_check_emacs_local_variables($fullline, \%modeline_settings); |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
# Do we have vim smart comments? |
69
|
404
|
100
|
100
|
|
|
865
|
if ($class->_check_vim_modeline($fullline, \%modeline_settings) || $changed_modelines) { |
70
|
10
|
100
|
66
|
|
|
79
|
if (exists $modeline_settings{softtabstop} and exists $modeline_settings{usetabs}) { |
|
|
100
|
66
|
|
|
|
|
|
|
100
|
66
|
|
|
|
|
71
|
|
|
|
|
|
|
$modeline_settings{mixedmode} = $modeline_settings{usetabs} |
72
|
2
|
50
|
|
|
|
8
|
if not defined $modeline_settings{mixedmode}; |
73
|
|
|
|
|
|
|
return( |
74
|
|
|
|
|
|
|
($modeline_settings{mixedmode} ? "m" : "s") |
75
|
|
|
|
|
|
|
. $modeline_settings{softtabstop} |
76
|
2
|
100
|
|
|
|
19
|
); |
77
|
|
|
|
|
|
|
} |
78
|
|
|
|
|
|
|
elsif (exists $modeline_settings{tabstop} and $modeline_settings{usetabs}) { |
79
|
2
|
100
|
|
|
|
17
|
return( ($modeline_settings{mixedmode} ? "m" : "t") . $modeline_settings{tabstop} ); |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
elsif (exists $modeline_settings{tabstop} and exists $modeline_settings{usetabs}) { |
82
|
3
|
|
|
|
|
19
|
return( "s" . $modeline_settings{tabstop} ); |
83
|
|
|
|
|
|
|
} |
84
|
|
|
|
|
|
|
} |
85
|
|
|
|
|
|
|
|
86
|
397
|
50
|
|
|
|
775
|
if ($lines > MAX_LINES) { |
87
|
0
|
|
|
|
|
0
|
next; |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
|
90
|
397
|
50
|
|
|
|
734
|
if ($skip) { |
91
|
0
|
|
|
|
|
0
|
$skip--; |
92
|
0
|
|
|
|
|
0
|
next; |
93
|
|
|
|
|
|
|
} |
94
|
|
|
|
|
|
|
|
95
|
397
|
100
|
100
|
|
|
1025
|
if ($skip_pod and $ws eq '' and substr($rest, 0, 1) eq '=') { |
|
|
|
100
|
|
|
|
|
96
|
4
|
100
|
66
|
|
|
30
|
if (not $in_pod and $rest =~ /^=(?:head\d|over|item|back|pod|begin|for|end)/ ) { |
|
|
50
|
33
|
|
|
|
|
97
|
2
|
|
|
|
|
3
|
$in_pod = 1; |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
elsif ($in_pod and $rest =~ /^=cut/) { |
100
|
2
|
|
|
|
|
4
|
$in_pod = 0; |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
} |
104
|
397
|
100
|
100
|
|
|
1554
|
next if $in_pod or $rest eq ''; |
105
|
|
|
|
|
|
|
|
106
|
352
|
100
|
|
|
|
639
|
if ($ws eq '') { |
107
|
99
|
|
|
|
|
109
|
$prev_indent = $ws; |
108
|
99
|
|
|
|
|
364
|
next; |
109
|
|
|
|
|
|
|
} |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
# skip next line if the last char is a backslash. |
112
|
|
|
|
|
|
|
# Doesn't matter for Perl, but let's be generous! |
113
|
253
|
50
|
|
|
|
502
|
$skip = 1 if $rest =~ /\\$/; |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
# skip single-line comments |
116
|
253
|
50
|
|
|
|
623
|
next if $rest =~ /^(?:#|\/\/|\/\*)/; # TODO: parse /* ... */! |
117
|
|
|
|
|
|
|
|
118
|
253
|
100
|
|
|
|
408
|
if ($next_line_braces_pos_plus_1) { |
119
|
10
|
100
|
|
|
|
20
|
if ($next_line_braces_pos_plus_1==_length_with_tabs_converted($ws)) { |
120
|
8
|
|
|
|
|
32
|
next; |
121
|
|
|
|
|
|
|
} |
122
|
2
|
|
|
|
|
3
|
$next_line_braces_pos_plus_1=0; |
123
|
|
|
|
|
|
|
} else { |
124
|
243
|
100
|
|
|
|
521
|
if ($rest =~ /=> \{$/) { #handle case where hash keys and values are indented by braces pos + 1 |
125
|
2
|
|
|
|
|
6
|
$next_line_braces_pos_plus_1=_length_with_tabs_converted($ws)+length($rest); |
126
|
|
|
|
|
|
|
} |
127
|
|
|
|
|
|
|
} |
128
|
|
|
|
|
|
|
|
129
|
245
|
50
|
33
|
|
|
532
|
if ($first_level_indent_only and $prev_indent ne '') { |
130
|
0
|
|
|
|
|
0
|
next; |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
|
133
|
245
|
100
|
|
|
|
500
|
if ($prev_indent eq $ws) { |
134
|
70
|
50
|
|
|
|
127
|
if ($prev_indent_type) { |
135
|
70
|
|
|
|
|
117
|
$indentdiffs{$prev_indent_type}+=0.01; |
136
|
|
|
|
|
|
|
#coefficient is not based on data, so change if you think it should be different |
137
|
|
|
|
|
|
|
} |
138
|
70
|
|
|
|
|
282
|
next; |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
# prefix-matching higher indentation level |
142
|
175
|
100
|
|
|
|
2095
|
if ($ws =~ /^\Q$prev_indent\E(.+)$/) { |
143
|
75
|
|
|
|
|
128
|
my $diff = $1; |
144
|
75
|
|
|
|
|
115
|
my $indent_type=_analyse_indent_diff($diff); |
145
|
75
|
|
|
|
|
156
|
$indentdiffs{$indent_type}++; |
146
|
75
|
|
|
|
|
90
|
$prev_indent_type=$indent_type; |
147
|
75
|
|
|
|
|
87
|
$prev_indent = $ws; |
148
|
75
|
|
|
|
|
324
|
next; |
149
|
|
|
|
|
|
|
} |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
# prefix-matching lower indentation level |
152
|
100
|
100
|
|
|
|
929
|
if ($prev_indent =~ /^\Q$ws\E(.+)$/) { |
153
|
42
|
|
|
|
|
73
|
my $diff = $1; |
154
|
|
|
|
|
|
|
#_grok_indent_diff($diff, \%indentdiffs); |
155
|
42
|
|
|
|
|
72
|
my $indent_type=_analyse_indent_diff($diff); |
156
|
42
|
|
|
|
|
87
|
$indentdiffs{$indent_type}++; |
157
|
42
|
|
|
|
|
50
|
$prev_indent_type=$indent_type; |
158
|
42
|
|
|
|
|
57
|
$prev_indent = $ws; |
159
|
42
|
|
|
|
|
184
|
next; |
160
|
|
|
|
|
|
|
} |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
# at this point, we're desperate! |
163
|
58
|
|
|
|
|
91
|
my $prev_spaces = $prev_indent; |
164
|
58
|
|
|
|
|
215
|
$prev_spaces =~ s/[ ]{0,7}\t/ /g; |
165
|
58
|
|
|
|
|
86
|
my $spaces = $ws; |
166
|
58
|
|
|
|
|
196
|
$spaces =~ s/[ ]{0,7}\t/ /g; |
167
|
58
|
|
|
|
|
86
|
my $len_diff = length($spaces) - length($prev_spaces); |
168
|
58
|
50
|
|
|
|
126
|
if ($len_diff) { |
169
|
58
|
|
|
|
|
68
|
$len_diff = abs($len_diff); |
170
|
58
|
|
|
|
|
117
|
$indentdiffs{"m$len_diff"}++; |
171
|
|
|
|
|
|
|
} |
172
|
58
|
|
|
|
|
256
|
$prev_indent = $ws; |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
} # end while lines |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
# nothing found |
177
|
13
|
100
|
|
|
|
43
|
return 'u' if not keys %indentdiffs; |
178
|
|
|
|
|
|
|
|
179
|
12
|
|
|
|
|
16
|
my $max = 0; |
180
|
12
|
|
|
|
|
14
|
my $maxkey = undef; |
181
|
12
|
|
|
|
|
41
|
while (my ($key, $value) = each %indentdiffs) { |
182
|
27
|
100
|
|
|
|
116
|
$maxkey = $key, $max = $value if $value > $max; |
183
|
|
|
|
|
|
|
} |
184
|
|
|
|
|
|
|
|
185
|
12
|
100
|
|
|
|
36
|
if ($maxkey =~ /^s(\d+)$/) { |
186
|
8
|
|
|
|
|
20
|
my $mixedkey = "m" . $1; |
187
|
8
|
|
|
|
|
12
|
my $mixed = $indentdiffs{$mixedkey}; |
188
|
8
|
100
|
66
|
|
|
31
|
if (defined($mixed) and $mixed >= $max * 0.2) { |
189
|
3
|
|
|
|
|
6
|
$maxkey = $mixedkey; |
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
} |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
# fallback to emacs styles which are guesses only |
194
|
12
|
|
|
|
|
20
|
foreach my $key (qw(softtabstop tabstop usetabs)) { |
195
|
36
|
100
|
100
|
|
|
169
|
if (not exists $modeline_settings{$key} |
196
|
|
|
|
|
|
|
and exists $modeline_settings{"style_$key"}) { |
197
|
6
|
|
|
|
|
16
|
$modeline_settings{$key} = $modeline_settings{"style_$key"}; |
198
|
|
|
|
|
|
|
} |
199
|
|
|
|
|
|
|
} |
200
|
|
|
|
|
|
|
|
201
|
12
|
100
|
|
|
|
37
|
if (exists $modeline_settings{softtabstop}) { |
|
|
100
|
|
|
|
|
|
202
|
3
|
|
|
|
|
19
|
$maxkey =~ s/\d+/$modeline_settings{softtabstop}/; |
203
|
|
|
|
|
|
|
} |
204
|
|
|
|
|
|
|
elsif (exists $modeline_settings{tabstop}) { |
205
|
1
|
|
|
|
|
6
|
$maxkey =~ s/\d+/$modeline_settings{tabstop}/; |
206
|
|
|
|
|
|
|
} |
207
|
12
|
100
|
|
|
|
31
|
if (exists $modeline_settings{usetabs}) { |
208
|
2
|
50
|
|
|
|
7
|
if ($modeline_settings{usetabs}) { |
209
|
2
|
100
|
|
|
|
8
|
$maxkey =~ s/^(.)(\d+)$/$1 eq 'u' ? "t8" : ($2 == 8 ? "t8" : "m$2")/e; |
|
2
|
50
|
|
|
|
13
|
|
210
|
|
|
|
|
|
|
} |
211
|
|
|
|
|
|
|
else { |
212
|
0
|
|
|
|
|
0
|
$maxkey =~ s/^./m/; |
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
} |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
# mixedmode explicitly asked for |
217
|
12
|
50
|
|
|
|
24
|
if ($modeline_settings{mixedmode}) { |
218
|
0
|
|
|
|
|
0
|
$maxkey =~ s/^./m/; |
219
|
|
|
|
|
|
|
} |
220
|
|
|
|
|
|
|
|
221
|
12
|
|
|
|
|
64
|
return $maxkey; |
222
|
|
|
|
|
|
|
} |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
sub _length_with_tabs_converted { |
225
|
12
|
|
|
12
|
|
16
|
my $str=shift; |
226
|
12
|
|
50
|
|
|
39
|
my $tablen=shift || 8; |
227
|
12
|
|
|
|
|
40
|
$str =~ s/( +)$//; |
228
|
12
|
|
50
|
|
|
38
|
my $trailing_spaces = $1||''; |
229
|
12
|
|
|
|
|
14
|
$str =~ s/ +//g; # assume the spaces are all contained in tabs! |
230
|
12
|
|
|
|
|
38
|
return length($str)*$tablen+length($trailing_spaces); |
231
|
|
|
|
|
|
|
} |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
sub _grok_indent_diff { |
234
|
0
|
|
|
0
|
|
0
|
my $diff = shift; |
235
|
0
|
|
|
|
|
0
|
my $indentdiffs = shift; |
236
|
|
|
|
|
|
|
|
237
|
0
|
0
|
|
|
|
0
|
if ($diff =~ /^ +$/) { |
|
|
0
|
|
|
|
|
|
238
|
0
|
|
|
|
|
0
|
$indentdiffs->{"s" . length($diff)}++; |
239
|
|
|
|
|
|
|
} |
240
|
|
|
|
|
|
|
elsif ($diff =~ /^\t+$/) { |
241
|
0
|
|
|
|
|
0
|
$indentdiffs->{"t8"}++; # we can't infer what a tab means. Or rather, we need smarter code to do it |
242
|
|
|
|
|
|
|
} |
243
|
|
|
|
|
|
|
else { # mixed! |
244
|
0
|
|
|
|
|
0
|
$indentdiffs->{"m" . _length_with_tabs_converted($diff)}++; |
245
|
|
|
|
|
|
|
} |
246
|
|
|
|
|
|
|
} |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
sub _analyse_indent_diff { |
249
|
117
|
|
|
117
|
|
170
|
my $diff = shift; |
250
|
|
|
|
|
|
|
|
251
|
117
|
100
|
|
|
|
339
|
if ($diff =~ /^ +$/) { |
|
|
50
|
|
|
|
|
|
252
|
102
|
|
|
|
|
263
|
return "s" . length($diff); |
253
|
|
|
|
|
|
|
} |
254
|
|
|
|
|
|
|
elsif ($diff =~ /^\t+$/) { |
255
|
15
|
|
|
|
|
29
|
return "t8"; # we can't infer what a tab means. Or rather, we need smarter code to do it |
256
|
|
|
|
|
|
|
} |
257
|
|
|
|
|
|
|
else { # mixed! |
258
|
0
|
|
|
|
|
0
|
return "m" . _length_with_tabs_converted($diff); |
259
|
|
|
|
|
|
|
} |
260
|
|
|
|
|
|
|
} |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
{ |
263
|
|
|
|
|
|
|
# the vim modeline regexes |
264
|
|
|
|
|
|
|
my $VimTag = qr/(?:ex|vim?(?:[<=>]\d+)?):/; |
265
|
|
|
|
|
|
|
my $OptionArg = qr/[^\s\\]*(?:\\[\s\\][^\s\\]*)*/; |
266
|
|
|
|
|
|
|
my $VimOption = qr/ |
267
|
|
|
|
|
|
|
\w+(?:=)?$OptionArg |
268
|
|
|
|
|
|
|
/xo; |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
my $VimModeLineStart = qr/(?:^|\s+)$VimTag/o; |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
# while technically, we match against $VimModeLineStart before, |
273
|
|
|
|
|
|
|
# IF there is a vim modeline, we don't need to optimize |
274
|
|
|
|
|
|
|
my $VimModelineTypeOne = qr/ |
275
|
|
|
|
|
|
|
$VimModeLineStart |
276
|
|
|
|
|
|
|
\s* |
277
|
|
|
|
|
|
|
($VimOption |
278
|
|
|
|
|
|
|
(?: |
279
|
|
|
|
|
|
|
(?:\s*:\s*|\s+) |
280
|
|
|
|
|
|
|
$VimOption |
281
|
|
|
|
|
|
|
)* |
282
|
|
|
|
|
|
|
) |
283
|
|
|
|
|
|
|
\s*$ |
284
|
|
|
|
|
|
|
/xo; |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
my $VimModelineTypeTwo = qr/ |
287
|
|
|
|
|
|
|
$VimModeLineStart |
288
|
|
|
|
|
|
|
\s* |
289
|
|
|
|
|
|
|
set?\s+ |
290
|
|
|
|
|
|
|
($VimOption |
291
|
|
|
|
|
|
|
(?:\s+$VimOption)* |
292
|
|
|
|
|
|
|
) |
293
|
|
|
|
|
|
|
\s* |
294
|
|
|
|
|
|
|
: |
295
|
|
|
|
|
|
|
/xo; |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
sub _check_vim_modeline { |
298
|
404
|
|
|
404
|
|
557
|
my $class = shift; |
299
|
404
|
|
|
|
|
564
|
my $line = shift; |
300
|
404
|
|
|
|
|
491
|
my $settings = shift; |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
# Quoting the vim docs: |
303
|
|
|
|
|
|
|
# There are two forms of modelines. The first form: |
304
|
|
|
|
|
|
|
# [text]{white}{vi:|vim:|ex:}[white]{options} |
305
|
|
|
|
|
|
|
# |
306
|
|
|
|
|
|
|
#[text] any text or empty |
307
|
|
|
|
|
|
|
#{white} at least one blank character ( or ) |
308
|
|
|
|
|
|
|
#{vi:|vim:|ex:} the string "vi:", "vim:" or "ex:" |
309
|
|
|
|
|
|
|
#[white] optional white space |
310
|
|
|
|
|
|
|
#{options} a list of option settings, separated with white space or ':', |
311
|
|
|
|
|
|
|
# where each part between ':' is the argument for a ":set" |
312
|
|
|
|
|
|
|
# command (can be empty) |
313
|
|
|
|
|
|
|
# |
314
|
|
|
|
|
|
|
#Example: |
315
|
|
|
|
|
|
|
# vi:noai:sw=3 ts=6 ~ |
316
|
|
|
|
|
|
|
# The second form (this is compatible with some versions of Vi): |
317
|
|
|
|
|
|
|
# |
318
|
|
|
|
|
|
|
# [text]{white}{vi:|vim:|ex:}[white]se[t] {options}:[text] |
319
|
|
|
|
|
|
|
# |
320
|
|
|
|
|
|
|
#[text] any text or empty |
321
|
|
|
|
|
|
|
#{white} at least one blank character ( or ) |
322
|
|
|
|
|
|
|
#{vi:|vim:|ex:} the string "vi:", "vim:" or "ex:" |
323
|
|
|
|
|
|
|
#[white] optional white space |
324
|
|
|
|
|
|
|
#se[t] the string "set " or "se " (note the space) |
325
|
|
|
|
|
|
|
#{options} a list of options, separated with white space, which is the |
326
|
|
|
|
|
|
|
# argument for a ":set" command |
327
|
|
|
|
|
|
|
#: a colon |
328
|
|
|
|
|
|
|
#[text] any text or empty |
329
|
|
|
|
|
|
|
# |
330
|
|
|
|
|
|
|
#Example: |
331
|
|
|
|
|
|
|
# /* vim: set ai tw=75: */ ~ |
332
|
|
|
|
|
|
|
# |
333
|
|
|
|
|
|
|
|
334
|
404
|
|
|
|
|
415
|
my @options; |
335
|
404
|
100
|
|
|
|
3126
|
return if $line !~ $VimModeLineStart; |
336
|
|
|
|
|
|
|
|
337
|
6
|
100
|
|
|
|
600
|
if ($line =~ $VimModelineTypeOne) { |
|
|
50
|
|
|
|
|
|
338
|
2
|
|
|
|
|
12
|
push @options, split /(?!<\\)[:\s]+/, $1; |
339
|
|
|
|
|
|
|
} |
340
|
|
|
|
|
|
|
elsif ($line =~ $VimModelineTypeTwo) { |
341
|
4
|
|
|
|
|
22
|
push @options, split /(?!<\\)\s+/, $1; |
342
|
|
|
|
|
|
|
} |
343
|
|
|
|
|
|
|
else { |
344
|
0
|
|
|
|
|
0
|
return; |
345
|
|
|
|
|
|
|
} |
346
|
|
|
|
|
|
|
|
347
|
6
|
50
|
|
|
|
18
|
return if not @options; |
348
|
|
|
|
|
|
|
|
349
|
6
|
|
|
|
|
8
|
my $changed = 0; |
350
|
6
|
|
|
|
|
26
|
foreach (@options) { |
351
|
14
|
100
|
|
|
|
48
|
/s(?:ts|ofttabstop)=(\d+)/i and $settings->{softtabstop} = $1, $changed = 1, next; |
352
|
11
|
100
|
|
|
|
45
|
/t(?:s|abstop)=(\d+)/i and $settings->{tabstop} = $1, $changed = 1, next; |
353
|
7
|
100
|
33
|
|
|
56
|
/((?:no)?)(?:expandtab|et)/i and $settings->{usetabs} = (defined $1 and $1 =~ /no/i ? 1 : 0), $changed = 1, next; |
354
|
|
|
|
|
|
|
} |
355
|
6
|
|
|
|
|
24
|
return $changed; |
356
|
|
|
|
|
|
|
} |
357
|
|
|
|
|
|
|
} |
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
{ |
363
|
|
|
|
|
|
|
# lookup for emacs tab modes |
364
|
|
|
|
|
|
|
my %tabmodelookup = ( |
365
|
|
|
|
|
|
|
t => sub { |
366
|
|
|
|
|
|
|
$_[0]->{mixedmode} = 1; |
367
|
|
|
|
|
|
|
$_[0]->{usetabs} = 1; |
368
|
|
|
|
|
|
|
}, |
369
|
|
|
|
|
|
|
nil => sub { |
370
|
|
|
|
|
|
|
delete $_[0]->{mixedmode}; |
371
|
|
|
|
|
|
|
$_[0]->{usetabs} = 0; |
372
|
|
|
|
|
|
|
}, |
373
|
|
|
|
|
|
|
); |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
# lookup for emacs styles |
376
|
|
|
|
|
|
|
my %stylelookup = ( |
377
|
|
|
|
|
|
|
kr => sub { |
378
|
|
|
|
|
|
|
$_[0]->{style_softtabstop} = 4; |
379
|
|
|
|
|
|
|
$_[0]->{style_tabstop} = 8; |
380
|
|
|
|
|
|
|
$_[0]->{style_usetabs} = 1; |
381
|
|
|
|
|
|
|
}, |
382
|
|
|
|
|
|
|
linux => sub { |
383
|
|
|
|
|
|
|
$_[0]->{style_softtabstop} = 8; |
384
|
|
|
|
|
|
|
$_[0]->{style_tabstop} = 8; |
385
|
|
|
|
|
|
|
$_[0]->{style_usetabs} = 1; |
386
|
|
|
|
|
|
|
}, |
387
|
|
|
|
|
|
|
'gnu' => sub { |
388
|
|
|
|
|
|
|
$_[0]->{style_softtabstop} = 2; |
389
|
|
|
|
|
|
|
$_[0]->{style_tabstop} = 8; |
390
|
|
|
|
|
|
|
$_[0]->{style_usetabs} = 1; |
391
|
|
|
|
|
|
|
}, |
392
|
|
|
|
|
|
|
'bsd' => sub { |
393
|
|
|
|
|
|
|
$_[0]->{style_softtabstop} = 4; |
394
|
|
|
|
|
|
|
$_[0]->{style_tabstop} = 8; |
395
|
|
|
|
|
|
|
$_[0]->{style_usetabs} = 1; |
396
|
|
|
|
|
|
|
}, |
397
|
|
|
|
|
|
|
'ellemtel' => sub { |
398
|
|
|
|
|
|
|
$_[0]->{style_softtabstop} = 3; |
399
|
|
|
|
|
|
|
$_[0]->{style_tabstop} = 3; |
400
|
|
|
|
|
|
|
$_[0]->{style_usetabs} = 0; |
401
|
|
|
|
|
|
|
}, |
402
|
|
|
|
|
|
|
'java' => sub { |
403
|
|
|
|
|
|
|
$_[0]->{style_softtabstop} = 4; |
404
|
|
|
|
|
|
|
$_[0]->{style_tabstop} = 8; |
405
|
|
|
|
|
|
|
}, |
406
|
|
|
|
|
|
|
); |
407
|
|
|
|
|
|
|
$stylelookup{'k&r'} = $stylelookup{kr}; |
408
|
|
|
|
|
|
|
$stylelookup{'bsd'} = $stylelookup{kr}; |
409
|
|
|
|
|
|
|
$stylelookup{'whitesmith'} = $stylelookup{kr}; |
410
|
|
|
|
|
|
|
$stylelookup{'stroustrup'} = $stylelookup{kr}; |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
my $FirstLineVar = qr/[^\s:]+/; |
413
|
|
|
|
|
|
|
my $FirstLineValue = qr/[^;]+/; # dumb |
414
|
|
|
|
|
|
|
my $FirstLinePair = qr/\s*$FirstLineVar\s*:\s*$FirstLineValue;/o; |
415
|
|
|
|
|
|
|
my $FirstLineRegexp = qr/-\*-\s*mode:\s*[^\s;]+;\s*($FirstLinePair+)\s*-\*-/o; |
416
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
sub _check_emacs_local_variables_first_line { |
419
|
37
|
|
|
37
|
|
52
|
my $class = shift; |
420
|
37
|
|
|
|
|
54
|
my $line = shift; |
421
|
37
|
|
|
|
|
47
|
my $settings = shift; |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
# on first line (second if there is a shebang): |
424
|
|
|
|
|
|
|
# -*- mode: $MODENAME; $VARNAME: $VALUE; ... -*- |
425
|
|
|
|
|
|
|
# ($FOO is not a literal) |
426
|
|
|
|
|
|
|
# Example with a Lisp comment: |
427
|
|
|
|
|
|
|
# ;; -*- mode: Lisp; fill-column: 75; comment-column: 50; -*- |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
|
430
|
37
|
|
|
|
|
44
|
my $changed = 0; |
431
|
37
|
100
|
|
|
|
180
|
if ($line =~ $FirstLineRegexp) { |
432
|
4
|
|
|
|
|
28
|
my @pairs = split /\s*;\s*/, $1; |
433
|
4
|
|
|
|
|
8
|
foreach my $pair (@pairs) { |
434
|
7
|
|
|
|
|
34
|
my ($key, $value) = split /\s*:\s*/, $pair, 2; |
435
|
7
|
100
|
|
|
|
33
|
if ($key eq 'tab-width') { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
436
|
1
|
|
|
|
|
3
|
$settings->{tabstop} = $value;# FIXME: check var |
437
|
1
|
|
|
|
|
3
|
$changed = 1; |
438
|
|
|
|
|
|
|
} |
439
|
|
|
|
|
|
|
elsif ($key eq 'indent-tabs-mode') { |
440
|
3
|
50
|
|
|
|
15
|
$tabmodelookup{$value}->($settings), $changed = 1 if defined $tabmodelookup{$value}; |
441
|
|
|
|
|
|
|
} |
442
|
|
|
|
|
|
|
elsif ($key eq 'c-basic-offset') { |
443
|
2
|
|
33
|
|
|
11
|
$settings->{tabstop} ||= $value; # tab-width takes precedence!? |
444
|
2
|
|
|
|
|
5
|
$changed = 1; |
445
|
|
|
|
|
|
|
} |
446
|
|
|
|
|
|
|
elsif ($key eq 'style') { # this is quite questionable practice... |
447
|
1
|
50
|
|
|
|
8
|
$stylelookup{$value}->($settings), $changed = 1 if defined $stylelookup{$value}; |
448
|
|
|
|
|
|
|
} |
449
|
|
|
|
|
|
|
} |
450
|
|
|
|
|
|
|
} |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
# do this only as a LAST resort! |
453
|
|
|
|
|
|
|
#$settings->{tabstop} = $settings->{style_tabstop} if not exists $settings->{tabstop}; |
454
|
|
|
|
|
|
|
#$settings->{softtabstop} = $settings->{style_softtabstop} if not exists $settings->{softtabstop}; |
455
|
|
|
|
|
|
|
#$settings->{usetabs} = $settings->{style_usetabs} if not exists $settings->{usetabs}; |
456
|
|
|
|
|
|
|
|
457
|
37
|
|
|
|
|
75
|
return $changed; |
458
|
|
|
|
|
|
|
} |
459
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
sub _check_emacs_local_variables { |
461
|
513
|
|
|
513
|
|
792
|
my $class = shift; |
462
|
513
|
|
|
|
|
906
|
my $line = shift; |
463
|
513
|
|
|
|
|
635
|
my $settings = shift; |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
# A local variables list goes near the end of the file, in the last page.[...] |
466
|
|
|
|
|
|
|
# The local variables list starts with a line containing the string `Local Variables:', |
467
|
|
|
|
|
|
|
# and ends with a line containing the string `End:'. In between come the variable names |
468
|
|
|
|
|
|
|
# and values, one set per line, as `variable: value'. The values are not evaluated; |
469
|
|
|
|
|
|
|
# they are used literally. If a file has both a local variables list and a `-*-' |
470
|
|
|
|
|
|
|
# line, Emacs processes everything in the `-*-' line first, and everything in the |
471
|
|
|
|
|
|
|
# local variables list afterward. |
472
|
|
|
|
|
|
|
# |
473
|
|
|
|
|
|
|
# Here is an example of a local variables list: |
474
|
|
|
|
|
|
|
# |
475
|
|
|
|
|
|
|
# ;; Local Variables: ** |
476
|
|
|
|
|
|
|
# ;; mode:lisp ** |
477
|
|
|
|
|
|
|
# ;; comment-column:0 ** |
478
|
|
|
|
|
|
|
# ;; comment-start: ";; " ** |
479
|
|
|
|
|
|
|
# ;; comment-end:"**" ** |
480
|
|
|
|
|
|
|
# ;; End: ** |
481
|
|
|
|
|
|
|
# |
482
|
|
|
|
|
|
|
# Each line starts with the prefix `;; ' and each line ends with the suffix ` **'. |
483
|
|
|
|
|
|
|
# Emacs recognizes these as the prefix and suffix based on the first line of the |
484
|
|
|
|
|
|
|
# list, by finding them surrounding the magic string `Local Variables:'; then it |
485
|
|
|
|
|
|
|
# automatically discards them from the other lines of the list. |
486
|
|
|
|
|
|
|
# |
487
|
|
|
|
|
|
|
# The usual reason for using a prefix and/or suffix is to embed the local variables |
488
|
|
|
|
|
|
|
# list in a comment, so it won't confuse other programs that the file is intended as |
489
|
|
|
|
|
|
|
# input for. The example above is for a language where comment lines start with `;; ' |
490
|
|
|
|
|
|
|
# and end with `**'; the local values for comment-start and comment-end customize the |
491
|
|
|
|
|
|
|
# rest of Emacs for this unusual syntax. Don't use a prefix (or a suffix) if you don't need one. |
492
|
|
|
|
|
|
|
# |
493
|
|
|
|
|
|
|
# |
494
|
|
|
|
|
|
|
# Can it be any more annoying to parse? --Steffen |
495
|
|
|
|
|
|
|
|
496
|
513
|
100
|
|
|
|
3024
|
if ($settings->{in_local_variables_section}) { |
|
|
100
|
|
|
|
|
|
497
|
5
|
|
|
|
|
9
|
my $prefix = $settings->{local_variable_prefix}; |
498
|
5
|
50
|
|
|
|
13
|
$prefix = '' if not defined $prefix; |
499
|
5
|
|
|
|
|
8
|
$prefix = quotemeta($prefix); |
500
|
5
|
|
|
|
|
8
|
my $suffix = $settings->{local_variable_suffix}; |
501
|
5
|
50
|
|
|
|
11
|
$suffix = '' if not defined $suffix; |
502
|
5
|
|
|
|
|
6
|
$suffix = quotemeta($suffix); |
503
|
|
|
|
|
|
|
|
504
|
5
|
100
|
|
|
|
67
|
if ($line =~ /^\s*$prefix\s*([^\s:]+):\s*(.+)$suffix\s*$/) { |
505
|
3
|
|
|
|
|
6
|
my $key = $1; |
506
|
3
|
|
|
|
|
6
|
my $value = $2; |
507
|
3
|
|
|
|
|
7
|
$value =~ s/\s+$//; |
508
|
3
|
50
|
|
|
|
14
|
if ($key eq 'tab-width') { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
509
|
0
|
|
|
|
|
0
|
$settings->{tabstop} = $value; |
510
|
|
|
|
|
|
|
} |
511
|
|
|
|
|
|
|
elsif ($key eq 'indent-tabs-mode') { |
512
|
1
|
50
|
|
|
|
7
|
$tabmodelookup{$value}->($settings) if defined $tabmodelookup{$value}; |
513
|
|
|
|
|
|
|
} |
514
|
|
|
|
|
|
|
elsif ($key eq 'c-basic-offset') { |
515
|
1
|
|
33
|
|
|
71
|
$settings->{tabstop} ||= $value; # tab-width takes precedence!? |
516
|
|
|
|
|
|
|
} |
517
|
|
|
|
|
|
|
elsif ($key eq 'style') { # this is quite questionable practice... |
518
|
1
|
50
|
|
|
|
7
|
$stylelookup{$value}->($settings) if defined $stylelookup{$value}; |
519
|
|
|
|
|
|
|
} |
520
|
|
|
|
|
|
|
} # end if variable line |
521
|
|
|
|
|
|
|
else { |
522
|
2
|
|
|
|
|
4
|
delete $settings->{in_local_variables_section}; |
523
|
2
|
|
|
|
|
3
|
delete $settings->{local_variable_prefix}; |
524
|
2
|
|
|
|
|
9
|
delete $settings->{local_variable_suffix}; |
525
|
|
|
|
|
|
|
} |
526
|
|
|
|
|
|
|
} |
527
|
|
|
|
|
|
|
elsif ($line =~ /^\s*(\S*)\s*Local Variables:\s*(\S*)\s*$/) { |
528
|
2
|
|
|
|
|
8
|
$settings->{local_variable_prefix} = $1; |
529
|
2
|
|
|
|
|
4
|
$settings->{local_variable_suffix} = $2; |
530
|
2
|
|
|
|
|
11
|
$settings->{in_local_variables_section} = 1; |
531
|
|
|
|
|
|
|
} |
532
|
|
|
|
|
|
|
} |
533
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
sub _check_emacs_local_variables_at_file_end { |
535
|
21
|
|
|
21
|
|
35
|
my $class = shift; |
536
|
21
|
|
|
|
|
32
|
my $textref = shift; |
537
|
21
|
|
|
|
|
33
|
my $settings = shift; |
538
|
21
|
|
|
|
|
26
|
my $len = length($$textref); |
539
|
21
|
|
|
|
|
34
|
my $start = $len-3000; |
540
|
21
|
50
|
|
|
|
55
|
$start = 0 if $start < 0; |
541
|
21
|
|
|
|
|
64
|
my $text = substr($$textref, $start); |
542
|
|
|
|
|
|
|
|
543
|
21
|
|
|
|
|
129
|
while ($text =~ /\G[ \t]*([^\r\n]*)[\r\n]+/cgs) { |
544
|
513
|
|
|
|
|
1056
|
$class->_check_emacs_local_variables($1, $settings); |
545
|
|
|
|
|
|
|
} |
546
|
21
|
|
|
|
|
51
|
return; |
547
|
|
|
|
|
|
|
} |
548
|
|
|
|
|
|
|
} # end lexical block for emacs lookups |
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
sub to_vim_commands { |
552
|
0
|
|
|
0
|
1
|
|
my $indent = shift; |
553
|
0
|
0
|
|
|
|
|
$indent = shift if $indent eq __PACKAGE__; |
554
|
0
|
0
|
0
|
|
|
|
$indent = __PACKAGE__->parse($indent) if ref($indent) or length($indent) > 5; |
555
|
|
|
|
|
|
|
|
556
|
0
|
|
|
|
|
|
my @cmd; |
557
|
0
|
0
|
|
|
|
|
if ( $indent =~ /^t(\d+)/ ) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
558
|
0
|
|
|
|
|
|
my $chars = $1; |
559
|
0
|
|
|
|
|
|
push @cmd, ":set shiftwidth=$chars"; |
560
|
0
|
|
|
|
|
|
push @cmd, ":set tabstop=$chars"; |
561
|
0
|
|
|
|
|
|
push @cmd, ":set softtabstop=0"; |
562
|
0
|
|
|
|
|
|
push @cmd, ":set noexpandtab"; |
563
|
|
|
|
|
|
|
} elsif ( $indent =~ /^s(\d+)/ ) { |
564
|
0
|
|
|
|
|
|
my $spaces = $1; |
565
|
0
|
|
|
|
|
|
push @cmd, ":set shiftwidth=$spaces"; |
566
|
0
|
|
|
|
|
|
push @cmd, ":set tabstop=8"; |
567
|
0
|
|
|
|
|
|
push @cmd, ":set softtabstop=$spaces"; |
568
|
0
|
|
|
|
|
|
push @cmd, ':set expandtab'; |
569
|
|
|
|
|
|
|
} elsif ( $indent =~ /^m(\d+)/ ) { |
570
|
0
|
|
|
|
|
|
my $spaces = $1; |
571
|
0
|
|
|
|
|
|
push @cmd, ":set shiftwidth=$spaces"; |
572
|
0
|
|
|
|
|
|
push @cmd, ":set tabstop=8"; |
573
|
0
|
|
|
|
|
|
push @cmd, ":set softtabstop=$spaces"; |
574
|
0
|
|
|
|
|
|
push @cmd, ':set noexpandtab'; |
575
|
|
|
|
|
|
|
} |
576
|
0
|
|
|
|
|
|
return @cmd; |
577
|
|
|
|
|
|
|
} |
578
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
1; |
580
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
__END__ |