aboutsummaryrefslogtreecommitdiff
path: root/vim/vimrc
blob: 1ba3805e6ddf45d73c9c1659a364250d5f1102e4 (plain) (blame)
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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
"
" Tom Ryder (tejr)'s Literate Vimrc
" =================================
"
" <https://sanctum.geek.nz/cgit/dotfiles.git>
"
" This is an attempt at something like a 'literate vimrc', in the tradition of
" Donald Knuth's "literate programming".
"
" <http://www.literateprogramming.com/>
"
" It's a long file, and comments abound.  If this bothers you, you can do
" something like this to strip out all the blank lines and lines with only
" comments:
"
"   :v/^\s*[^"]/d
"
" This file should be saved as "vimrc" in the user runtime directory. On
" Unix-like operating systems, this is ~/.vim; on Windows, it's ~/vimfiles.
" It requires Vim 7.0 or newer with +eval, not running in &compatible mode.
" The vimrc stub at ~/.vimrc on Unix or ~/_vimrc on Windows checks that these
" conditions are met before loading this file.
"
" > And I was lifted up in heart, and thought
" > Of all my late-shown prowess in the lists,
" > How my strong lance had beaten down the knights,
" > So many and famous names; and never yet
" > Had heaven appeared so blue, nor earth so green,
" > For all my blood danced in me, and I knew
" > That I should light upon the Holy Grail.
" > --Tennyson
"

" This file contains a few Unicode characters, and the Vint Vim script linter
" wants me to declare that, so I'll do so.  The :help for :scriptencoding says
" that I should do that after 'encoding' is set, so we'll do that now.
"
" On Unix, I keep LANG defined in my environment, and it's almost always set
" to a multibyte (UTF-8) locale.  This informs Vim's choice of internal
" character encoding, but the default for the 'encoding' option is latin1,
" which is seldom what I want, and if I do want it, I'll specify it with LANG
" or possibly a manual :set command.  UTF-8 makes much more sense as a default
" encoding if Vim can't glean what I want from LANG.
"
if !exists('$LANG')
  set encoding=utf-8
endif
scriptencoding utf-8

" With encoding handled, the next thing we'll do is set an environment
" variable MYVIM for the user runtime directory, if such a variable does not
" already exist in the environment, and there's a value in 'runtimepath' from
" which to glean a useable path.  We'll use the path nominated in the MYVIM
" variable as the root of our 'backupdir', 'directory', 'undodir', and
" 'viminfofile' caches, and anywhere else we need a sensible writeable
" location for Vim-related files.  Having it available as an environment
" variable makes assignments with :set more convenient, without requiring
" :execute wrappers.
"
" I think the absence of a variable like this is a glaring omission from Vim.
" We have VIM, VIMRUNTIME, and MYVIMRC, so why is there not an environment
" variable for the user's Vim runtime directory?  It is a mystery.
"
" We'll use the first path specified in 'runtimepath' as a default value.
" This is similar to what Vim does internally for the location of the spelling
" database files in the absence of a setting for 'spellfile'.
"
" Splitting the values of a comma-separated option like 'runtimepath'
" correctly, is a bit more complicated than it seems.  Its list separator is
" more accurately defined as a comma that is not preceded by a backslash, and
" which is followed by any number of spaces and/or further commas.
"
" The pattern required for the split breaks down like this:
"
"   \\     Literal backslash
"   \@<!   Negative lookbehind assertion; means that whatever occurred before
"          this pattern, i.e. a backslash, cannot precede what follows, but is
"          not included as part of the split delimiter itself
"   ,      Literal comma
"   [, ]*  Any number of commas and spaces
"
" We don't have to deal with escaped backslashes; read the source of
" copy_option_part() in vim/src/misc2.c to see why.  As an edge case, if
" &runtimepath is blank, MYVIM will be set to the empty string, which will
" throw an error in the next block, due to the way that split() works by
" default.
"
" Vim, I love you, but you are so weird.
"
if !exists('$MYVIM')
  let $MYVIM = split(&runtimepath, '\\\@<!,[, ]*')[0]
endif

" We need to check the MYVIM environment variable's value to ensure it's not
" going to cause problems for the rest of this file.
"
" Firstly, it can't be empty.
"
" Secondly, if it contains a comma, its use in comma-separated option values
" will confuse Vim into thinking more than one directory is being specified,
" per normal :set semantics.  It's possible to work around this with some
" careful escaping, either at :set time with an :execute abstraction or with
" a separate environment variable for that particular context, but it's not
" really worth the extra complexity for such a niche situation.
"
" Thirdly, some versions of Vim prior to v7.2.0 exhibit bizarre behaviour with
" escaping with the backslash character on the command line, so on these older
" versions of Vim, forbid that character.  I haven't found the exact patch
" level that this was fixed yet, nor the true reason for the bug.
"
" If any of those conditions are meant, throw an explanatory error and stop
" reading this file.  Most of the file doesn't depend on MYVIM, but there's
" no point catering to these edge cases.
"
if $MYVIM ==# ''
  echoerr 'Blank user runtime path'
  finish
elseif $MYVIM =~# ','
  echoerr 'Illegal comma in user runtime path'
  finish
elseif $MYVIM =~# '\\' && v:version < 702
  echoerr 'Illegal backslash in user runtime path on Vim < v7.2'
  finish
endif

" Use all of the filetype detection, plugin, and indent support available.
" I define my own filetype.vim and scripts.vim files for filetype detection,
" in a similar but not identical form to the stock runtime files.  I also
" define my own ftplugin and indent files for some types, sometimes replacing
" and sometimes supplementing the runtime files.
"
filetype plugin indent on

" There are a couple of contexts in which it's useful to reload filetypes for
" the current buffer, quietly doing nothing if filetypes aren't enabled.
" We'll set up a script-local function to do this, just to be tidy, which is
" abstracted behind a simple user command of the same name.
"
function! s:ReloadFileType() abort
  if exists('g:did_load_filetypes')
    doautocmd filetypedetect BufRead
  endif
endfunction
command! -bar ReloadFileType
      \ call s:ReloadFileType()

" We'll also define a :ReloadVimrc command.  This may seem like overkill at
" first; surely just :source $MYVIMRC is good enough?
"
" We're defining the command because of an edge case: if the vimrc stub or
" main file is re-sourced, the global settings for options like 'expandtab'
" and 'shiftwidth' may trample over different buffer-local settings that were
" specified by filetype and indent plugins.  To handle this, we'll define the
" command to run :ReloadFileType after the vimrc file is sourced.
"
" We can't put these two commands in a script-local function in the vimrc, in
" order to be tidy like we did for :ReloadFileType above, because Vim would
" get upset that we're trying to redefine a function as it executes!
"
" Just to be on the safe side, we also suppress any further ##SourceCmd hooks
" from running the :source command with a :noautocmd wrapper.  This is
" a defensive measure to avoid infinite recursion.
"
command! -bar ReloadVimrc
      \ noautocmd source $MYVIMRC | ReloadFileType

" Reset and define a group of automatic command hooks specific to matters
" related to reloading the vimrc itself.
"
augroup vimrc
  autocmd!

  " Reload the vimrc each time the stub vimrc or this vimrc are saved.  This
  " often makes errors in the file immediately apparent, and saves restarting
  " Vim or running the :source command manually, which I almost always want to
  " do, anyway.
  "
  autocmd BufWritePost $MYVIMRC,$MYVIM/vimrc
        \ ReloadVimrc

  " If Vim is new enough (v7.0.187) to support the ##SourceCmd event for
  " automatic command hooks, we'll also apply that to catch invocations of
  " :source of either the stub or main vimrc, and translate that into sourcing
  " the stub vimrc and reloading the filetype using our new command.
  "
  if exists('##SourceCmd')
    autocmd SourceCmd $MYVIMRC,$MYVIM/vimrc
          \ ReloadVimrc
  endif

augroup END

" We're going to be creating a few directories now, and the code to do so in
" a compatible way is surprisingly verbose, because as well as expanding what
" we were provided as an argument, we need to check the mkdir() function is
" actually available.
"
" We also need to check whether the directory concerned already exists, even
" if we specify the special 'p' value for its optional {path} argument.  This
" is because the meaning of mkdir(..., 'p') is not the same as `mkdir -p` in
" shell script, or at least, it isn't in versions of Vim  before v8.0.1708.
" Even with the magic 'p' sauce, these versions throw errors if the directory
" already exists, despite what someone familiar with `mkdir -p`'s behaviour in
" shell script might expect.
"
" So, let's wrap all that nonsense in a script-local function, and then
" abstract that away too with a user command, to keep the semantics of the
" :set operations nice and clean.  We'll make all the directories we create
" have restrictive permissions, too, with a {prot} argument of 0700 for the
" final one, since every directory we want to create in this file should be
" locked down in this way.
"
function! s:Establish(path) abort
  let path = expand(a:path)
  return isdirectory(path)
        \ || exists('*mkdir') && mkdir(path, 'p', 0700)
endfunction

" Now we define the :Establish command for user-level access to the
" s:Establish() function.  We set the tab completion to provide directory
" names as candidates, and specify that there must be only one argument, which
" we'll provide as a quoted parameter to the function.
"
command! -bar -complete=dir -nargs=1 Establish
      \ call s:Establish(<q-args>)

" Now that we have a clean means to create directories if they don't already
" exist, let's apply it for the first time to the user runtime directory.
"
Establish $MYVIM

" Keep the viminfo file in a cache subdirectory of the user runtime directory,
" creating that subdirectory first if necessary.
"
" Using this non-default location for viminfo has the nice benefit of
" preventing command and search history from getting clobbered when something
" runs Vim without using this vimrc, because it writes its history to the
" default viminfo path instead.  It also means that everything Vim-related in
" the user's home directory should be encapsulated in the one ~/.vim or
" ~/vimfiles directory.
"
" The normal method of specifying the path to the viminfo file used here is an
" addendum to the 'viminfo' option, which works OK.  Vim v8.1.716 introduced
" a nicer way to set it with a 'viminfofile' option, but there's no particular
" reason to use it until it's in a few more stable versions.
"
Establish $MYVIM/cache
set viminfo+=n$MYVIM/cache/viminfo

" Speaking of recorded data in viminfo files, the command and search history
" count default limit of 50 is pretty restrictive.  Because I don't think I'm
" ever likely to be in a situation where remembering several thousand Vim
" commands and search patterns is going to severely tax memory, let alone hard
" disk space, I'd rather it were much higher, as it's sometimes really handy
" to dig up commands from some time ago.  The maximum value for this option is
" documented as 10000, so let's just use that and see if anything breaks.
"
set history=10000

" Enable automatic backups of most file buffers.  In practice, I don't need
" these backups very much if I'm using version control sensibly, but they have
" still saved my bacon a few times.  We're not done here yet, though; it
" requires some fine-tuning.
"
set backup

" Try to keep the aforementioned backup files in a dedicated cache directory,
" to stop them proliferating next to their prime locations and getting
" committed to version control repositories.  Create said directory if needed,
" too, with restrictive permissions.
"
" If Vim is new enough (v8.1.251), add two trailing slashes to the path we're
" inserting, which prompts Vim to incorporate the full escaped path in the
" backup filename, avoiding collisions.
"
" As a historical note, other similar directory path list options supported
" this trailing slashes hint for a long time before 'backupdir' caught up to
" them.  The 'directory' option for swap files has supported it at least as
" far back as v5.8.0 (2001), and 'undodir' appears to have supported it since
" its creation in v7.2.438.  Even though the :help for 'backupdir' didn't say
" so, people assumed it would work the same way, when in fact Vim simply
" ignored it until v8.1.251.
"
" I don't want to add the slashes to the option value in older versions of Vim
" where they don't do anything, so I check the version to see if there's any
" point adding them.
"
" It's all so awkward.  Surely options named something like 'backupfullpath',
" 'swapfilefullpath', and 'undofullpath' would have been clearer.
"
Establish $MYVIM/cache/backup
if has('patch-8.1.251')
  set backupdir^=$MYVIM/cache/backup//
else
  set backupdir^=$MYVIM/cache/backup
endif

" Vim doesn't seem to check patterns added to 'backupskip' for uniqueness,
" so adding them repeatedly if this file is reloaded results in duplicates,
" due to the absence of the P_NODUP flag for its definition in src/option.c.
" This is likely a bug in Vim.  For the moment, to work around the problem,
" we reset the path back to its default first.
"
set backupskip&

" Files in certain directories on Unix-compatible filesystems should not be
" backed up for reasons of privacy, or an intentional ephemerality, or both.
" This is particularly important if editing temporary files created by
" sudoedit(8).  On Unix-like systems, we here add a few paths to the default
" value of 'backupskip' in order to prevent the creation of such undesired
" backup files.
"
" * /dev/shm: RAM disk, default path for password-store's temporary files
" * /usr/tmp: Hard-coded path for sudoedit(8) [1/2]
" * /var/tmp: Hard-coded path for sudoedit(8) [2/2]
"
if has('unix')
  set backupskip^=/dev/shm/*,/usr/tmp/*,/var/tmp/*
endif

" Keep swap files for file buffers in a dedicated directory, rather than the
" default of writing them to the same directory as the buffer file.  Add two
" trailing slashes to the path to prompt Vim to use the full escaped path in
" its name, in order to avoid filename collisions.  Create that path if
" needed, too.
"
Establish $MYVIM/cache/swap
set directory^=$MYVIM/cache/swap//

" Keep tracked undo history for files permanently, in a dedicated cache
" directory, so that the u/:undo and CTRL-R/:redo commands will work between
" Vim invocations.
"
" Support for persistent undo file caches was not added until v7.2.438, so we
" need to check for the feature's presence before we enable it.
"
if has('persistent_undo')

  " This has the same structure as 'backupdir' and 'directory'; if we have
  " a user runtime directory, create a sub-subdirectory within it dedicated to
  " the undo files cache.  Note also the trailing double-slash as a signal to
  " Vim to use the full path of the original file in its undo file cache's
  " name.
  "
  Establish $MYVIM/cache/undo
  set undodir^=$MYVIM/cache/undo//

  " Turn the persistent undo features on, regardless of whether we have
  " a cache directory for them as a result of the logic above.  The files
  " might sprinkle around the filesystem annoyingly, but that's still better
  " than losing the history completely.
  "
  set undofile

endif

" For word completion in insert mode with CTRL-X CTRL-K, or if 'complete'
" includes the 'k' flag, the 'dictionary' option specifies the path to the
" system word list.  This makes the dictionary completion work consistently,
" even if 'spell' isn't set at the time to coax it into using 'spellfile'.
"
" It's not an error if the system directory file added first doesn't exist;
" it's just a common location that often yields a workable word list, and does
" so on all of my main machines.
"
" At some point, I may end up having to set this option along with 'spellfile'
" a bit more intelligently to ensure that spell checking and dictionary
" function consistently, and with reference to the same resources.  For the
" moment, I've just added another entry referring to a directory in the user
" runtime directory, but I don't have anything distinct to put there yet.
"
" In much the same way, we add an expected path to a thesaurus, for completion
" with CTRL-X CTRL-T in insert mode, or with 't' added to 'completeopt'.  The
" thesaurus data isn't installed as part of the default `install-vim` target
" in tejr's dotfiles, but it can be retrieved and installed with
" `install-vim-thesaurus`.
"
" I got the thesaurus itself from the link in the :help for 'thesaurus' in
" v8.1.1487.  It's from WordNet and MyThes-1.  I maintain a mirror on my own
" website that the Makefile recipe attempts to retrieve.  I had to remove the
" first two metadata lines from thesaurus.txt, as Vim appeared to interpret
" them as part of the body data.
"
" Extra checks for appending the 'dictionary' and 'thesaurus' paths in MYVIM
" need to be made, because the P_NDNAME property is assigned to them, which
" enforces a character blacklist in the option value.  We check for the same
" set of blacklist characters here, and if the MYVIM path offends, we just
" skip the setting entirely, rather than throwing cryptic errors at the user.
" None of them are particularly wise characters to have in paths, anyway,
" legal though they may be on Unix filesystems.
"
set dictionary^=/usr/share/dict/words
if $MYVIM !~# '[*?[|;&<>\r\n]'
  set dictionary^=$MYVIM/ref/dictionary.txt
  set thesaurus^=$MYVIM/ref/thesaurus.txt
endif

" Next, we'll modernise a little in adjusting some options with old
" language-specific defaults.
"
" Traditional vi was often used for development in the C programming language.
" The default values for a lot of Vim's options still reflect this common use
" pattern.  In this case, the 'comments' and 'commentstring' options reflect
" the C syntax for comments:
"
"     /*
"      * This is an ANSI C comment.
"      */
"
" Similarly, the 'define' and 'include' options default to C preprocessor
" directives:
"
"     #define FOO "bar"
"
"     #include "baz.h"
"
" Times change, however, and I don't get to work with C nearly as much as I'd
" like.  The defaults for these options no longer make sense, and so we blank
" them, compelling filetype plugins to set them as they need instead.
"
set comments= commentstring= define= include=

" The default value for the 'path' option is similar, in that it has an aged
" default; this option specifies directories in which project files and
" includes can be unearthed by navigation commands like 'gf'.  Specifically,
" its default value comprises /usr/include, which is another C default.  Let's
" get rid of that, too.
"
set path-=/usr/include

" Next, we'll adjust the global indentation settings.  In general and as
" a default, I prefer spaces to tabs, and I like to use four of them, for
" a more distinct visual structure.  Should you happen to disagree with this,
" I cordially invite you to fite me irl.
"
" <https://sanctum.geek.nz/blinkenlights/spaces.webm>
"
" Filetype indent plugins will often refine these settings for individual
" buffers.  For example, 'expandtab' is not appropriate for Makefiles, nor for
" the Go programming language.  For another, two-space indents are more
" traditional for Vim script.
"
set autoindent    " Use indent of previous line on new lines
set expandtab     " Insert spaces when tab key is pressed in insert mode
set shiftwidth=4  " Indent command like < and > use four-space indents

" Apply 'softtabstop' option to make a tab key press in insert mode insert the
" same number of spaces as defined by the indent depth in 'shiftwidth'.  If
" Vim is new enough to support it (v7.3.693), apply a negative value to do
" this dynamically if 'shiftwidth' changes.
"
if v:version > 730 || v:version == 730 && has('patch693')
  set softtabstop=-1
else
  let &softtabstop = &shiftwidth
endif

" Relax traditional vi's harsh standards over what regions of the buffer can
" be removed with backspace in insert mode.  While this admittedly allows bad
" habits to continue, since insert mode by definition is not really intended
" for deleting text, I feel the convenience outweighs that in this case.
"
set backspace+=eol     " Line breaks
set backspace+=indent  " Leading whitespace characters created by 'autoindent'
set backspace+=start   " Text before the start of the current insertion

" When soft-wrapping text with the 'wrap' option on, which is off by default,
" break the lines between words, rather than within them; it's much easier to
" read.
"
set linebreak

" Similarly, show that the screen line is a trailing part of a wrapped line by
" prefixing it with an ellipsis.  If we have a multi-byte encoding, use U+2026
" HORIZONTAL ELLIPSIS to save a couple of columns, but otherwise three periods
" will do just fine.
"
if has('multi_byte_encoding')
  set showbreak=else
  set showbreak=...
endif

" The visual structure of code provided by indents breaks down if a lot of the
" lines wrap.  Ideally, most if not all lines would be kept below 80
" characters, but in cases where this isn't possible, soft-wrapping longer
" lines when 'wrap' is on so that the indent is preserved in the following
" line mitigates this breakdown somewhat.
"
" With this set, it's particularly important to have 'showbreak' set to
" something, above, otherwise without line numbers it's hard to tell what's
" a logical line and what's not.
"
" This option wasn't added until v7.4.338, so we need to check it exists
" before we set it.
"
if exists('+breakindent')
  set breakindent
endif

" Rather than rejecting operations like :write or :saveas when 'readonly' is
" set, and other situations in which data might be lost or I'm acting against
" an option, Vim should give me a prompt to allow me to confirm that I know
" what I'm doing.
"
set confirm

" If Vim receives an Escape key code in insert mode, it shouldn't wait to see
" if it's going to be followed by another key code, despite this being how the
" function keys and Meta/Alt modifier are implemented for many terminal types.
" Otherwise, if I press Escape, there's an annoying delay before 'showmode'
" stops showing "--INSERT--".
"
" This breaks the function keys and the Meta/Alt modifier in insert mode in
" most or maybe all of the terminals I use, but I don't want those keys in
" insert mode anyway.  It all works fine in the GUI, of course.
"
" There's no such option as 'esckeys' in Neovim, which I gather has completely
" overhauled its method of keyboard event handling, so we need to check
" whether the option exists before we try to set it.
"
if exists('+esckeys')
  set noesckeys
endif

" By default, I prefer that figuring out where a region of text to fold away
" should be done by the indent level of its lines, since I tend to be careful
" about my indentation even in languages where it has no structural
" significance.
"
set foldmethod=indent

" That said, I don't want any folding to actually take place unless
" I specifically ask for it.
"
" I think of a Vim window with a file buffer loaded as a two-dimensional
" planar view of the file, so that moving down one screen line means moving
" down one buffer line, at least when 'wrap' is unset.  Folds break that
" mental model, and so I usually enable them explicitly only when I'm
" struggling to grasp some in-depth code with very long functions or loops.
"
" Therefore, we set the depth level at which folds should automatically start
" as closed to a rather high number, per the documentation's recommendations.
"
set foldlevelstart=99

" Automatic text wrapping options using flags in the 'formatoptions' option
" begin here.  I allow filetypes to set 't' and 'c' to configure whether text
" or comments should be wrapped, and so I don't mess with either of those
" flags here.

" If a line is already longer than 'textwidth' would otherwise limit when
" editing of that line begins in insert mode, don't suddenly automatically
" wrap it; I'll break it apart myself with a command like 'gq'.
"
set formatoptions+=l

" Don't wrap a line in such a way that a single-letter word like "I" or "a" is
" at the end of it.  Typographically, as far as I can tell, this seems to be
" a stylistic preference rather than a rule like avoiding "widow" and "orphan"
" lines in typesetting.  I think it generally looks better to have the short
" word start the line.
"
set formatoptions+=1

" If the filetype plugins have correctly described what the comment syntax for
" the buffer's language looks like, it makes sense to use that to figure out
" how to join lines within comments without redundant comment leaders cropping
" up.  For example, with this set, in Vim, joining lines in this very comment
" with 'J' would remove the leading '"' characters that denote a comment.
"
" This option flag wasn't added until v7.3.541.  Because we can't test for the
" availability of option flags directly, we resort to a version number check
" before attempting to add the flag.  I don't like using :silent! to suppress
" errors for this sort of thing when I can reasonably avoid it, even if it's
" somewhat more verbose.
"
if v:version > 730 || v:version == 730 && has('patch541')
  set formatoptions+=j
endif

" A momentary digression here into the doldrums of 'cpoptions'--after
" staunchly opposing it for years, I have converted to two-spacing.  You can
" blame Steve Losh:
"
" <http://stevelosh.com/blog/2012/10/why-i-two-space/>
"
" Consequently, we specify that sentence objects for the purposes of the 's'
" text object, the '(' and ')' sentence motions, and formatting with the 'gq'
" command must be separated by *two* spaces.  One space does not suffice.
"
" My defection to the two-spacers is also the reason I now leave 'joinspaces'
" set, per its default, so that two spaces are inserted when consecutive
" sentences separated by a line break are joined onto one line by the 'J'
" command.
"
set cpoptions+=J

" Separating sentences with two spaces has an advantage in making a clear
" distinction between two different types of periods: periods that abbreviate
" longer words, as in "Mr. Moolenaar", and periods that terminate sentences,
" like this one.
"
" If we're using two-period spacing for sentences, Vim can interpret the
" different spacing to distinguish between the two types, and can thereby
" avoid breaking a line just after an abbreviating period.  For example, the
" two words in "Mr. Moolenaar" should never be split apart, preventing
" confusion on the reader's part lest the word "Mr." look too much like the
" end of a sentence, and also preserving the semantics of that same period for
" subsequent reformats; its single-space won't get lost.
"
" So, getting back to our 'formatoptions' settings, that is what the 'p' flag
" does.  I wrote the patch that added it, after becoming envious of an
" analogous feature during an ill-fated foray into GNU Emacs usage.
"
" <https://github.com/vim/vim/commit/c3c3158>
"
if has('patch-8.1.728')
  set formatoptions+=p
endif

" In an effort to avoid loading unnecessary files, we add a flag to the
" 'guioptions' option to prevent the menu.vim runtime file from being loaded.
" It doesn't do any harm, but I never use it, and it's easy to turn it off.
"
" The documentation for this flag in `:help 'go-M'` includes a note saying the
" flag should be set here, rather that in the GUI-specific gvimrc file, as one
" might otherwise think.
"
if has('gui_running')
  set guioptions+=M
endif

" By default, Vim doesn't allow a file buffer to have unsaved changes if it's
" not displayed in a window.  Setting this option removes that restriction so
" that buffers can remain in a modified state while not actually displayed
" anywhere.
"
" This option is set in almost every vimrc I read; it's so pervasive that
" I sometimes see comments expressing astonishment or annoyance that it isn't
" set by default.  However, I didn't actually need this option for several
" years of Vim usage, because I instinctively close windows onto buffers only
" after the buffers within them were saved anyway.
"
" However, the option really is required for batch operations performed with
" commands like :argdo or :bufdo, because Vim won't otherwise tolerate unsaved
" changes to a litany of buffers that are not displayed in any window.  After
" I started using such command maps a bit more often, I realised I finally had
" a reason to turn this on permanently.
"
set hidden

" Do highlight matches for completed searches in the text, but clear that
" highlighting away when this vimrc is reloaded.  Later on in this file,
" CTRL-L in normal mode is remapped to tack on a :nohlsearch as well.
"
set hlsearch
nohlsearch

" Highlight search matches in my text while I'm still typing my pattern,
" including scrolling the screen to show the first such match if necessary.
" This can be somewhat jarring, particularly when the cursor ends up scrolling
" a long way from home in a large file, but I think the benefits of being able
" to see instances of what I'm trying to match as I try to match it do
" outweigh that discomfort.
"
set incsearch

" If there's only one window, I don't need a status line to appear beneath it.
" I very often edit only a few files in one window in a Vim session.  I like
" the initial screen just being empty save for the trademark tildes.  It gives
" me an extra screen line, too.  It's a reflex for me to press CTRL-G in
" normal mode if I need to see the buffer name.
"
" This value reflects the Vim default, but Neovim changed its default to '2'
" for an 'always-on' status line, so we'll explicitly set it to the default
" here in case we're using Neovim.
"
set laststatus=1

" Don't waste cycles and bandwidth redrawing the screen during execution of
" aggregate commands in e.g. macros.  I think this does amount to the
" occasional :redraw needing to be in a script, but it's not too bad, and last
" I checked it really does speed things up, especially for operations on
" really big data sets.
"
set lazyredraw

" Define meta-characters to show in place of characters that are otherwise
" invisible, or line wrapping attributes when the 'list' option is enabled.
"
" We need to reset the option to its default value first, because at the time
" of writing at least, Neovim v0.3.5 doesn't check these for uniqueness,
" resulting in duplicates if this file is reloaded.  'backupskip' has similar
" problems in the original Vim v8.1.1487 and earlier.
"
set listchars&vi

" These 'list' characters all correspond to invisible or indistinguishable
" characters.  We leave the default eol:$ in place to show newlines, and add
" a few more.
"
set listchars+=tab:>-   " Tab characters, preserve width with hyphens
set listchars+=trail:-  " Trailing spaces
set listchars+=nbsp:+   " Non-breaking spaces

" The next pair of 'list' characters are arguably somewhat misplaced, in that
" they don't really represent invisible characters in the same way as the
" others, but are hints for the presence of other characters on unwrapped
" lines that are wider than the screen.  They're very useful, though.
"
" If the current encoding supports it, use these non-ASCII characters for the
" markers, as they're visually distinctive:
"
" extends: Signals presence of unwrapped text to screen right
"     »  U+00BB  RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
" precedes: Signals presence of unwrapped text to screen left
"     «  U+00BB  LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
"
" Failing that, '<' and '>' will still do the trick.
"
if has('multi_byte_encoding')
  set listchars+=extends:»,precedes:«
else
  set listchars+=extends:>,precedes:<
endif

" Don't let your editor's options be configured by content in arbitrary files!
" Down with modelines!  Purge them from your files!  Écrasez l'infâme!
"
" I think that modelines are Vim's worst misfeature, and that 'nomodeline'
" should be the default.  It's enabled pretty bad security vulnerabilities
" over the years, and it's a lot more effective to use filetype detection,
" other automatic command hooks, or systems like .editorconfig to set
" variables specifically for a buffer or project.
"
set nomodeline

" The only octal numbers I can think of that I ever even encounter are Unix
" permissions masks, and I'd never use CTRL-A or CTRL-X to increment them.
" Numbers with leading zeroes are far more likely to be decimals.
"
set nrformats-=octal

" I like to leave the last line of the screen blank unless something is
" actually happening in it, so I have grown to like the Vim default of
" 'noruler'.  CTRL-G shows me everything I need to know, and is
" near-instinctive now.
"
" Rude system vimrc files tend to switch this back on, though, and Neovim has
" it on by default, so we will often need to put it back to normal, as we do
" here.
"
set noruler

" Sessions preserving buffer and window layout are great for more complex and
" longer-term projects like books, but they don't play together well with
" plugins and filetype plugins.  Restoring the same settings from both
" reloaded plugins and from the session causes screeds of errors.  Adjusting
" session behaviour to stop it trying to restore quite so much makes them
" useable.
"
set sessionoptions-=localoptions  " No buffer options or mappings
set sessionoptions-=options       " No global options or mappings

" The 'I' flag for the 'shortmess' option prevents the display of the Vim
" startup screen with version information, :help hints, and donation
" suggestion.  After I registered Vim and donated to Uganda per the screen's
" plea, I didn't feel bad about turning this off anymore.  Even with this
" setting in place, I wouldn't normally see it too often anyway, as I seldom
" start Vim with no file arguments.
"
" I haven't felt the need to mess with the other flags in this option.
" I don't have any problems with spurious Enter prompts, which seems to be
" the main reason people pile it full of letters.
"
set shortmess+=I

" I find the defaults of new windows opening above or to the left of the
" previous window too jarring, because I'm used to both the i3 window manager
" and the tmux terminal multiplexer doing it the other way around, in reading
" order.  I prefer the visual effect of the previous text staying where it is,
" and the new window occupying previously blank space.
"
set splitbelow splitright

" Limit the number of characters per line that syntax highlighting will
" attempt to match.  This is as much an effort to encourage me to break long
" lines and do hard wrapping correctly as it is for efficiency.
"
set synmaxcol=500

" Vim has an internal list of terminal types that support using smoother
" terminal redrawing, and for which 'ttyfast' is normally set, described in
" `:help 'ttyfast'`.  That list includes most of the terminals I use, but
" there are a couple more for which the 'ttyfast' option should apply: the
" windows terminal emulator PuTTY, and the terminal multiplexer tmux, both of
" which I use heavily.
"
if &term =~# '^putty\|^tmux'
  set ttyfast
endif

" We really don't want a mouse; while I use it a lot for cut and paste in X,
" at the terminal application level, it just gets in the way.  Mouse events
" should be exclusively handled by the terminal emulator application, so Vim
" shouldn't try to give me terminal mouse support, even if it would work.
"
" The manual suggests that disabling this should be done by clearing 't_RV',
" but that didn't actually seem to work when I tried it.
"
" We have to check for the existence of the option first, as it doesn't exist
" in Neovim.
"
if exists('+ttymouse')
  set ttymouse=
endif

" While using virtual block mode, allow me to navigate to any column of the
" buffer window; don't confine the boundaries of the block to the coordinates
" of characters that actually exist in the buffer text.  While working with
" formatted columnar data with this off is generally OK, it's a hassle for
" more subtle applications of visual block mode.
"
set virtualedit+=block

" I can't recall a time that Vim's error beeping or flashing was actually
" useful to me, and so we turn it off in the manner that the manual instructs
" in `:help 'visualbell'`.  This enables visual rather than audio error bells,
" but in the same breath blanks the terminal attribute that would be used to
" trigger such screen blinking, indirectly disabling the bell altogether.
"
" I thought at first that the newer 'belloff' and/or 'errorbells' options
" would be a more intuitive way to keep Vim quiet, but the last time I checked
" that they didn't actually appear to work as comprehensively as this older
" method does.
"
" Interestingly, the :help says that this setting has to be repeated in the
" gvimrc file for GUI Vim, so you'll find this exact same command issued again
" in there.
"
set visualbell t_vb=

" When Ex command line completion is started with Tab, list valid completions
" and complete the command line to the longest common substring, just as Bash
" does, with just the one key press.
"
" The default value of 'full' for the 'wildmode' option puts the full
" completion onto the line immediately, which I tolerate for insert mode
" completion but don't really like on the Ex command line.  Instead, I arrange
" for that with a second key press if I ever want it, which isn't often.  I did
" without using it at all for years.
"
set wildmenu
set wildmode=list:longest,full

" Define a list of patterns for the 'wildignore' option.  Files and
" directories with names matching any of these patterns won't be presented as
" candidates for tab completion on the command line.
"
" To make this list, I went right through my home directory with
" a `find`-toothed comb; counted the occurrences of every extension, forced
" down to lowercase; and then manually selected the ones that I was confident
" would seldom contain plain text.
"
" The following incantation does the trick with POSIX-compatible shell tools,
" giving you patterns for the top 50 extensions:
"
"     $ find ~ -type f -name '*.*' |
"           awk -F. '{exts[tolower($NF)]++}
"           END {for(ext in exts)print exts[ext], "*." ext}' |
"           sort -k1,1nr |
"           sed 50q
"
" I turned out to have rather a lot of .html and .vim files.
"
" It's tempting to put the list of patterns here into a separate file--or at
" least into a more readily editable intermediate list variable--rather than
" the minor maintenance hassle it presently constitutes in this compact form.
" I'm not sure whether I'll do that just yet.
"
set wildignore=*~,#*#,*.7z,.DS_Store,.git,.hg,.svn,*.a,*.adf,*.asc,*.au,*.aup
      \,*.avi,*.bin,*.bmp,*.bz2,*.class,*.db,*.dbm,*.djvu,*.docx,*.exe
      \,*.filepart,*.flac,*.gd2,*.gif,*.gifv,*.gmo,*.gpg,*.gz,*.hdf,*.ico
      \,*.iso,*.jar,*.jpeg,*.jpg,*.m4a,*.mid,*.mp3,*.mp4,*.o,*.odp,*.ods,*.odt
      \,*.ogg,*.ogv,*.opus,*.pbm,*.pdf,*.png,*.ppt,*.psd,*.pyc,*.rar,*.rm
      \,*.s3m,*.sdbm,*.sqlite,*.swf,*.swp,*.tar,*.tga,*.ttf,*.wav,*.webm,*.xbm
      \,*.xcf,*.xls,*.xlsx,*.xpm,*.xz,*.zip

" Allow me to be lazy and type a path to complete on the Ex command line in
" all-lowercase, and transform the consequent completion to match the
" appropriate case, like the Readline setting completion-ignore-case can be
" used for GNU Bash.
"
" As far as I can tell, despite its name, the 'wildignore' case option doesn't
" have anything to do with the 'wildignore' option, and so files that would
" match any of those patterns only with case insensitivity implied will still
" be candidates for completion.
"
" The option wasn't added until v7.3.72, so we need to check it exists before
" we try to set it.
"
if exists('+wildignorecase')
  set wildignorecase
endif

" Enable syntax highlighting, but only if it's not already on, to save
" reloading the syntax files unnecessarily.
"
" <https://sanctum.geek.nz/blinkenlights/syntax-on.jpg>
"
" For several months in 2018, as an experiment, I tried using terminals with
" no colour at all, imitating a phenomenally productive BSD purist co-worker
" who abhorred colour in any form on his terminals.  He only drank black
" coffee, too.  If you're reading this: Hello, bdh!
"
" That experiment was instructive and interesting, and I found I had been
" leaning on colour information in some surprising ways.  However, some months
" later, I found I still missed my colours, and so I went back to my
" Kodachrome roots, and didn't pine at all for that monochrome world.
"
" The thing I most like about syntax highlighting is detecting runaway
" strings, which generally works in even the most threadbare language syntax
" highlighting definitions.  I kept missing such errors when I didn't have the
" colours.  I don't have high standards for it otherwise, except maybe for
" shell script.
"
if !exists('syntax_on')
  syntax enable
endif

" We'll have Vim try to use my 'sahara' fork of the 'desert256' colour scheme,
" and if it manages to do so without errors, turn on the 'cursorline' feature,
" since the scheme configures it and 'cursorcolumn' to be a very dark grey
" that doesn't stand out too much against a black background.  Aside from the
" aforementioned experiment with monochrome terminals, I exclusively use dark
" backgrounds.
"
" If we fail to load the colour scheme, for whatever reason, suppress the
" error, and reset the syntax highlighting, 'background', and 'cursorline' for
" dark-background default colours.  I used it for years; it looks and works
" just fine.
"
" There's also a very simple grayscale colour scheme I occasionally use
" instead called 'juvenile', which is included as a Git submodule with this
" dotfiles distribution.
"
try
  colorscheme sahara
  set cursorline
catch
  colorscheme default
  set background=dark
  set nocursorline
endtry

" My mapping definitions begin here.  I have some general personal rules for
" approaches to mappings:
"
" * Use the configured Leader key as a prefix for mappings as much as
"   possible.
"
" * Use only the configured LocalLeader key as a prefix for mappings that are
"   defined as local to a buffer, which for me are almost always based on
"   &filetype and set up by ftplugin files.
"
" * If a normal mode map would make sense in visual mode, take the time to
"   configure that too.  Use :xmap and its analogues rather than :vmap to
"   avoid defining unusable select-mode mappings, even though I never actually
"   use selection mode directly.
"
" * Avoid mapping in insert mode; let characters be literal to the greatest
"   extent possible, and avoid "doing more" in insert mode besides merely
"   inserting text as it's typed.
"
" * Avoid chording with Ctrl in favour of leader keys.
"
" * Never use Alt/Meta chording; the terminal support for them is just too
"   confusing and flaky.
"
" * Don't suppress display of mapped commands for no reason; it's OK to show
"   the user the command that's being run under the hood.  Do avoid HIT-ENTER
"   prompts, though.
"
" * Avoid shadowing any of Vim's existing functionality.  If possible, extend
"   or supplement what Vim does, rather than replacing it.
"
" We'll start with the non-leader mappings.  Ideally, there shouldn't be too
" many of these.
"
" I like using the space bar to scroll down a page, so I can lazily tap it to
" read documents, and I find its default behaviour of moving right one
" character to be useless.
"
" I also have a custom plugin named scroll_next.vim that issues :next to have
" it move to the next file in the arglist if the bottom line of the buffer is
" visible, for reading multiple buffers.
"
" <https://sanctum.geek.nz/cgit/vim-scroll-next.git/about/>
"
" However, I only want that functionality mapped if the required plugin is
" actually going to load, so I check that it's available and that the
" 'loadplugin' option is set before using its provided map target, because if
" it doesn't it will kill the space key.  If the plugin doesn't look like it's
" going to load, I just bind Space to do the same thing as PageDown.
"
" Either way, the downside of this arrangement is it's an easy key to hit
" accidentally.  I'm keeping it for the moment, though.
"
" I always wanted you to go into space, man.
"
if &loadplugins && globpath(&runtimepath, 'plugin/scroll_next.vim') !=# ''
  nmap <Space> <Plug>(ScrollNext)
else
  nnoremap <Space> <PageDown>
endif

" I hate CTRL-C in insert mode, which ends the insert session without firing
" the InsertLeave event for automatic command hooks.  It seems worse than
" useless; why would you want that?  It breaks plugins that hinge on mirrored
" functionality between the InsertEnter and InsertLeave automatic command
" events, and doesn't otherwise do anything different from Escape.  Even
" worse, people think it's a *synonym* for Escape, and use it because it's
" easier to reach than the Escape key or CTRL-[.  It's terrible!
"
" Instead, I apply a custom plugin named insert_cancel.vim to make it cancel
" the current insert operation; that is, if the buffer has changed at all
" since the start of the insert operation, pressing CTRL-C will reverse it,
" while ending insert mode and firing InsertLeave as normal.  This makes way
" more sense to me, and I use it all the time now.
"
" <https://sanctum.geek.nz/cgit/vim-insert-cancel.git/about/>
"
" You might think on a first look, as I did, that a plugin is overkill, and
" that a mapping like this would be all that's required:
"
"   :inoremap <C-C> <Esc>u
"
" Indeed, it *mostly* works, but there are some subtle problems with it.  The
" primary issue is that if you didn't make any changes during the insert mode
" session that you're terminating, it *still* reverses the previous change,
" which will be something else entirely that you probably *didn't* mean to be
" undone.  The plugin's way of working around this and the other shortcomings
" of the simple mapping above is not too much more complicated, but it was not
" easy to figure out.
"
" At any rate, as with the space bar's leverage of the scroll_next.vim plugin
" above, we only want to establish the mapping if we can expect the plugin to
" load, so test that it exists with the expected name and that 'loadplugins'
" is set.
"
" If the plugin isn't available, I just abandon CTRL-C to continue its
" uselessness.
"
if &loadplugins && globpath(&runtimepath, 'plugin/insert_cancel.vim') !=# ''
  imap <C-C> <Plug>(InsertCancel)
endif

" I often don't remember or can't guess digraph codes very well, and want to
" look up how to compose a specific character that I can name, at least in
" part.  The table in `:help digraph-table` is what to use for that situation,
" and it solves the problem, but the overhead of repeated lookups therein was
" just a little bit high.
"
" Steve Losh has a solution I liked where a double-tap of CTRL-K in insert
" mode brought up a help window with the table, which could then be searched
" as normal:
"
" <https://bitbucket.org/sjl/dotfiles/src/2559256/vim/vimrc#lines-309:310>
"
" I took it one step further with a custom plugin digraph_search.vim that
" parses the digraph table and runs a simple text search of its names using
" a string provided by the user.  For example, searching for ACUTE yields:
"
" > Digraphs matching ACUTE:
" > ´  ''  ACUTE ACCENT
" > Á  A'  LATIN CAPITAL LETTER A WITH ACUTE
" > É  E'  LATIN CAPITAL LETTER E WITH ACUTE
" > Í  I'  LATIN CAPITAL LETTER I WITH ACUTE
" > ...etc...
"
" <https://sanctum.geek.nz/cgit/vim-digraph-search.git/about/>
"
" This leaves you in insert mode, ready to hit CTRL-K one more time and then
" type the digraph that you've hopefully found.
"
" Since a double-tap of CTRL-K does nothing in default Vim, we don't bother
" checking that the plugin's available before we map to it; it'll just quietly
" do nothing.
"
imap <C-K><C-K> <Plug>(DigraphSearch)

" I end up hitting CTRL-L to clear or redraw the screen in interactive shells
" and tools like Mutt and Vim pretty often.  It feels natural to me to stack
" issuing a :nohlsearch command to stop highlighting searches on top of this.
"
" This gets by far the most use in normal mode, but I'd like it to work in
" insert and visual modes, too, where it's occasionally useful, especially on
" things like mobile phone terminal emulators that can be choppy and require
" a lot of redrawing.
"
" For each of these, we end the mapping with a CTRL-L in normal mode, thereby
" extending rather than replacing Vim's normal behaviour.
"
nnoremap <C-L>
      \ :<C-U>nohlsearch<CR><C-L>

" The insert mode wrapper for normal CTRL-L uses CTRL-O to issue a single
" normal mode command.  We intentionally use `:normal` rather than `:normal!`
" so that the normal mode mapping applies.  I tried using just <C-O><C-L>
" directly for this, but it didn't work; maybe i_CTRL-O doesn't respect
" mappings, but I couldn't find any documentation about this.
"
inoremap <C-L> <C-O>:execute "normal \<C-L>"<CR>

" We use :vnoremap here rather than :xnoremap and thereby make the mapping
" apply to select mode as well, because CTRL-L doesn't reflect a printable
" character, and so we may as well make it work, even though I don't actually
" use select mode directly.
"
vmap <C-L> <Esc><C-L>gv

" By default, the very-useful normal mode command '&' that repeats the
" previous :substitute command doesn't preserve the flags from that
" substitution.  I'd prefer it to do so, like the :&& command does, and it's
" easily remapped for both normal and visual mode, so let's just do it.
"
nnoremap &
      \ :&&<CR>
xnoremap &
      \ :&&<CR>

" I really like using the '!' command in normal mode as an operator to filter
" text through a shell command.  It always bugged me a little that there
" didn't seem to be an analogue for a motion to filter text through an
" internal command like :sort, so I wrote one.
"
nmap g: <Plug>(ColonOperator)

" I used Tim Pope's unimpaired.vim plugin for ages, and I liked some of these
" bracket pair mappings, so I've carried a few of the simpler ones over.  All
" of these can be prefixed with a count if needed, too.  I use all of them
" pretty regularly, even though cycling through lists to look for something
" can be a bit wasteful.

" Argument list
nnoremap [a
      \ :previous<CR>
nnoremap ]a
      \ :next<CR>
" Buffers
nnoremap [b
      \ :bprevious<CR>
nnoremap ]b
      \ :bnext<CR>
" Quickfix list
nnoremap [c
      \ :cprevious<CR>
nnoremap ]c
      \ :cnext<CR>
" Location list
nnoremap [l
      \ :lprevious<CR>
nnoremap ]l
      \ :lnext<CR>

" Here's another mapping I particularly liked from unimpaired.vim; insert
" blank lines from normal mode, using a custom plugin of mine called
" put_blank_lines.vim.  These use operator functions so that they're
" repeatable without repeat.vim.  They accept count prefixes, too.
"
nmap [<Space> <Plug>(PutBlankLinesAbove)
nmap ]<Space> <Plug>(PutBlankLinesBelow)

" We're on to the leader maps, now.  It's difficult to know what order to put
" these in; I originally had them in alphabetical order, but it seems more
" useful now to group the by the category of their function, albeit roughly.
"
" First of all, let's set the leader keys; backslash happens to be the
" default, but I like to make my choice explicit here.
"
" As of 2019, I'm still not certain that comma is the best choice for my local
" leader.  I use it all the time for this purpose, and it works well, but
" I don't much like that it shadows a useful function in the fFtT;, group, and
" wonder if I would use it more if I hadn't shadowed it.
"
let mapleader = '\'
let maplocalleader = ','

" Let's start with some simple ones; these ones all just toggle a boolean
" option, and print its new value.  They're dirt simple to specify, and don't
" require any plugins.
"
" These are sometimes applicable in visual mode, and sometimes not.  We'll
" start with the ones that only make sense as normal mode maps.  Interesting,
" a visual mode mapping for 'cursorline' toggling doesn't work at all;
" 'cursorline' is always off when in any visual mode, including block mode,
" where it actually might have been really handy.

"" Leader,TAB toggles automatic indentation based on the previous line
nnoremap <Leader><Tab>
      \ :<C-U>setlocal autoindent! autoindent?<CR>
"" Leader,c toggles highlighted cursor row; doesn't work in visual mode
nnoremap <Leader>c
      \ :<C-U>setlocal cursorline! cursorline?<CR>
"" Leader,h toggles highlighting search results
nnoremap <Leader>h
      \ :<C-U>set hlsearch! hlsearch?<CR>
"" Leader,i toggles showing matches as I enter my pattern
nnoremap <Leader>i
      \ :<C-U>set incsearch! incsearch?<CR>
"" Leader,s toggles spell checking
nnoremap <Leader>s
      \ :<C-U>setlocal spell! spell?<CR>

" The next group of option-toggling maps are much the same as the previous
" group, except they also include analogous maps for visual mode, defined as
" recursive maps into normal mode that conclude with re-selecting the text.

"" Leader,C toggles highlighted cursor column; works in visual mode
nnoremap <Leader>C
      \ :<C-U>setlocal cursorcolumn! cursorcolumn?<CR>
xmap <Leader>C <Esc><Leader>Cgv
"" Leader,l toggles showing tab, end-of-line, and trailing white space
nnoremap <Leader>l
      \ :<C-U>setlocal list! list?<CR>
xmap <Leader>l <Esc><Leader>lgv
"" Leader,n toggles line number display
nnoremap <Leader>n
      \ :<C-U>setlocal number! number?<CR>
xmap <Leader>n <Esc><Leader>ngv
"" Leader,N toggles position display in bottom right
nnoremap <Leader>N
      \ :<C-U>set ruler! ruler?<CR>
xmap <Leader>N <Esc><Leader>Ngv
"" Leader,p toggles paste mode
nnoremap <Leader>p
      \ :<C-U>set paste! paste?<CR>
xmap <Leader>p <Esc><Leader>pgv
"" Leader,w toggles soft wrapping
nnoremap <Leader>w
      \ :<C-U>setlocal wrap! wrap?<CR>
xmap <Leader>w <Esc><Leader>wgv

" This next one just shows option state of the 'formatoptions' affecting how
" text is automatically formatted; it doesn't change its value.

"" Leader,f shows the current 'formatoptions' at a glance
nnoremap <Leader>f
      \ :<C-U>setlocal formatoptions?<CR>

" I often have to switch between US English and NZ English.  The latter is
" almost exactly the same as UK English in most locales, although we use
" dollars rather than pounds.  This is mostly so I remember things like
" excluding or including the 'u' in words like 'favourite', depending on the
" target audience.  I generally use US English for international audiences.

"" Leader,u sets US English spelling language
nnoremap <Leader>u
      \ :<C-U>setlocal spelllang=en_us<CR>
"" Leader,z sets NZ English spelling language
nnoremap <Leader>z
      \ :<C-U>setlocal spelllang=en_nz<CR>

" The next mapping is also for toggling an option, but it's more complicated;
" it uses a simple plugin of mine called copy_linebreak.vim to manage several
" options at once, related to the 'wrap' option that soft-wraps text.
"
" It's designed for usage in terminal emulators and multiplexers to
" temporarily make the buffer text suitable for copying in such a way that the
" wrapping and any associated soft formatting won't pervert the text,
" including 'breakindent', 'linebreak', and 'showbreak' artifacts.
"
" This is really handy for quick selections of small regions of text.  For
" larger blocks of text or for manipulating the text as it leaves the buffer,
" it makes more sense to use :! commands.
"

"" Leader,b toggles settings friendly to copying and pasting
nmap <Leader>b <Plug>(CopyLinebreakToggle)

" The above mappings show that mappings for toggling boolean options are
" simple, but there isn't a way to toggle single flags within option strings,
" so I wrote a plugin called toggle_flags.vim to provide :ToggleFlag and
" :ToggleFlagLocal commands.  The first argument is the name of an option, and
" the second is the flag within it that should be toggled on or off.

"" Leader,a toggles 'formatoptions' 'a' auto-flowing flag
nnoremap <Leader>a
      \ :<C-U>ToggleFlagLocal formatoptions a<CR>
"" Leader,L toggles 'colorcolumn' showing the first column beyond 'textwidth'
nnoremap <Leader>L
      \ :<C-U>ToggleFlagLocal colorcolumn +1<CR>
xmap <Leader>L <Esc><Leader>Lgv

" These mappings are for managing filetypes.  The first one uses the
" :ReloadFileType command that was defined much earlier in this file for
" application in the vimrc reload command.

"" Leader,F reloads filetype settings
nnoremap <Leader>F
      \ :<C-U>ReloadFileType<CR>
"" Leader,t shows current filetype
nnoremap <Leader>t
      \ :<C-U>setlocal filetype?<CR>
"" Leader,T clears filetype
nnoremap <Leader>T
      \ :<C-U>setlocal filetype=<CR>

" These mappings use my put_date.vim and utc.vim plugins for date insertion
" into the buffer.

"" Leader,d inserts the local date (RFC 2822)
nnoremap <Leader>d
      \ :PutDate<CR>
"" Leader,D inserts the UTC date (RFC 2822)
nnoremap <Leader>D
      \ :<Home>UTC<End> PutDate<CR>

" This group contains mappings that are to do with file and path management
" relative to the current buffer.  The Leader,P mapping that creates
" directory hierarchies uses the :Establish command created earlier.

"" Leader,g shows the current file's fully expanded path
nnoremap <Leader>g
      \ :<C-U>echo expand('%:p')<CR>
"" Leader,G changes directory to the current file's location
nnoremap <Leader>G
      \ :<C-U>cd %:h<Bar>pwd<CR>
"" Leader,P creates the path to the current file if it doesn't exist
nnoremap <Leader>P
      \ :<C-U>Establish %:h<CR>

" This group contains mappings that show information about Vim's internals:
" marks, registers, variables, and the like.

"" Leader,H shows command history
nnoremap <Leader>H
      \ :<C-U>history :<CR>
"" Leader,k shows my marks
nnoremap <Leader>k
      \ :<C-U>marks<CR>
"" Leader,m shows normal maps
nnoremap <Leader>m
      \ :<C-U>nmap<CR>
"" Leader,M shows buffer-local normal maps
nnoremap <Leader>M
      \ :<C-U>nmap <buffer><CR>
"" Leader,S shows loaded scripts
nnoremap <Leader>S
      \ :<C-U>scriptnames<CR>
"" Leader,v shows all global variables
nnoremap <Leader>v
      \ :<C-U>let g: v:<CR>
"" Leader,V shows all local variables
nnoremap <Leader>V
      \ :<C-U>let b: t: w:<CR>
"" Leader,y shows all registers
nnoremap <Leader>y
      \ :<C-U>registers<CR>

" This group contains mappings concerned with buffer navigation and
" management.  I use the "jetpack" buffer jumper one like crazy; I really like
" it.  I got it from one of bairui's "Vim and Vigor" comics:
"
" <http://of-vim-and-vigor.blogspot.com/p/vim-vigor-comic.html>

"" Leader,DEL deletes the current buffer
nnoremap <Leader><Delete>
      \ :bdelete<CR>
"" Leader,INS edits a new buffer
nnoremap <Leader><Insert>
      \ :<C-U>enew<CR>
"" Leader,e forces a buffer to be editable, even a :help one
nnoremap <Leader>e
      \ :<C-U>setlocal modifiable noreadonly<CR>
"" Leader,E locks a buffer, reversible with <Leader>e
nnoremap <Leader>E
      \ :<C-U>setlocal nomodifiable readonly<CR>
"" Leader,j jumps to buffers ("jetpack")
nnoremap <Leader>j
      \ :<C-U>buffers<CR>:buffer<Space>

" This ground defines mappings for filtering and batch operations to clean up
" buffer text.  All of these mappings use commands from my custom plugins:
"
" strip_trailing_whitespace.vim:
"   :StripTrailingWhitespace
" squeeze_repeat_blanks.vim:
"   :SqueezeRepeatBlanks
" keep_position.vim
"   :KeepPosition

"" Leader,x strips trailing whitespace via a custom plugin
nnoremap <Leader>x
      \ :StripTrailingWhitespace<CR>
xnoremap <Leader>x
      \ :StripTrailingWhitespace<CR>
"" Leader,X squeezes repeated blank lines via a custom plugin
nnoremap <Leader>X
      \ :SqueezeRepeatBlanks<CR>
xnoremap <Leader>X
      \ :SqueezeRepeatBlanks<CR>
"" Leader,= runs the whole buffer through =, preserving position
nnoremap <Leader>=
      \ :<C-U>KeepPosition execute 'normal! 1G=G'<CR>
"" Leader,+ runs the whole buffer through gq, preserving position
nnoremap <Leader>+
      \ :<C-U>KeepPosition execute 'normal! 1GgqG'<CR>

" This group defines a few :onoremap commands to make my own text objects.
" I should probably make some more of these, as they've proven to be
" terrifically handy.

"" Leader,_ uses last changed or yanked text as an object
onoremap <Leader>_
      \ :<C-U>execute 'normal! `[v`]'<CR>
"" Leader,% uses entire buffer as an object
onoremap <Leader>%
      \ :<C-U>execute 'normal! 1GVG'<CR>

" This group defines some useful motions.

"" Leader,{ and Leader,} move to lines with non-space chars before current column
map <Leader>{ <Plug>(VerticalRegionUp)
sunmap <Leader>{
map <Leader>} <Plug>(VerticalRegionDown)
sunmap <Leader>}
"" Leader,\ jumps to the last edit position mark: think "Now, where was I?"
nnoremap <Leader>\ `"
xnoremap <Leader>\ `"

" This group does both: useful motions on defined text objects.

"" Leader,< and Leader,> adjust indent of last edit; good for pasting
nnoremap <Leader><lt>
      \ :<C-U>'[,']<lt><CR>
nnoremap <Leader>>
      \ :<C-U>'[,']><CR>

" This group is for directory tree or help search convenience mappings.

"" Leader,/ types :vimgrep for me ready to enter a search pattern
nnoremap <Leader>/
      \ :<C-U>vimgrep /\c/j **<S-Left><S-Left><Right>
"" Leader,? types :lhelpgrep for me ready to enter a search pattern
nnoremap <Leader>?
      \ :<C-U>lhelpgrep \c<S-Left>

" This group contains miscellaneous mappings for which I couldn't find any
" other place.  The plugin mappings probably require their own documentation
" comment block, but my hands are getting tired from all this typing.

"" Leader,. runs the configured make program into the location list
nnoremap <Leader>.
      \ :<C-U>lmake!<CR>
"" Leader,o opens a line below in paste mode
nmap <Leader>o <Plug>(PasteOpenBelow)
"" Leader,O opens a line above in paste mode
nmap <Leader>O <Plug>(PasteOpenAbove)
"" Leader,q formats the current paragraph
nnoremap <Leader>q gqap
"" Leader,r acts as a replacement operator
nmap <Leader>r <Plug>(ReplaceOperator)
xmap <Leader>r <Plug>(ReplaceOperator)
"" Leader,* escapes regex metacharacters
nmap <Leader>* <Plug>(RegexEscape)
xmap <Leader>* <Plug>(RegexEscape)

" And last, but definitely not least, I'm required by Vim fanatic law to
" include a mapping that reloads my whole configuration.  This uses the
" command wrapper defined much earlier in the file, so that filetypes also get
" reloaded afterwards, meaning I don't need to follow <Leader>R with
" a <Leader>F to fix up broken global settings.

"" Leader,R reloads ~/.vimrc
nnoremap <Leader>R
      \ :<C-U>ReloadVimrc<CR>

" I'll close this file with a few abbreviations.  Perhaps of everything in
" here, I'm least confident that these should be in here, but they've proven
" pretty useful.  First, some 'deliberate' abbreviations for stuff I type
" a lot:
"
inoreabbrev tr@ tom@sanctum.geek.nz
inoreabbrev tr/ <https://sanctum.geek.nz/>

" And then, just fix some typographical and spelling errors for me
" automatically.
"
inoreabbrev almsot almost
inoreabbrev wrnog wrong
inoreabbrev Fielding Feilding
inoreabbrev THe The
inoreabbrev THere There

" Here endeth the literate vimrc.
"
" > Consequently, it is soon recognised that they write for the sake of
" > filling up the paper, and this is the case sometimes with the best
" > authors...as soon as this is perceived the book should be thrown away,
" > for time is precious.
" > -- Schopenhauer
"