on master help sign in
.github/workflows/release.yml+5−5
since splitsnapshots
unifiedsplit
@@ -36,7 +36,7 @@ jobs:
36 - name: Bundle grammars
36 - name: Bundle grammars
37 run: tar cJf grammars.tar.xz -C runtime/grammars/sources .
37 run: tar cJf grammars.tar.xz -C runtime/grammars/sources .
38
38
39− - uses: actions/upload-artifact@v3
39+ - uses: actions/upload-artifact@v4
40 with:
40 with:
41 name: grammars
41 name: grammars
42 path: grammars.tar.xz
42 path: grammars.tar.xz
@@ -106,7 +106,7 @@ jobs:
106 uses: actions/checkout@v4
106 uses: actions/checkout@v4
107
107
108 - name: Download grammars
108 - name: Download grammars
109− uses: actions/download-artifact@v3
109+ uses: actions/download-artifact@v4
110
110
111 - name: Move grammars under runtime
111 - name: Move grammars under runtime
112 if: "!startsWith(matrix.os, 'windows')"
112 if: "!startsWith(matrix.os, 'windows')"
@@ -220,7 +220,7 @@ jobs:
220 fi
220 fi
221 cp -r runtime dist
221 cp -r runtime dist
222
222
223− - uses: actions/upload-artifact@v3
223+ - uses: actions/upload-artifact@v4
224 with:
224 with:
225 name: bins-${{ matrix.build }}
225 name: bins-${{ matrix.build }}
226 path: dist
226 path: dist
@@ -233,7 +233,7 @@ jobs:
233 - name: Checkout sources
233 - name: Checkout sources
234 uses: actions/checkout@v4
234 uses: actions/checkout@v4
235
235
236− - uses: actions/download-artifact@v3
236+ - uses: actions/download-artifact@v4
237
237
238 - name: Build archive
238 - name: Build archive
239 shell: bash
239 shell: bash
@@ -288,7 +288,7 @@ jobs:
288 overwrite: true
288 overwrite: true
289
289
290 - name: Upload binaries as artifact
290 - name: Upload binaries as artifact
291− uses: actions/upload-artifact@v3
291+ uses: actions/upload-artifact@v4
292 if: env.preview == 'true'
292 if: env.preview == 'true'
293 with:
293 with:
294 name: release
294 name: release
Cargo.lock+126−115
@@ -62,9 +62,9 @@ dependencies = [
62
62
63[[package]]
63[[package]]
64name = "anyhow"
64name = "anyhow"
65−version = "1.0.75"
65+version = "1.0.76"
66source = "registry+https://github.com/rust-lang/crates.io-index"
66source = "registry+https://github.com/rust-lang/crates.io-index"
67−checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
67+checksum = "59d2a3357dde987206219e78ecfbbb6e8dad06cbb65292758d3270e6254f7355"
68
68
69[[package]]
69[[package]]
70name = "arc-swap"
70name = "arc-swap"
@@ -415,9 +415,9 @@ dependencies = [
415
415
416[[package]]
416[[package]]
417name = "faster-hex"
417name = "faster-hex"
418−version = "0.8.1"
418+version = "0.9.0"
419source = "registry+https://github.com/rust-lang/crates.io-index"
419source = "registry+https://github.com/rust-lang/crates.io-index"
420−checksum = "239f7bfb930f820ab16a9cd95afc26f88264cf6905c960b340a615384aa3338a"
420+checksum = "a2a2b11eda1d40935b26cf18f6833c526845ae8c41e58d09af6adeb6f0269183"
421dependencies = [
421dependencies = [
422 "serde",
422 "serde",
423]
423]
@@ -469,9 +469,9 @@ dependencies = [
469
469
470[[package]]
470[[package]]
471name = "futures-core"
471name = "futures-core"
472−version = "0.3.29"
472+version = "0.3.30"
473source = "registry+https://github.com/rust-lang/crates.io-index"
473source = "registry+https://github.com/rust-lang/crates.io-index"
474−checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c"
474+checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
475
475
476[[package]]
476[[package]]
477name = "futures-executor"
477name = "futures-executor"
@@ -486,15 +486,15 @@ dependencies = [
486
486
487[[package]]
487[[package]]
488name = "futures-task"
488name = "futures-task"
489−version = "0.3.29"
489+version = "0.3.30"
490source = "registry+https://github.com/rust-lang/crates.io-index"
490source = "registry+https://github.com/rust-lang/crates.io-index"
491−checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2"
491+checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
492
492
493[[package]]
493[[package]]
494name = "futures-util"
494name = "futures-util"
495−version = "0.3.29"
495+version = "0.3.30"
496source = "registry+https://github.com/rust-lang/crates.io-index"
496source = "registry+https://github.com/rust-lang/crates.io-index"
497−checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104"
497+checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
498dependencies = [
498dependencies = [
499 "futures-core",
499 "futures-core",
500 "futures-task",
500 "futures-task",
@@ -522,9 +522,9 @@ checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e"
522
522
523[[package]]
523[[package]]
524name = "gix"
524name = "gix"
525−version = "0.55.2"
525+version = "0.57.0"
526source = "registry+https://github.com/rust-lang/crates.io-index"
526source = "registry+https://github.com/rust-lang/crates.io-index"
527−checksum = "002667cd1ebb789313d0d0afe3d23b2821cf3b0e91605095f0e6d8751f0ceeea"
527+checksum = "721c7497ab24b665ed5a1eb2b3526936aa60068e61ba260651f7e77c52feec69"
528dependencies = [
528dependencies = [
529 "gix-actor",
529 "gix-actor",
530 "gix-commitgraph",
530 "gix-commitgraph",
@@ -563,46 +563,46 @@ dependencies = [
563
563
564[[package]]
564[[package]]
565name = "gix-actor"
565name = "gix-actor"
566−version = "0.28.0"
566+version = "0.29.0"
567source = "registry+https://github.com/rust-lang/crates.io-index"
567source = "registry+https://github.com/rust-lang/crates.io-index"
568−checksum = "948a5f9e43559d16faf583694f1c742eb401ce24ce8e6f2238caedea7486433c"
568+checksum = "886014c4865b93ce268f1d3eddd4fd3261242c3f3ee61eb36009f913016a9059"
569dependencies = [
569dependencies = [
570 "bstr",
570 "bstr",
571 "btoi",
571 "btoi",
572 "gix-date",
572 "gix-date",
573 "itoa",
573 "itoa",
574 "thiserror",
574 "thiserror",
575− "winnow 0.5.17",
575+ "winnow 0.5.28",
576]
576]
577
577
578[[package]]
578[[package]]
579name = "gix-chunk"
579name = "gix-chunk"
580−version = "0.4.4"
580+version = "0.4.6"
581source = "registry+https://github.com/rust-lang/crates.io-index"
581source = "registry+https://github.com/rust-lang/crates.io-index"
582−checksum = "5b42ea64420f7994000130328f3c7a2038f639120518870436d31b8bde704493"
582+checksum = "2ade37ef69870de0ed966b97c57a3a947a22ff3482a52c3b99b205f77bcb08fb"
583dependencies = [
583dependencies = [
584 "thiserror",
584 "thiserror",
585]
585]
586
586
587[[package]]
587[[package]]
588name = "gix-commitgraph"
588name = "gix-commitgraph"
589−version = "0.22.0"
589+version = "0.23.0"
590source = "registry+https://github.com/rust-lang/crates.io-index"
590source = "registry+https://github.com/rust-lang/crates.io-index"
591−checksum = "7e8bc78b1a6328fa6d8b3a53b6c73997af37fd6bfc1d6c49f149e63bda5cbb36"
591+checksum = "e7559ea9cefee188cd88b05afcc8e3ef7a3cb4a5c647bccf06b981e591b02b77"
592dependencies = [
592dependencies = [
593 "bstr",
593 "bstr",
594 "gix-chunk",
594 "gix-chunk",
595 "gix-features",
595 "gix-features",
596 "gix-hash",
596 "gix-hash",
597− "memmap2 0.7.1",
597+ "memmap2",
598 "thiserror",
598 "thiserror",
599]
599]
600
600
601[[package]]
601[[package]]
602name = "gix-config"
602name = "gix-config"
603−version = "0.31.0"
603+version = "0.33.0"
604source = "registry+https://github.com/rust-lang/crates.io-index"
604source = "registry+https://github.com/rust-lang/crates.io-index"
605−checksum = "5cae98c6b4c66c09379bc35274b172587d6b0ac369a416c39128ad8c6454f9bb"
605+checksum = "9c35dc7f9c00a42bbc9cfa1ca2ec0a78ad1b76ff0736d3d35dfd612962244467"
606dependencies = [
606dependencies = [
607 "bstr",
607 "bstr",
608 "gix-config-value",
608 "gix-config-value",
@@ -616,14 +616,14 @@ dependencies = [
616 "smallvec",
616 "smallvec",
617 "thiserror",
617 "thiserror",
618 "unicode-bom",
618 "unicode-bom",
619− "winnow 0.5.17",
619+ "winnow 0.5.28",
620]
620]
621
621
622[[package]]
622[[package]]
623name = "gix-config-value"
623name = "gix-config-value"
624−version = "0.14.0"
624+version = "0.14.2"
625source = "registry+https://github.com/rust-lang/crates.io-index"
625source = "registry+https://github.com/rust-lang/crates.io-index"
626−checksum = "ea7505b97f4d8e7933e29735a568ba2f86d8de466669d9f0e8321384f9972f47"
626+checksum = "39f388cb2396aee82d6f460a2e7770659bdf8854e9e5478f7d2b1324a9698284"
627dependencies = [
627dependencies = [
628 "bitflags 2.4.1",
628 "bitflags 2.4.1",
629 "bstr",
629 "bstr",
@@ -634,9 +634,9 @@ dependencies = [
634
634
635[[package]]
635[[package]]
636name = "gix-date"
636name = "gix-date"
637−version = "0.8.0"
637+version = "0.8.2"
638source = "registry+https://github.com/rust-lang/crates.io-index"
638source = "registry+https://github.com/rust-lang/crates.io-index"
639−checksum = "fc7df669639582dc7c02737642f76890b03b5544e141caba68a7d6b4eb551e0d"
639+checksum = "9d85f4a01e0d05c3de585e28bae514d4baf01655e3fc3f14ce6f30bf62405345"
640dependencies = [
640dependencies = [
641 "bstr",
641 "bstr",
642 "itoa",
642 "itoa",
@@ -646,10 +646,11 @@ dependencies = [
646
646
647[[package]]
647[[package]]
648name = "gix-diff"
648name = "gix-diff"
649−version = "0.37.0"
649+version = "0.39.0"
650source = "registry+https://github.com/rust-lang/crates.io-index"
650source = "registry+https://github.com/rust-lang/crates.io-index"
651−checksum = "931394f69fb8c9ed6afc0aae3487bd869e936339bcc13ed8884472af072e0554"
651+checksum = "ac0e75f5afd2f6c47c800b6b0a000a08045739d0450d20482e8faa42543f62d1"
652dependencies = [
652dependencies = [
653+ "bstr",
653 "gix-hash",
654 "gix-hash",
654 "gix-object",
655 "gix-object",
655 "thiserror",
656 "thiserror",
@@ -657,9 +658,9 @@ dependencies = [
657
658
658[[package]]
659[[package]]
659name = "gix-discover"
660name = "gix-discover"
660−version = "0.26.0"
661+version = "0.28.0"
661source = "registry+https://github.com/rust-lang/crates.io-index"
662source = "registry+https://github.com/rust-lang/crates.io-index"
662−checksum = "a45d5cf0321178883e38705ab2b098f625d609a7d4c391b33ac952eff2c490f2"
663+checksum = "f0b63ce2ad81632ac1a84ac370f85a5e580c3261580bd85ea17ff35d3a0bba18"
663dependencies = [
664dependencies = [
664 "bstr",
665 "bstr",
665 "dunce",
666 "dunce",
@@ -672,9 +673,9 @@ dependencies = [
672
673
673[[package]]
674[[package]]
674name = "gix-features"
675name = "gix-features"
675−version = "0.36.0"
676+version = "0.37.0"
676source = "registry+https://github.com/rust-lang/crates.io-index"
677source = "registry+https://github.com/rust-lang/crates.io-index"
677−checksum = "51f4365ba17c4f218d7fd9ec102b8d2d3cb0ca200a835e81151ace7778aec827"
678+checksum = "befe7edea299a824504b5acc96d7a3a538125b38c42f3a8379f6912a29c90c81"
678dependencies = [
679dependencies = [
679 "crc32fast",
680 "crc32fast",
680 "flate2",
681 "flate2",
@@ -690,18 +691,18 @@ dependencies = [
690
691
691[[package]]
692[[package]]
692name = "gix-fs"
693name = "gix-fs"
693−version = "0.8.0"
694+version = "0.9.0"
694source = "registry+https://github.com/rust-lang/crates.io-index"
695source = "registry+https://github.com/rust-lang/crates.io-index"
695−checksum = "8cd171c0cae97cd0dc57e7b4601cb1ebf596450e263ef3c02be9107272c877bd"
696+checksum = "007017ce93b819ea52c0ec68306f7d72212a2d307c3d70f1548c7141c015d0a1"
696dependencies = [
697dependencies = [
697 "gix-features",
698 "gix-features",
698]
699]
699
700
700[[package]]
701[[package]]
701name = "gix-glob"
702name = "gix-glob"
702−version = "0.14.0"
703+version = "0.15.0"
703source = "registry+https://github.com/rust-lang/crates.io-index"
704source = "registry+https://github.com/rust-lang/crates.io-index"
704−checksum = "8fac08925dbc14d414bd02eb45ffb4cecd912d1fce3883f867bd0103c192d3e4"
705+checksum = "61fb116c2516d3a1170e010118f639944a6389872c875a008960cdab2a44ac72"
705dependencies = [
706dependencies = [
706 "bitflags 2.4.1",
707 "bitflags 2.4.1",
707 "bstr",
708 "bstr",
@@ -711,9 +712,9 @@ dependencies = [
711
712
712[[package]]
713[[package]]
713name = "gix-hash"
714name = "gix-hash"
714−version = "0.13.1"
715+version = "0.14.0"
715source = "registry+https://github.com/rust-lang/crates.io-index"
716source = "registry+https://github.com/rust-lang/crates.io-index"
716−checksum = "1884c7b41ea0875217c1be9ce91322f90bde433e91d374d0e1276073a51ccc60"
717+checksum = "c52c3170a17b6031833cd2eb4ad7bc23f2755d06e6e70f78dec21d42e8fe1b30"
717dependencies = [
718dependencies = [
718 "faster-hex",
719 "faster-hex",
719 "thiserror",
720 "thiserror",
@@ -721,9 +722,9 @@ dependencies = [
721
722
722[[package]]
723[[package]]
723name = "gix-hashtable"
724name = "gix-hashtable"
724−version = "0.4.0"
725+version = "0.5.0"
725source = "registry+https://github.com/rust-lang/crates.io-index"
726source = "registry+https://github.com/rust-lang/crates.io-index"
726−checksum = "409268480841ad008e81c17ca5a293393fbf9f2b6c2f85b8ab9de1f0c5176a16"
727+checksum = "40a838e6366a5e5b84668b6997ce0981b833136468e8ba949f424c0ef2927eba"
727dependencies = [
728dependencies = [
728 "gix-hash",
729 "gix-hash",
729 "hashbrown 0.14.3",
730 "hashbrown 0.14.3",
@@ -732,9 +733,9 @@ dependencies = [
732
733
733[[package]]
734[[package]]
734name = "gix-lock"
735name = "gix-lock"
735−version = "11.0.0"
736+version = "12.0.0"
736source = "registry+https://github.com/rust-lang/crates.io-index"
737source = "registry+https://github.com/rust-lang/crates.io-index"
737−checksum = "f4feb1dcd304fe384ddc22edba9dd56a42b0800032de6537728cea2f033a4f37"
738+checksum = "6cf112ddee94223c119a8534dad027740dc3aba3365ac5edeef8a7f6660c74db"
738dependencies = [
739dependencies = [
739 "gix-tempfile",
740 "gix-tempfile",
740 "gix-utils",
741 "gix-utils",
@@ -743,9 +744,9 @@ dependencies = [
743
744
744[[package]]
745[[package]]
745name = "gix-macros"
746name = "gix-macros"
746−version = "0.1.0"
747+version = "0.1.2"
747source = "registry+https://github.com/rust-lang/crates.io-index"
748source = "registry+https://github.com/rust-lang/crates.io-index"
748−checksum = "9d8acb5ee668d55f0f2d19a320a3f9ef67a6999ad483e11135abcc2464ed18b6"
749+checksum = "fc207b64189cf71bcb17fc841ab99a5d63d0845546b611e2703ce467f659323a"
749dependencies = [
750dependencies = [
750 "proc-macro2",
751 "proc-macro2",
751 "quote",
752 "quote",
@@ -754,9 +755,9 @@ dependencies = [
754
755
755[[package]]
756[[package]]
756name = "gix-object"
757name = "gix-object"
757−version = "0.38.0"
758+version = "0.40.0"
758source = "registry+https://github.com/rust-lang/crates.io-index"
759source = "registry+https://github.com/rust-lang/crates.io-index"
759−checksum = "740f2a44267f58770a1cb3a3d01d14e67b089c7136c48d4bddbb3cfd2bf86a51"
760+checksum = "e4c77e47ffba92127faf632a841fce23d19547e269bd8b88e68961a70eab4e93"
760dependencies = [
761dependencies = [
761 "bstr",
762 "bstr",
762 "btoi",
763 "btoi",
@@ -768,14 +769,14 @@ dependencies = [
768 "itoa",
769 "itoa",
769 "smallvec",
770 "smallvec",
770 "thiserror",
771 "thiserror",
771− "winnow 0.5.17",
772+ "winnow 0.5.28",
772]
773]
773
774
774[[package]]
775[[package]]
775name = "gix-odb"
776name = "gix-odb"
776−version = "0.54.0"
777+version = "0.56.0"
777source = "registry+https://github.com/rust-lang/crates.io-index"
778source = "registry+https://github.com/rust-lang/crates.io-index"
778−checksum = "8630b56cb80d8fa684d383dad006a66401ee8314e12fbf0e566ddad8c115143b"
779+checksum = "a3254f2005cc7553ea78e85e816a09150c6f7a64e6b7627b8d1fdc56721bea73"
779dependencies = [
780dependencies = [
780 "arc-swap",
781 "arc-swap",
781 "gix-date",
782 "gix-date",
@@ -792,9 +793,9 @@ dependencies = [
792
793
793[[package]]
794[[package]]
794name = "gix-pack"
795name = "gix-pack"
795−version = "0.44.0"
796+version = "0.46.0"
796source = "registry+https://github.com/rust-lang/crates.io-index"
797source = "registry+https://github.com/rust-lang/crates.io-index"
797−checksum = "1431ba2e30deff1405920693d54ab231c88d7c240dd6ccc936ee223d8f8697c3"
798+checksum = "02ebc8cd657eec207d82d8f876ca402361308ecd3c87a47935b0299506257b4f"
798dependencies = [
799dependencies = [
799 "clru",
800 "clru",
800 "gix-chunk",
801 "gix-chunk",
@@ -804,7 +805,7 @@ dependencies = [
804 "gix-object",
805 "gix-object",
805 "gix-path",
806 "gix-path",
806 "gix-tempfile",
807 "gix-tempfile",
807− "memmap2 0.7.1",
808+ "memmap2",
808 "parking_lot",
809 "parking_lot",
809 "smallvec",
810 "smallvec",
810 "thiserror",
811 "thiserror",
@@ -812,9 +813,9 @@ dependencies = [
812
813
813[[package]]
814[[package]]
814name = "gix-path"
815name = "gix-path"
815−version = "0.10.0"
816+version = "0.10.2"
816source = "registry+https://github.com/rust-lang/crates.io-index"
817source = "registry+https://github.com/rust-lang/crates.io-index"
817−checksum = "6a1d370115171e3ae03c5c6d4f7d096f2981a40ddccb98dfd704c773530ba73b"
818+checksum = "10931652a3da126990ac93bce1b1c600cc99d7c268d712b6360ed52174ce1b68"
818dependencies = [
819dependencies = [
819 "bstr",
820 "bstr",
820 "gix-trace",
821 "gix-trace",
@@ -825,9 +826,9 @@ dependencies = [
825
826
826[[package]]
827[[package]]
827name = "gix-quote"
828name = "gix-quote"
828−version = "0.4.7"
829+version = "0.4.9"
829source = "registry+https://github.com/rust-lang/crates.io-index"
830source = "registry+https://github.com/rust-lang/crates.io-index"
830−checksum = "475c86a97dd0127ba4465fbb239abac9ea10e68301470c9791a6dd5351cdc905"
831+checksum = "6c43530cb94a7759807e6f8d180e17ac9c65673b891645c6c433831dc0cf4342"
831dependencies = [
832dependencies = [
832 "bstr",
833 "bstr",
833 "btoi",
834 "btoi",
@@ -836,9 +837,9 @@ dependencies = [
836
837
837[[package]]
838[[package]]
838name = "gix-ref"
839name = "gix-ref"
839−version = "0.38.0"
840+version = "0.40.0"
840source = "registry+https://github.com/rust-lang/crates.io-index"
841source = "registry+https://github.com/rust-lang/crates.io-index"
841−checksum = "0ec2f6d07ac88d2fb8007ee3fa3e801856fb9d82e7366ec0ca332eb2c9d74a52"
842+checksum = "baa951b3b850d6d1be4f900768e49af20b76bf9505beac22af723d57249a2f1d"
842dependencies = [
843dependencies = [
843 "gix-actor",
844 "gix-actor",
844 "gix-date",
845 "gix-date",
@@ -850,16 +851,16 @@ dependencies = [
850 "gix-path",
851 "gix-path",
851 "gix-tempfile",
852 "gix-tempfile",
852 "gix-validate",
853 "gix-validate",
853− "memmap2 0.7.1",
854+ "memmap2",
854 "thiserror",
855 "thiserror",
855− "winnow 0.5.17",
856+ "winnow 0.5.28",
856]
857]
857
858
858[[package]]
859[[package]]
859name = "gix-refspec"
860name = "gix-refspec"
860−version = "0.19.0"
861+version = "0.21.0"
861source = "registry+https://github.com/rust-lang/crates.io-index"
862source = "registry+https://github.com/rust-lang/crates.io-index"
862−checksum = "ccb0974cc41dbdb43a180c7f67aa481e1c1e160fcfa8f4a55291fd1126c1a6e7"
863+checksum = "88c61f849d58c06e3068a0b601cf10127d2a07cdad00a725ed66cf303f76f6b3"
863dependencies = [
864dependencies = [
864 "bstr",
865 "bstr",
865 "gix-hash",
866 "gix-hash",
@@ -871,9 +872,9 @@ dependencies = [
871
872
872[[package]]
873[[package]]
873name = "gix-revision"
874name = "gix-revision"
874−version = "0.23.0"
875+version = "0.25.0"
875source = "registry+https://github.com/rust-lang/crates.io-index"
876source = "registry+https://github.com/rust-lang/crates.io-index"
876−checksum = "2ca97ac73459a7f3766aa4a5638a6e37d56d4c7962bc1986fbaf4883d0772588"
877+checksum = "bcbbf91f4200c5c76802ef5f057b96f5a336827508881fe55a780be71a794d22"
877dependencies = [
878dependencies = [
878 "bstr",
879 "bstr",
879 "gix-date",
880 "gix-date",
@@ -887,9 +888,9 @@ dependencies = [
887
888
888[[package]]
889[[package]]
889name = "gix-revwalk"
890name = "gix-revwalk"
890−version = "0.9.0"
891+version = "0.11.0"
891source = "registry+https://github.com/rust-lang/crates.io-index"
892source = "registry+https://github.com/rust-lang/crates.io-index"
892−checksum = "a16d8c892e4cd676d86f0265bf9d40cefd73d8d94f86b213b8b77d50e77efae0"
893+checksum = "33ab66354d83f70f2730391747d0c75f94ef3c3dd40ebde2e206db9faaf7a0a7"
893dependencies = [
894dependencies = [
894 "gix-commitgraph",
895 "gix-commitgraph",
895 "gix-date",
896 "gix-date",
@@ -902,21 +903,21 @@ dependencies = [
902
903
903[[package]]
904[[package]]
904name = "gix-sec"
905name = "gix-sec"
905−version = "0.10.0"
906+version = "0.10.2"
906source = "registry+https://github.com/rust-lang/crates.io-index"
907source = "registry+https://github.com/rust-lang/crates.io-index"
907−checksum = "92b9542ac025a8c02ed5d17b3fc031a111a384e859d0be3532ec4d58c40a0f28"
908+checksum = "a9963f38a42144253ed4d571882d7db5ef644c12e6726e4b75135597cc9d0e1a"
908dependencies = [
909dependencies = [
909 "bitflags 2.4.1",
910 "bitflags 2.4.1",
910 "gix-path",
911 "gix-path",
911 "libc",
912 "libc",
912− "windows",
913+ "windows 0.52.0",
913]
914]
914
915
915[[package]]
916[[package]]
916name = "gix-tempfile"
917name = "gix-tempfile"
917−version = "11.0.0"
918+version = "12.0.0"
918source = "registry+https://github.com/rust-lang/crates.io-index"
919source = "registry+https://github.com/rust-lang/crates.io-index"
919−checksum = "05cc2205cf10d99f70b96e04e16c55d4c7cf33efc151df1f793e29fd12a931f8"
920+checksum = "e76a494bd530e1a1309188ff971825a24f159c76c2db0bf71fa5dfb469a2c915"
920dependencies = [
921dependencies = [
921 "gix-fs",
922 "gix-fs",
922 "libc",
923 "libc",
@@ -927,15 +928,15 @@ dependencies = [
927
928
928[[package]]
929[[package]]
929name = "gix-trace"
930name = "gix-trace"
930−version = "0.1.3"
931+version = "0.1.5"
931source = "registry+https://github.com/rust-lang/crates.io-index"
932source = "registry+https://github.com/rust-lang/crates.io-index"
932−checksum = "96b6d623a1152c3facb79067d6e2ecdae48130030cf27d6eb21109f13bd7b836"
933+checksum = "bda62acb44dd86a40c7c3762a5403cfc1ac789ea559df54085cedf79864f809e"
933
934
934[[package]]
935[[package]]
935name = "gix-traverse"
936name = "gix-traverse"
936−version = "0.34.0"
937+version = "0.36.0"
937source = "registry+https://github.com/rust-lang/crates.io-index"
938source = "registry+https://github.com/rust-lang/crates.io-index"
938−checksum = "14d050ec7d4e1bb76abf0636cf4104fb915b70e54e3ced9a4427c999100ff38a"
939+checksum = "8661fab39985c9214e56d81a63ceb5886ac948cec2fba76c39494d1e0e307ea8"
939dependencies = [
940dependencies = [
940 "gix-commitgraph",
941 "gix-commitgraph",
941 "gix-date",
942 "gix-date",
@@ -949,9 +950,9 @@ dependencies = [
949
950
950[[package]]
951[[package]]
951name = "gix-url"
952name = "gix-url"
952−version = "0.25.1"
953+version = "0.26.0"
953source = "registry+https://github.com/rust-lang/crates.io-index"
954source = "registry+https://github.com/rust-lang/crates.io-index"
954−checksum = "b1b9ac8ed32ad45f9fc6c5f8c0be2ed911e544a5a19afd62d95d524ebaa95671"
955+checksum = "10a0129c1e8b52736d7c5128300a4485dbc85863001371e2771ac1754bd89fd7"
955dependencies = [
956dependencies = [
956 "bstr",
957 "bstr",
957 "gix-features",
958 "gix-features",
@@ -963,18 +964,18 @@ dependencies = [
963
964
964[[package]]
965[[package]]
965name = "gix-utils"
966name = "gix-utils"
966−version = "0.1.5"
967+version = "0.1.7"
967source = "registry+https://github.com/rust-lang/crates.io-index"
968source = "registry+https://github.com/rust-lang/crates.io-index"
968−checksum = "b85d89dc728613e26e0ed952a19583744e7f5240fcd4aa30d6c824ffd8b52f0f"
969+checksum = "ab9277a5e32e85d53f738096d872a4a9b76067ad471894ad31bd99c8fa2da1dc"
969dependencies = [
970dependencies = [
970 "fastrand",
971 "fastrand",
971]
972]
972
973
973[[package]]
974[[package]]
974name = "gix-validate"
975name = "gix-validate"
975−version = "0.8.0"
976+version = "0.8.2"
976source = "registry+https://github.com/rust-lang/crates.io-index"
977source = "registry+https://github.com/rust-lang/crates.io-index"
977−checksum = "e05cab2b03a45b866156e052aa38619f4ece4adcb2f79978bfc249bc3b21b8c5"
978+checksum = "f805ebbdbaa4bfd98e2ee43e6d14099b8a4d9141f5d7b8202fea4d48e44263e7"
978dependencies = [
979dependencies = [
979 "bstr",
980 "bstr",
980 "thiserror",
981 "thiserror",
@@ -1027,7 +1028,7 @@ dependencies = [
1027 "grep-matcher",
1028 "grep-matcher",
1028 "log",
1029 "log",
1029 "memchr",
1030 "memchr",
1030− "memmap2 0.9.0",
1031+ "memmap2",
1031]
1032]
1032
1033
1033[[package]]
1034[[package]]
@@ -1285,7 +1286,7 @@ dependencies = [
1285 "iana-time-zone-haiku",
1286 "iana-time-zone-haiku",
1286 "js-sys",
1287 "js-sys",
1287 "wasm-bindgen",
1288 "wasm-bindgen",
1288− "windows",
1289+ "windows 0.48.0",
1289]
1290]
1290
1291
1291[[package]]
1292[[package]]
@@ -1386,9 +1387,9 @@ dependencies = [
1386
1387
1387[[package]]
1388[[package]]
1388name = "libc"
1389name = "libc"
1389−version = "0.2.150"
1390+version = "0.2.151"
1390source = "registry+https://github.com/rust-lang/crates.io-index"
1391source = "registry+https://github.com/rust-lang/crates.io-index"
1391−checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
1392+checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
1392
1393
1393[[package]]
1394[[package]]
1394name = "libloading"
1395name = "libloading"
@@ -1433,9 +1434,9 @@ checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
1433
1434
1434[[package]]
1435[[package]]
1435name = "lsp-types"
1436name = "lsp-types"
1436−version = "0.94.1"
1437+version = "0.95.0"
1437source = "registry+https://github.com/rust-lang/crates.io-index"
1438source = "registry+https://github.com/rust-lang/crates.io-index"
1438−checksum = "c66bfd44a06ae10647fe3f8214762e9369fd4248df1350924b4ef9e770a85ea1"
1439+checksum = "158c1911354ef73e8fe42da6b10c0484cb65c7f1007f28022e847706c1ab6984"
1439dependencies = [
1440dependencies = [
1440 "bitflags 1.3.2",
1441 "bitflags 1.3.2",
1441 "serde",
1442 "serde",
@@ -1450,15 +1451,6 @@ version = "2.6.3"
1450source = "registry+https://github.com/rust-lang/crates.io-index"
1451source = "registry+https://github.com/rust-lang/crates.io-index"
1451checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c"
1452checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c"
1452
1453
1453−[[package]]
1454−name = "memmap2"
1455−version = "0.7.1"
1456−source = "registry+https://github.com/rust-lang/crates.io-index"
1457−checksum = "f49388d20533534cd19360ad3d6a7dadc885944aa802ba3995040c5ec11288c6"
1458−dependencies = [
1459− "libc",
1460−]
1461−
1462[[package]]
1454[[package]]
1463name = "memmap2"
1455name = "memmap2"
1464version = "0.9.0"
1456version = "0.9.0"
@@ -1559,9 +1551,9 @@ dependencies = [
1559
1551
1560[[package]]
1552[[package]]
1561name = "once_cell"
1553name = "once_cell"
1562−version = "1.18.0"
1554+version = "1.19.0"
1563source = "registry+https://github.com/rust-lang/crates.io-index"
1555source = "registry+https://github.com/rust-lang/crates.io-index"
1564−checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
1556+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
1565
1557
1566[[package]]
1558[[package]]
1567name = "open"
1559name = "open"
@@ -1632,9 +1624,9 @@ dependencies = [
1632
1624
1633[[package]]
1625[[package]]
1634name = "prodash"
1626name = "prodash"
1635−version = "26.2.2"
1627+version = "28.0.0"
1636source = "registry+https://github.com/rust-lang/crates.io-index"
1628source = "registry+https://github.com/rust-lang/crates.io-index"
1637−checksum = "794b5bf8e2d19b53dcdcec3e4bba628e20f5b6062503ba89281fa7037dd7bbcf"
1629+checksum = "744a264d26b88a6a7e37cbad97953fa233b94d585236310bcbc88474b4092d79"
1638
1630
1639[[package]]
1631[[package]]
1640name = "pulldown-cmark"
1632name = "pulldown-cmark"
@@ -1770,9 +1762,9 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
1770
1762
1771[[package]]
1763[[package]]
1772name = "rustix"
1764name = "rustix"
1773−version = "0.38.26"
1765+version = "0.38.28"
1774source = "registry+https://github.com/rust-lang/crates.io-index"
1766source = "registry+https://github.com/rust-lang/crates.io-index"
1775−checksum = "9470c4bf8246c8daf25f9598dca807fb6510347b1e1cfa55749113850c79d88a"
1767+checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316"
1776dependencies = [
1768dependencies = [
1777 "bitflags 2.4.1",
1769 "bitflags 2.4.1",
1778 "errno",
1770 "errno",
@@ -2036,18 +2028,18 @@ dependencies = [
2036
2028
2037[[package]]
2029[[package]]
2038name = "thiserror"
2030name = "thiserror"
2039−version = "1.0.50"
2031+version = "1.0.52"
2040source = "registry+https://github.com/rust-lang/crates.io-index"
2032source = "registry+https://github.com/rust-lang/crates.io-index"
2041−checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"
2033+checksum = "83a48fd946b02c0a526b2e9481c8e2a17755e47039164a86c4070446e3a4614d"
2042dependencies = [
2034dependencies = [
2043 "thiserror-impl",
2035 "thiserror-impl",
2044]
2036]
2045
2037
2046[[package]]
2038[[package]]
2047name = "thiserror-impl"
2039name = "thiserror-impl"
2048−version = "1.0.50"
2040+version = "1.0.52"
2049source = "registry+https://github.com/rust-lang/crates.io-index"
2041source = "registry+https://github.com/rust-lang/crates.io-index"
2050−checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
2042+checksum = "e7fbe9b594d6568a6a1443250a7e67d80b74e1e96f6d1715e1e21cc1888291d3"
2051dependencies = [
2043dependencies = [
2052 "proc-macro2",
2044 "proc-macro2",
2053 "quote",
2045 "quote",
@@ -2109,9 +2101,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
2109
2101
2110[[package]]
2102[[package]]
2111name = "tokio"
2103name = "tokio"
2112−version = "1.35.0"
2104+version = "1.35.1"
2113source = "registry+https://github.com/rust-lang/crates.io-index"
2105source = "registry+https://github.com/rust-lang/crates.io-index"
2114−checksum = "841d45b238a16291a4e1584e61820b8ae57d696cc5015c459c229ccc6990cc1c"
2106+checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104"
2115dependencies = [
2107dependencies = [
2116 "backtrace",
2108 "backtrace",
2117 "bytes",
2109 "bytes",
@@ -2392,6 +2384,25 @@ dependencies = [
2392 "windows-targets 0.48.0",
2384 "windows-targets 0.48.0",
2393]
2385]
2394
2386
2387+[[package]]
2388+name = "windows"
2389+version = "0.52.0"
2390+source = "registry+https://github.com/rust-lang/crates.io-index"
2391+checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
2392+dependencies = [
2393+ "windows-core",
2394+ "windows-targets 0.52.0",
2395+]
2396+
2397+[[package]]
2398+name = "windows-core"
2399+version = "0.52.0"
2400+source = "registry+https://github.com/rust-lang/crates.io-index"
2401+checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
2402+dependencies = [
2403+ "windows-targets 0.52.0",
2404+]
2405+
2395[[package]]
2406[[package]]
2396name = "windows-sys"
2407name = "windows-sys"
2397version = "0.45.0"
2408version = "0.45.0"
@@ -2601,9 +2612,9 @@ dependencies = [
2601
2612
2602[[package]]
2613[[package]]
2603name = "winnow"
2614name = "winnow"
2604−version = "0.5.17"
2615+version = "0.5.28"
2605source = "registry+https://github.com/rust-lang/crates.io-index"
2616source = "registry+https://github.com/rust-lang/crates.io-index"
2606−checksum = "a3b801d0e0a6726477cc207f60162da452f3a95adb368399bef20a946e06f65c"
2617+checksum = "6c830786f7720c2fd27a1a0e27a709dbd3c4d009b56d098fc742d4f4eab91fe2"
2607dependencies = [
2618dependencies = [
2608 "memchr",
2619 "memchr",
2609]
2620]
@@ -2621,18 +2632,18 @@ dependencies = [
2621
2632
2622[[package]]
2633[[package]]
2623name = "zerocopy"
2634name = "zerocopy"
2624−version = "0.7.20"
2635+version = "0.7.31"
2625source = "registry+https://github.com/rust-lang/crates.io-index"
2636source = "registry+https://github.com/rust-lang/crates.io-index"
2626−checksum = "dd66a62464e3ffd4e37bd09950c2b9dd6c4f8767380fabba0d523f9a775bc85a"
2637+checksum = "1c4061bedbb353041c12f413700357bec76df2c7e2ca8e4df8bac24c6bf68e3d"
2627dependencies = [
2638dependencies = [
2628 "zerocopy-derive",
2639 "zerocopy-derive",
2629]
2640]
2630
2641
2631[[package]]
2642[[package]]
2632name = "zerocopy-derive"
2643name = "zerocopy-derive"
2633−version = "0.7.20"
2644+version = "0.7.31"
2634source = "registry+https://github.com/rust-lang/crates.io-index"
2645source = "registry+https://github.com/rust-lang/crates.io-index"
2635−checksum = "255c4596d41e6916ced49cfafea18727b24d67878fa180ddfd69b9df34fd1726"
2646+checksum = "b3c129550b3e6de3fd0ba67ba5c81818f9805e58b8d7fee80a3a59d2c9fc601a"
2636dependencies = [
2647dependencies = [
2637 "proc-macro2",
2648 "proc-macro2",
2638 "quote",
2649 "quote",
README.md+1−1
@@ -18,7 +18,7 @@
18
18
19![Screenshot](./screenshot.png)
19![Screenshot](./screenshot.png)
20
20
21−A Kakoune / Neovim inspired editor, written in Rust.
21+A [Kakoune](https://github.com/mawww/kakoune) / [Neovim](https://github.com/neovim/neovim) inspired editor, written in Rust.
22
22
23The editing model is very heavily based on Kakoune; during development I found
23The editing model is very heavily based on Kakoune; during development I found
24myself agreeing with most of Kakoune's design decisions.
24myself agreeing with most of Kakoune's design decisions.
book/src/configuration.md+17
@@ -65,6 +65,8 @@ Its settings will be merged with the configuration directory `config.toml` and t
65| `workspace-lsp-roots` | Directories relative to the workspace root that are treated as LSP roots. Should only be set in `.helix/config.toml` | `[]` |
65| `workspace-lsp-roots` | Directories relative to the workspace root that are treated as LSP roots. Should only be set in `.helix/config.toml` | `[]` |
66| `default-line-ending` | The line ending to use for new documents. Can be `native`, `lf`, `crlf`, `ff`, `cr` or `nel`. `native` uses the platform's native line ending (`crlf` on Windows, otherwise `lf`). | `native` |
66| `default-line-ending` | The line ending to use for new documents. Can be `native`, `lf`, `crlf`, `ff`, `cr` or `nel`. `native` uses the platform's native line ending (`crlf` on Windows, otherwise `lf`). | `native` |
67| `insert-final-newline` | Whether to automatically insert a trailing line-ending on write if missing | `true` |
67| `insert-final-newline` | Whether to automatically insert a trailing line-ending on write if missing | `true` |
68+| `popup-border` | Draw border around `popup`, `menu`, `all`, or `none` | `none` |
69+| `indent-heuristic` | How the indentation for a newly inserted line is computed: `simple` just copies the indentation level from the previous line, `tree-sitter` computes the indentation based on the syntax tree and `hybrid` combines both approaches. If the chosen heuristic is not available, a different one will be used as a fallback (the fallback order being `hybrid` -> `tree-sitter` -> `simple`). | `hybrid`
68
70
69### `[editor.statusline]` Section
71### `[editor.statusline]` Section
70
72
@@ -176,6 +178,21 @@ All git related options are only enabled in a git repository.
176|`git-exclude` | Enables reading `.git/info/exclude` files | `true`
178|`git-exclude` | Enables reading `.git/info/exclude` files | `true`
177|`max-depth` | Set with an integer value for maximum depth to recurse | Defaults to `None`.
179|`max-depth` | Set with an integer value for maximum depth to recurse | Defaults to `None`.
178
180
181+Ignore files can be placed locally as `.ignore` or put in your home directory as `~/.ignore`. They support the usual ignore and negative ignore (unignore) rules used in `.gitignore` files.
182+
183+Additionally, you can use Helix-specific ignore files by creating a local `.helix/ignore` file in the current workspace or a global `ignore` file located in your Helix config directory:
184+- Linux and Mac: `~/.config/helix/ignore`
185+- Windows: `%AppData%\helix\ignore`
186+
187+Example:
188+
189+```ini
190+# unignore in file picker and global search
191+!.github/
192+!.gitignore
193+!.gitattributes
194+```
195+
179### `[editor.auto-pairs]` Section
196### `[editor.auto-pairs]` Section
180
197
181Enables automatic insertion of pairs to parentheses, brackets, etc. Can be a
198Enables automatic insertion of pairs to parentheses, brackets, etc. Can be a
book/src/generated/lang-support.md+4−1
@@ -1,5 +1,6 @@
1| Language | Syntax Highlighting | Treesitter Textobjects | Auto Indent | Default LSP |
1| Language | Syntax Highlighting | Treesitter Textobjects | Auto Indent | Default LSP |
2| --- | --- | --- | --- | --- |
2| --- | --- | --- | --- | --- |
3+| agda | ✓ | | | |
3| astro | ✓ | | | |
4| astro | ✓ | | | |
4| awk | ✓ | ✓ | | `awk-language-server` |
5| awk | ✓ | ✓ | | `awk-language-server` |
5| bash | ✓ | ✓ | ✓ | `bash-language-server` |
6| bash | ✓ | ✓ | ✓ | `bash-language-server` |
@@ -10,7 +11,7 @@
10| blueprint | ✓ | | | `blueprint-compiler` |
11| blueprint | ✓ | | | `blueprint-compiler` |
11| c | ✓ | ✓ | ✓ | `clangd` |
12| c | ✓ | ✓ | ✓ | `clangd` |
12| c-sharp | ✓ | ✓ | | `OmniSharp` |
13| c-sharp | ✓ | ✓ | | `OmniSharp` |
13−| cabal | | | | |
14+| cabal | | | | `haskell-language-server-wrapper` |
14| cairo | ✓ | ✓ | ✓ | `cairo-language-server` |
15| cairo | ✓ | ✓ | ✓ | `cairo-language-server` |
15| capnp | ✓ | | ✓ | |
16| capnp | ✓ | | ✓ | |
16| clojure | ✓ | | | `clojure-lsp` |
17| clojure | ✓ | | | `clojure-lsp` |
@@ -73,6 +74,7 @@
73| idris | | | | `idris2-lsp` |
74| idris | | | | `idris2-lsp` |
74| iex | ✓ | | | |
75| iex | ✓ | | | |
75| ini | ✓ | | | |
76| ini | ✓ | | | |
77+| janet | ✓ | | | |
76| java | ✓ | ✓ | ✓ | `jdtls` |
78| java | ✓ | ✓ | ✓ | `jdtls` |
77| javascript | ✓ | ✓ | ✓ | `typescript-language-server` |
79| javascript | ✓ | ✓ | ✓ | `typescript-language-server` |
78| jinja | ✓ | | | |
80| jinja | ✓ | | | |
@@ -146,6 +148,7 @@
146| scheme | ✓ | | ✓ | |
148| scheme | ✓ | | ✓ | |
147| scss | ✓ | | | `vscode-css-language-server` |
149| scss | ✓ | | | `vscode-css-language-server` |
148| slint | ✓ | | ✓ | `slint-lsp` |
150| slint | ✓ | | ✓ | `slint-lsp` |
151+| smali | ✓ | | ✓ | |
149| smithy | ✓ | | | `cs` |
152| smithy | ✓ | | | `cs` |
150| sml | ✓ | | | |
153| sml | ✓ | | | |
151| solidity | ✓ | | | `solc` |
154| solidity | ✓ | | | `solc` |
book/src/generated/typable-cmd.md+1−1
@@ -17,7 +17,7 @@
17| `:write-buffer-close!`, `:wbc!` | Force write changes to disk creating necessary subdirectories and closes the buffer. Accepts an optional path (:write-buffer-close! some/path.txt) |
17| `:write-buffer-close!`, `:wbc!` | Force write changes to disk creating necessary subdirectories and closes the buffer. Accepts an optional path (:write-buffer-close! some/path.txt) |
18| `:new`, `:n` | Create a new scratch buffer. |
18| `:new`, `:n` | Create a new scratch buffer. |
19| `:format`, `:fmt` | Format the file using the LSP formatter. |
19| `:format`, `:fmt` | Format the file using the LSP formatter. |
20−| `:indent-style` | Set the indentation style for editing. ('t' for tabs or 1-8 for number of spaces.) |
20+| `:indent-style` | Set the indentation style for editing. ('t' for tabs or 1-16 for number of spaces.) |
21| `:line-ending` | Set the document's default line ending. Options: crlf, lf. |
21| `:line-ending` | Set the document's default line ending. Options: crlf, lf. |
22| `:earlier`, `:ear` | Jump back to an earlier point in edit history. Accepts a number of steps or a time span. |
22| `:earlier`, `:ear` | Jump back to an earlier point in edit history. Accepts a number of steps or a time span. |
23| `:later`, `:lat` | Jump to a later point in edit history. Accepts a number of steps or a time span. |
23| `:later`, `:lat` | Jump to a later point in edit history. Accepts a number of steps or a time span. |
book/src/guides/adding_languages.md+2
@@ -36,6 +36,7 @@ below.
363. Refer to the
363. Refer to the
37 [tree-sitter website](https://tree-sitter.github.io/tree-sitter/syntax-highlighting#queries)
37 [tree-sitter website](https://tree-sitter.github.io/tree-sitter/syntax-highlighting#queries)
38 for more information on writing queries.
38 for more information on writing queries.
39+4. A list of highlight captures can be found [on the themes page](https://docs.helix-editor.com/themes.html#scopes).
39
40
40> 💡 In Helix, the first matching query takes precedence when evaluating
41> 💡 In Helix, the first matching query takes precedence when evaluating
41> queries, which is different from other editors such as Neovim where the last
42> queries, which is different from other editors such as Neovim where the last
@@ -51,3 +52,4 @@ below.
51 grammars.
52 grammars.
52- If a parser is causing a segfault, or you want to remove it, make sure to
53- If a parser is causing a segfault, or you want to remove it, make sure to
53 remove the compiled parser located at `runtime/grammars/<name>.so`.
54 remove the compiled parser located at `runtime/grammars/<name>.so`.
55+- If you are attempting to add queries and Helix is unable to locate them, ensure that the environment variable `HELIX_RUNTIME` is set to the location of the `runtime` folder you're developing in.
book/src/guides/indent.md+9
@@ -12,6 +12,15 @@ Note that it matters where these added indents begin. For example,
12multiple indent level increases that start on the same line only increase
12multiple indent level increases that start on the same line only increase
13the total indent level by 1. See [Capture types](#capture-types).
13the total indent level by 1. See [Capture types](#capture-types).
14
14
15+By default, Helix uses the `hybrid` indentation heuristic. This means that
16+indent queries are not used to compute the expected absolute indentation of a
17+line but rather the expected difference in indentation between the new and an
18+already existing line. This difference is then added to the actual indentation
19+of the already existing line. Since this makes errors in the indent queries
20+harder to find, it is recommended to disable it when testing via
21+`:set indent-heuristic tree-sitter`. The rest of this guide assumes that
22+the `tree-sitter` heuristic is used.
23+
15## Indent queries
24## Indent queries
16
25
17When Helix is inserting a new line through `o`, `O`, or `<ret>`, to determine
26When Helix is inserting a new line through `o`, `O`, or `<ret>`, to determine
book/src/keymap.md+1−1
@@ -25,7 +25,7 @@
25
25
26## Normal mode
26## Normal mode
27
27
28−Normal mode is the default mode when you launch helix. Return to it from other modes by typing `Escape`.
28+Normal mode is the default mode when you launch helix. You can return to it from other modes by pressing the `Escape` key.
29
29
30### Movement
30### Movement
31
31
book/src/languages.md+1
@@ -69,6 +69,7 @@ These configuration keys are available:
69| `soft-wrap` | [editor.softwrap](./configuration.md#editorsoft-wrap-section)
69| `soft-wrap` | [editor.softwrap](./configuration.md#editorsoft-wrap-section)
70| `text-width` | Maximum line length. Used for the `:reflow` command and soft-wrapping if `soft-wrap.wrap-at-text-width` is set, defaults to `editor.text-width` |
70| `text-width` | Maximum line length. Used for the `:reflow` command and soft-wrapping if `soft-wrap.wrap-at-text-width` is set, defaults to `editor.text-width` |
71| `workspace-lsp-roots` | Directories relative to the workspace root that are treated as LSP roots. Should only be set in `.helix/config.toml`. Overwrites the setting of the same name in `config.toml` if set. |
71| `workspace-lsp-roots` | Directories relative to the workspace root that are treated as LSP roots. Should only be set in `.helix/config.toml`. Overwrites the setting of the same name in `config.toml` if set. |
72+| `persistent-diagnostic-sources` | An array of LSP diagnostic sources assumed unchanged when the language server resends the same set of diagnostics. Helix can track the position for these diagnostics internally instead. Useful for diagnostics that are recomputed on save.
72
73
73### File-type detection and the `file-types` key
74### File-type detection and the `file-types` key
74
75
helix-core/Cargo.toml+1−1
@@ -27,7 +27,7 @@ unicode-general-category = "0.6"
27# slab = "0.4.2"
27# slab = "0.4.2"
28slotmap = "1.0"
28slotmap = "1.0"
29tree-sitter.workspace = true
29tree-sitter.workspace = true
30−once_cell = "1.18"
30+once_cell = "1.19"
31arc-swap = "1"
31arc-swap = "1"
32regex = "1"
32regex = "1"
33bitflags = "2.4"
33bitflags = "2.4"
helix-core/src/diagnostic.rs+4
@@ -39,6 +39,10 @@ pub enum DiagnosticTag {
39#[derive(Debug, Clone)]
39#[derive(Debug, Clone)]
40pub struct Diagnostic {
40pub struct Diagnostic {
41 pub range: Range,
41 pub range: Range,
42+ // whether this diagnostic ends at the end of(or inside) a word
43+ pub ends_at_word: bool,
44+ pub starts_at_word: bool,
45+ pub zero_width: bool,
42 pub line: usize,
46 pub line: usize,
43 pub message: String,
47 pub message: String,
44 pub severity: Option<Severity>,
48 pub severity: Option<Severity>,
helix-core/src/indent.rs+310−101
@@ -4,10 +4,11 @@ use tree_sitter::{Query, QueryCursor, QueryPredicateArg};
4
4
5use crate::{
5use crate::{
6 chars::{char_is_line_ending, char_is_whitespace},
6 chars::{char_is_line_ending, char_is_whitespace},
7+ find_first_non_whitespace_char,
7 graphemes::{grapheme_width, tab_width_at},
8 graphemes::{grapheme_width, tab_width_at},
8− syntax::{LanguageConfiguration, RopeProvider, Syntax},
9+ syntax::{IndentationHeuristic, LanguageConfiguration, RopeProvider, Syntax},
9 tree_sitter::Node,
10 tree_sitter::Node,
10− Rope, RopeGraphemes, RopeSlice,
11+ Position, Rope, RopeGraphemes, RopeSlice,
11};
12};
12
13
13/// Enum representing indentation style.
14/// Enum representing indentation style.
@@ -21,7 +22,7 @@ pub enum IndentStyle {
21
22
22// 16 spaces
23// 16 spaces
23const INDENTS: &str = " ";
24const INDENTS: &str = " ";
24−const MAX_INDENT: u8 = 16;
25+pub const MAX_INDENT: u8 = 16;
25
26
26impl IndentStyle {
27impl IndentStyle {
27 /// Creates an `IndentStyle` from an indentation string.
28 /// Creates an `IndentStyle` from an indentation string.
@@ -196,6 +197,56 @@ pub fn indent_level_for_line(line: RopeSlice, tab_width: usize, indent_width: us
196 len / indent_width
197 len / indent_width
197}
198}
198
199
200+/// Create a string of tabs & spaces that has the same visual width as the given RopeSlice (independent of the tab width).
201+fn whitespace_with_same_width(text: RopeSlice) -> String {
202+ let mut s = String::new();
203+ for grapheme in RopeGraphemes::new(text) {
204+ if grapheme == "\t" {
205+ s.push('\t');
206+ } else {
207+ s.extend(std::iter::repeat(' ').take(grapheme_width(&Cow::from(grapheme))));
208+ }
209+ }
210+ s
211+}
212+
213+fn add_indent_level(
214+ mut base_indent: String,
215+ added_indent_level: isize,
216+ indent_style: &IndentStyle,
217+ tab_width: usize,
218+) -> String {
219+ if added_indent_level >= 0 {
220+ // Adding a non-negative indent is easy, we can simply append the indent string
221+ base_indent.push_str(&indent_style.as_str().repeat(added_indent_level as usize));
222+ base_indent
223+ } else {
224+ // In this case, we want to return a prefix of `base_indent`.
225+ // Since the width of a tab depends on its offset, we cannot simply iterate over
226+ // the chars of `base_indent` in reverse until we have the desired indent reduction,
227+ // instead we iterate over them twice in forward direction.
228+ let base_indent_rope = RopeSlice::from(base_indent.as_str());
229+ #[allow(deprecated)]
230+ let base_indent_width =
231+ crate::visual_coords_at_pos(base_indent_rope, base_indent_rope.len_chars(), tab_width)
232+ .col;
233+ let target_indent_width = base_indent_width
234+ .saturating_sub((-added_indent_level) as usize * indent_style.indent_width(tab_width));
235+ #[allow(deprecated)]
236+ let char_end_idx = crate::pos_at_visual_coords(
237+ base_indent_rope,
238+ Position {
239+ row: 0,
240+ col: target_indent_width,
241+ },
242+ tab_width,
243+ );
244+ let byte_end_idx = base_indent_rope.char_to_byte(char_end_idx);
245+ base_indent.truncate(byte_end_idx);
246+ base_indent
247+ }
248+}
249+
199/// Computes for node and all ancestors whether they are the first node on their line.
250/// Computes for node and all ancestors whether they are the first node on their line.
200/// The first entry in the return value represents the root node, the last one the node itself
251/// The first entry in the return value represents the root node, the last one the node itself
201fn get_first_in_line(mut node: Node, new_line_byte_pos: Option<usize>) -> Vec<bool> {
252fn get_first_in_line(mut node: Node, new_line_byte_pos: Option<usize>) -> Vec<bool> {
@@ -241,21 +292,21 @@ fn get_first_in_line(mut node: Node, new_line_byte_pos: Option<usize>) -> Vec<bo
241/// - max(0, indent - outdent) tabs, if tabs are used for indentation
292/// - max(0, indent - outdent) tabs, if tabs are used for indentation
242/// - max(0, indent - outdent)*indent_width spaces, if spaces are used for indentation
293/// - max(0, indent - outdent)*indent_width spaces, if spaces are used for indentation
243#[derive(Default, Debug, PartialEq, Eq, Clone)]
294#[derive(Default, Debug, PartialEq, Eq, Clone)]
244−pub struct Indentation {
295+pub struct Indentation<'a> {
245 indent: usize,
296 indent: usize,
246 indent_always: usize,
297 indent_always: usize,
247 outdent: usize,
298 outdent: usize,
248 outdent_always: usize,
299 outdent_always: usize,
249 /// The alignment, as a string containing only tabs & spaces. Storing this as a string instead of e.g.
300 /// The alignment, as a string containing only tabs & spaces. Storing this as a string instead of e.g.
250 /// the (visual) width ensures that the alignment is preserved even if the tab width changes.
301 /// the (visual) width ensures that the alignment is preserved even if the tab width changes.
251− align: Option<String>,
302+ align: Option<RopeSlice<'a>>,
252}
303}
253
304
254−impl Indentation {
305+impl<'a> Indentation<'a> {
255 /// Add some other [Indentation] to this.
306 /// Add some other [Indentation] to this.
256 /// The added indent should be the total added indent from one line.
307 /// The added indent should be the total added indent from one line.
257 /// Indent should always be added starting from the bottom (or equivalently, the innermost tree-sitter node).
308 /// Indent should always be added starting from the bottom (or equivalently, the innermost tree-sitter node).
258− fn add_line(&mut self, added: Indentation) {
309+ fn add_line(&mut self, added: Indentation<'a>) {
259 // Align overrides the indent from outer scopes.
310 // Align overrides the indent from outer scopes.
260 if self.align.is_some() {
311 if self.align.is_some() {
261 return;
312 return;
@@ -274,7 +325,7 @@ impl Indentation {
274 /// Only captures that apply to the same line should be added together in this way (otherwise use `add_line`)
325 /// Only captures that apply to the same line should be added together in this way (otherwise use `add_line`)
275 /// and the captures should be added starting from the innermost tree-sitter node (currently this only matters
326 /// and the captures should be added starting from the innermost tree-sitter node (currently this only matters
276 /// if multiple `@align` patterns occur on the same line).
327 /// if multiple `@align` patterns occur on the same line).
277− fn add_capture(&mut self, added: IndentCaptureType) {
328+ fn add_capture(&mut self, added: IndentCaptureType<'a>) {
278 match added {
329 match added {
279 IndentCaptureType::Indent => {
330 IndentCaptureType::Indent => {
280 if self.indent_always == 0 {
331 if self.indent_always == 0 {
@@ -303,43 +354,62 @@ impl Indentation {
303 }
354 }
304 }
355 }
305 }
356 }
306− fn into_string(self, indent_style: &IndentStyle) -> String {
357+ fn net_indent(&self) -> isize {
307− let indent = self.indent_always + self.indent;
358+ (self.indent + self.indent_always) as isize
308− let outdent = self.outdent_always + self.outdent;
359+ - ((self.outdent + self.outdent_always) as isize)
309−
360+ }
310− let indent_level = if indent >= outdent {
361+ /// Convert `self` into a string, taking into account the computed and actual indentation of some other line.
311− indent - outdent
362+ fn relative_indent(
312− } else {
363+ &self,
313− log::warn!("Encountered more outdent than indent nodes while calculating indentation: {} outdent, {} indent", self.outdent, self.indent);
364+ other_computed_indent: &Self,
314− 0
365+ other_leading_whitespace: RopeSlice,
315− };
366+ indent_style: &IndentStyle,
316− let mut indent_string = if let Some(align) = self.align {
367+ tab_width: usize,
317− align
368+ ) -> Option<String> {
369+ if self.align == other_computed_indent.align {
370+ // If self and baseline are either not aligned to anything or both aligned the same way,
371+ // we can simply take `other_leading_whitespace` and add some indent / outdent to it (in the second
372+ // case, the alignment should already be accounted for in `other_leading_whitespace`).
373+ let indent_diff = self.net_indent() - other_computed_indent.net_indent();
374+ Some(add_indent_level(
375+ String::from(other_leading_whitespace),
376+ indent_diff,
377+ indent_style,
378+ tab_width,
379+ ))
318 } else {
380 } else {
319− String::new()
381+ // If the alignment of both lines is different, we cannot compare their indentation in any meaningful way
320− };
382+ None
321− indent_string.push_str(&indent_style.as_str().repeat(indent_level));
383+ }
322− indent_string
384+ }
385+ pub fn to_string(&self, indent_style: &IndentStyle, tab_width: usize) -> String {
386+ add_indent_level(
387+ self.align
388+ .map_or_else(String::new, whitespace_with_same_width),
389+ self.net_indent(),
390+ indent_style,
391+ tab_width,
392+ )
323 }
393 }
324}
394}
325
395
326/// An indent definition which corresponds to a capture from the indent query
396/// An indent definition which corresponds to a capture from the indent query
327#[derive(Debug)]
397#[derive(Debug)]
328−struct IndentCapture {
398+struct IndentCapture<'a> {
329− capture_type: IndentCaptureType,
399+ capture_type: IndentCaptureType<'a>,
330 scope: IndentScope,
400 scope: IndentScope,
331}
401}
332#[derive(Debug, Clone, PartialEq)]
402#[derive(Debug, Clone, PartialEq)]
333−enum IndentCaptureType {
403+enum IndentCaptureType<'a> {
334 Indent,
404 Indent,
335 IndentAlways,
405 IndentAlways,
336 Outdent,
406 Outdent,
337 OutdentAlways,
407 OutdentAlways,
338 /// Alignment given as a string of whitespace
408 /// Alignment given as a string of whitespace
339− Align(String),
409+ Align(RopeSlice<'a>),
340}
410}
341
411
342−impl IndentCaptureType {
412+impl<'a> IndentCaptureType<'a> {
343 fn default_scope(&self) -> IndentScope {
413 fn default_scope(&self) -> IndentScope {
344 match self {
414 match self {
345 IndentCaptureType::Indent | IndentCaptureType::IndentAlways => IndentScope::Tail,
415 IndentCaptureType::Indent | IndentCaptureType::IndentAlways => IndentScope::Tail,
@@ -371,8 +441,8 @@ enum ExtendCapture {
371/// each node (identified by its ID) the relevant captures (already filtered
441/// each node (identified by its ID) the relevant captures (already filtered
372/// by predicates).
442/// by predicates).
373#[derive(Debug)]
443#[derive(Debug)]
374−struct IndentQueryResult {
444+struct IndentQueryResult<'a> {
375− indent_captures: HashMap<usize, Vec<IndentCapture>>,
445+ indent_captures: HashMap<usize, Vec<IndentCapture<'a>>>,
376 extend_captures: HashMap<usize, Vec<ExtendCapture>>,
446 extend_captures: HashMap<usize, Vec<ExtendCapture>>,
377}
447}
378
448
@@ -393,14 +463,14 @@ fn get_node_end_line(node: Node, new_line_byte_pos: Option<usize>) -> usize {
393 node_line
463 node_line
394}
464}
395
465
396−fn query_indents(
466+fn query_indents<'a>(
397 query: &Query,
467 query: &Query,
398 syntax: &Syntax,
468 syntax: &Syntax,
399 cursor: &mut QueryCursor,
469 cursor: &mut QueryCursor,
400− text: RopeSlice,
470+ text: RopeSlice<'a>,
401 range: std::ops::Range<usize>,
471 range: std::ops::Range<usize>,
402 new_line_byte_pos: Option<usize>,
472 new_line_byte_pos: Option<usize>,
403−) -> IndentQueryResult {
473+) -> IndentQueryResult<'a> {
404 let mut indent_captures: HashMap<usize, Vec<IndentCapture>> = HashMap::new();
474 let mut indent_captures: HashMap<usize, Vec<IndentCapture>> = HashMap::new();
405 let mut extend_captures: HashMap<usize, Vec<ExtendCapture>> = HashMap::new();
475 let mut extend_captures: HashMap<usize, Vec<ExtendCapture>> = HashMap::new();
406 cursor.set_byte_range(range);
476 cursor.set_byte_range(range);
@@ -488,7 +558,7 @@ fn query_indents(
488 "outdent" => IndentCaptureType::Outdent,
558 "outdent" => IndentCaptureType::Outdent,
489 "outdent.always" => IndentCaptureType::OutdentAlways,
559 "outdent.always" => IndentCaptureType::OutdentAlways,
490 // The alignment will be updated to the correct value at the end, when the anchor is known.
560 // The alignment will be updated to the correct value at the end, when the anchor is known.
491− "align" => IndentCaptureType::Align(String::from("")),
561+ "align" => IndentCaptureType::Align(RopeSlice::from("")),
492 "anchor" => {
562 "anchor" => {
493 if anchor.is_some() {
563 if anchor.is_some() {
494 log::error!("Invalid indent query: Encountered more than one @anchor in the same match.")
564 log::error!("Invalid indent query: Encountered more than one @anchor in the same match.")
@@ -560,22 +630,10 @@ fn query_indents(
560 }
630 }
561 Some(anchor) => anchor,
631 Some(anchor) => anchor,
562 };
632 };
563− // Create a string of tabs & spaces that should have the same width
633+ capture.capture_type = IndentCaptureType::Align(
564− // as the string that precedes the anchor (independent of the tab width).
565− let mut align = String::new();
566− for grapheme in RopeGraphemes::new(
567 text.line(anchor.start_position().row)
634 text.line(anchor.start_position().row)
568 .byte_slice(0..anchor.start_position().column),
635 .byte_slice(0..anchor.start_position().column),
569− ) {
636+ );
570− if grapheme == "\t" {
571− align.push('\t');
572− } else {
573− align.extend(
574− std::iter::repeat(' ').take(grapheme_width(&Cow::from(grapheme))),
575− );
576− }
577− }
578− capture.capture_type = IndentCaptureType::Align(align);
579 }
637 }
580 indent_captures
638 indent_captures
581 .entry(node_id)
639 .entry(node_id)
@@ -661,56 +719,20 @@ fn extend_nodes<'a>(
661 }
719 }
662}
720}
663
721
664−/// Use the syntax tree to determine the indentation for a given position.
722+/// Prepare an indent query by computing:
665−/// This can be used in 2 ways:
723+/// - The node from which to start the query (this is non-trivial due to `@extend` captures)
666−///
724+/// - The indent captures for all relevant nodes.
667−/// - To get the correct indentation for an existing line (new_line=false), not necessarily equal to the current indentation.
668−/// - In this case, pos should be inside the first tree-sitter node on that line.
669−/// In most cases, this can just be the first non-whitespace on that line.
670−/// - To get the indentation for a new line (new_line=true). This behaves like the first usecase if the part of the current line
671−/// after pos were moved to a new line.
672−///
673−/// The indentation is determined by traversing all the tree-sitter nodes containing the position.
674−/// Each of these nodes produces some [Indentation] for:
675−///
676−/// - The line of the (beginning of the) node. This is defined by the scope `all` if this is the first node on its line.
677−/// - The line after the node. This is defined by:
678−/// - The scope `tail`.
679−/// - The scope `all` if this node is not the first node on its line.
680−/// Intuitively, `all` applies to everything contained in this node while `tail` applies to everything except for the first line of the node.
681−/// The indents from different nodes for the same line are then combined.
682−/// The result [Indentation] is simply the sum of the [Indentation] for all lines.
683−///
684−/// Specifying which line exactly an [Indentation] applies to is important because indents on the same line combine differently than indents on different lines:
685−/// ```ignore
686−/// some_function(|| {
687−/// // Both the function parameters as well as the contained block should be indented.
688−/// // Because they are on the same line, this only yields one indent level
689−/// });
690−/// ```
691−///
692−/// ```ignore
693−/// some_function(
694−/// param1,
695−/// || {
696−/// // Here we get 2 indent levels because the 'parameters' and the 'block' node begin on different lines
697−/// },
698−/// );
699−/// ```
700#[allow(clippy::too_many_arguments)]
725#[allow(clippy::too_many_arguments)]
701−pub fn treesitter_indent_for_pos(
726+fn init_indent_query<'a, 'b>(
702 query: &Query,
727 query: &Query,
703− syntax: &Syntax,
728+ syntax: &'a Syntax,
704− indent_style: &IndentStyle,
729+ text: RopeSlice<'b>,
705 tab_width: usize,
730 tab_width: usize,
706 indent_width: usize,
731 indent_width: usize,
707− text: RopeSlice,
708 line: usize,
732 line: usize,
709− pos: usize,
733+ byte_pos: usize,
710− new_line: bool,
734+ new_line_byte_pos: Option<usize>,
711−) -> Option<String> {
735+) -> Option<(Node<'a>, HashMap<usize, Vec<IndentCapture<'b>>>)> {
712− let byte_pos = text.char_to_byte(pos);
713− let new_line_byte_pos = new_line.then_some(byte_pos);
714 // The innermost tree-sitter node which is considered for the indent
736 // The innermost tree-sitter node which is considered for the indent
715 // computation. It may change if some predeceding node is extended
737 // computation. It may change if some predeceding node is extended
716 let mut node = syntax
738 let mut node = syntax
@@ -754,7 +776,6 @@ pub fn treesitter_indent_for_pos(
754 (query_result, deepest_preceding)
776 (query_result, deepest_preceding)
755 })
777 })
756 };
778 };
757− let mut indent_captures = query_result.indent_captures;
758 let extend_captures = query_result.extend_captures;
779 let extend_captures = query_result.extend_captures;
759
780
760 // Check for extend captures, potentially changing the node that the indent calculation starts with
781 // Check for extend captures, potentially changing the node that the indent calculation starts with
@@ -769,6 +790,68 @@ pub fn treesitter_indent_for_pos(
769 indent_width,
790 indent_width,
770 );
791 );
771 }
792 }
793+ Some((node, query_result.indent_captures))
794+}
795+
796+/// Use the syntax tree to determine the indentation for a given position.
797+/// This can be used in 2 ways:
798+///
799+/// - To get the correct indentation for an existing line (new_line=false), not necessarily equal to the current indentation.
800+/// - In this case, pos should be inside the first tree-sitter node on that line.
801+/// In most cases, this can just be the first non-whitespace on that line.
802+/// - To get the indentation for a new line (new_line=true). This behaves like the first usecase if the part of the current line
803+/// after pos were moved to a new line.
804+///
805+/// The indentation is determined by traversing all the tree-sitter nodes containing the position.
806+/// Each of these nodes produces some [Indentation] for:
807+///
808+/// - The line of the (beginning of the) node. This is defined by the scope `all` if this is the first node on its line.
809+/// - The line after the node. This is defined by:
810+/// - The scope `tail`.
811+/// - The scope `all` if this node is not the first node on its line.
812+/// Intuitively, `all` applies to everything contained in this node while `tail` applies to everything except for the first line of the node.
813+/// The indents from different nodes for the same line are then combined.
814+/// The result [Indentation] is simply the sum of the [Indentation] for all lines.
815+///
816+/// Specifying which line exactly an [Indentation] applies to is important because indents on the same line combine differently than indents on different lines:
817+/// ```ignore
818+/// some_function(|| {
819+/// // Both the function parameters as well as the contained block should be indented.
820+/// // Because they are on the same line, this only yields one indent level
821+/// });
822+/// ```
823+///
824+/// ```ignore
825+/// some_function(
826+/// param1,
827+/// || {
828+/// // Here we get 2 indent levels because the 'parameters' and the 'block' node begin on different lines
829+/// },
830+/// );
831+/// ```
832+#[allow(clippy::too_many_arguments)]
833+pub fn treesitter_indent_for_pos<'a>(
834+ query: &Query,
835+ syntax: &Syntax,
836+ tab_width: usize,
837+ indent_width: usize,
838+ text: RopeSlice<'a>,
839+ line: usize,
840+ pos: usize,
841+ new_line: bool,
842+) -> Option<Indentation<'a>> {
843+ let byte_pos = text.char_to_byte(pos);
844+ let new_line_byte_pos = new_line.then_some(byte_pos);
845+ let (mut node, mut indent_captures) = init_indent_query(
846+ query,
847+ syntax,
848+ text,
849+ tab_width,
850+ indent_width,
851+ line,
852+ byte_pos,
853+ new_line_byte_pos,
854+ )?;
772 let mut first_in_line = get_first_in_line(node, new_line.then_some(byte_pos));
855 let mut first_in_line = get_first_in_line(node, new_line.then_some(byte_pos));
773
856
774 let mut result = Indentation::default();
857 let mut result = Indentation::default();
@@ -836,7 +919,7 @@ pub fn treesitter_indent_for_pos(
836 break;
919 break;
837 }
920 }
838 }
921 }
839− Some(result.into_string(indent_style))
922+ Some(result)
840}
923}
841
924
842/// Returns the indentation for a new line.
925/// Returns the indentation for a new line.
@@ -845,6 +928,7 @@ pub fn treesitter_indent_for_pos(
845pub fn indent_for_newline(
928pub fn indent_for_newline(
846 language_config: Option<&LanguageConfiguration>,
929 language_config: Option<&LanguageConfiguration>,
847 syntax: Option<&Syntax>,
930 syntax: Option<&Syntax>,
931+ indent_heuristic: &IndentationHeuristic,
848 indent_style: &IndentStyle,
932 indent_style: &IndentStyle,
849 tab_width: usize,
933 tab_width: usize,
850 text: RopeSlice,
934 text: RopeSlice,
@@ -853,14 +937,18 @@ pub fn indent_for_newline(
853 current_line: usize,
937 current_line: usize,
854) -> String {
938) -> String {
855 let indent_width = indent_style.indent_width(tab_width);
939 let indent_width = indent_style.indent_width(tab_width);
940+ if let (
941+ IndentationHeuristic::TreeSitter | IndentationHeuristic::Hybrid,
942+ Some(query),
856− if let (Some(query), Some(syntax)) = (
943+ Some(syntax),
944+ ) = (
945+ indent_heuristic,
857 language_config.and_then(|config| config.indent_query()),
946 language_config.and_then(|config| config.indent_query()),
858 syntax,
947 syntax,
859 ) {
948 ) {
860 if let Some(indent) = treesitter_indent_for_pos(
949 if let Some(indent) = treesitter_indent_for_pos(
861 query,
950 query,
862 syntax,
951 syntax,
863− indent_style,
864 tab_width,
952 tab_width,
865 indent_width,
953 indent_width,
866 text,
954 text,
@@ -868,9 +956,57 @@ pub fn indent_for_newline(
868 line_before_end_pos,
956 line_before_end_pos,
869 true,
957 true,
870 ) {
958 ) {
959+ if *indent_heuristic == IndentationHeuristic::Hybrid {
960+ // We want to compute the indentation not only based on the
961+ // syntax tree but also on the actual indentation of a previous
962+ // line. This makes indentation computation more resilient to
963+ // incomplete queries, incomplete source code & differing indentation
964+ // styles for the same language.
965+ // However, using the indent of a previous line as a baseline may not
966+ // make sense, e.g. if it has a different alignment than the new line.
967+ // In order to prevent edge cases with long running times, we only try
968+ // a constant number of (non-empty) lines.
969+ const MAX_ATTEMPTS: usize = 4;
970+ let mut num_attempts = 0;
971+ for line_idx in (0..=line_before).rev() {
972+ let line = text.line(line_idx);
973+ let first_non_whitespace_char = match find_first_non_whitespace_char(line) {
974+ Some(i) => i,
975+ None => {
976+ continue;
977+ }
978+ };
979+ if let Some(indent) = (|| {
980+ let computed_indent = treesitter_indent_for_pos(
981+ query,
982+ syntax,
983+ tab_width,
984+ indent_width,
985+ text,
986+ line_idx,
987+ text.line_to_char(line_idx) + first_non_whitespace_char,
988+ false,
989+ )?;
990+ let leading_whitespace = line.slice(0..first_non_whitespace_char);
991+ indent.relative_indent(
992+ &computed_indent,
993+ leading_whitespace,
994+ indent_style,
995+ tab_width,
996+ )
997+ })() {
871− return indent;
998+ return indent;
999+ }
1000+ num_attempts += 1;
1001+ if num_attempts == MAX_ATTEMPTS {
1002+ break;
1003+ }
1004+ }
1005+ }
1006+ return indent.to_string(indent_style, tab_width);
872 };
1007 };
873 }
1008 }
1009+ // Fallback in case we either don't have indent queries or they failed for some reason
874 let indent_level = indent_level_for_line(text.line(current_line), tab_width, indent_width);
1010 let indent_level = indent_level_for_line(text.line(current_line), tab_width, indent_width);
875 indent_style.as_str().repeat(indent_level)
1011 indent_style.as_str().repeat(indent_level)
876}
1012}
@@ -962,10 +1098,13 @@ mod test {
962 ..Default::default()
1098 ..Default::default()
963 };
1099 };
964
1100
1101+ fn add_capture<'a>(
965− let add_capture = |mut indent: Indentation, capture| {
1102+ mut indent: Indentation<'a>,
1103+ capture: IndentCaptureType<'a>,
1104+ ) -> Indentation<'a> {
966 indent.add_capture(capture);
1105 indent.add_capture(capture);
967 indent
1106 indent
968− };
1107+ }
969
1108
970 // adding an indent to no indent makes an indent
1109 // adding an indent to no indent makes an indent
971 assert_eq!(
1110 assert_eq!(
@@ -1060,4 +1199,74 @@ mod test {
1060 )
1199 )
1061 );
1200 );
1062 }
1201 }
1202+
1203+ #[test]
1204+ fn test_relative_indent() {
1205+ let indent_style = IndentStyle::Spaces(4);
1206+ let tab_width: usize = 4;
1207+ let no_align = [
1208+ Indentation::default(),
1209+ Indentation {
1210+ indent: 1,
1211+ ..Default::default()
1212+ },
1213+ Indentation {
1214+ indent: 5,
1215+ outdent: 1,
1216+ ..Default::default()
1217+ },
1218+ ];
1219+ let align = no_align.clone().map(|indent| Indentation {
1220+ align: Some(RopeSlice::from("12345")),
1221+ ..indent
1222+ });
1223+ let different_align = Indentation {
1224+ align: Some(RopeSlice::from("123456")),
1225+ ..Default::default()
1226+ };
1227+
1228+ // Check that relative and absolute indentation computation are the same when the line we compare to is
1229+ // indented as we expect.
1230+ let check_consistency = |indent: &Indentation, other: &Indentation| {
1231+ assert_eq!(
1232+ indent.relative_indent(
1233+ other,
1234+ RopeSlice::from(other.to_string(&indent_style, tab_width).as_str()),
1235+ &indent_style,
1236+ tab_width
1237+ ),
1238+ Some(indent.to_string(&indent_style, tab_width))
1239+ );
1240+ };
1241+ for a in &no_align {
1242+ for b in &no_align {
1243+ check_consistency(a, b);
1244+ }
1245+ }
1246+ for a in &align {
1247+ for b in &align {
1248+ check_consistency(a, b);
1249+ }
1250+ }
1251+
1252+ // Relative indent computation makes no sense if the alignment differs
1253+ assert_eq!(
1254+ align[0].relative_indent(
1255+ &no_align[0],
1256+ RopeSlice::from(" "),
1257+ &indent_style,
1258+ tab_width
1259+ ),
1260+ None
1261+ );
1262+ assert_eq!(
1263+ align[0].relative_indent(
1264+ &different_align,
1265+ RopeSlice::from(" "),
1266+ &indent_style,
1267+ tab_width
1268+ ),
1269+ None
1270+ );
1271+ }
1063}
1272}
helix-core/src/syntax.rs+18
@@ -155,6 +155,8 @@ pub struct LanguageConfiguration {
155 /// Hardcoded LSP root directories relative to the workspace root, like `examples` or `tools/fuzz`.
155 /// Hardcoded LSP root directories relative to the workspace root, like `examples` or `tools/fuzz`.
156 /// Falling back to the current working directory if none are configured.
156 /// Falling back to the current working directory if none are configured.
157 pub workspace_lsp_roots: Option<Vec<PathBuf>>,
157 pub workspace_lsp_roots: Option<Vec<PathBuf>>,
158+ #[serde(default)]
159+ pub persistent_diagnostic_sources: Vec<String>,
158}
160}
159
161
160#[derive(Debug, PartialEq, Eq, Hash)]
162#[derive(Debug, PartialEq, Eq, Hash)]
@@ -442,6 +444,22 @@ pub struct IndentationConfiguration {
442 pub unit: String,
444 pub unit: String,
443}
445}
444
446
447+/// How the indentation for a newly inserted line should be determined.
448+/// If the selected heuristic is not available (e.g. because the current
449+/// language has no tree-sitter indent queries), a simpler one will be used.
450+#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
451+#[serde(rename_all = "kebab-case")]
452+pub enum IndentationHeuristic {
453+ /// Just copy the indentation of the line that the cursor is currently on.
454+ Simple,
455+ /// Use tree-sitter indent queries to compute the expected absolute indentation level of the new line.
456+ TreeSitter,
457+ /// Use tree-sitter indent queries to compute the expected difference in indentation between the new line
458+ /// and the line before. Add this to the actual indentation level of the line before.
459+ #[default]
460+ Hybrid,
461+}
462+
445/// Configuration for auto pairs
463/// Configuration for auto pairs
446#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
464#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
447#[serde(rename_all = "kebab-case", deny_unknown_fields, untagged)]
465#[serde(rename_all = "kebab-case", deny_unknown_fields, untagged)]
helix-core/src/transaction.rs+73−14
@@ -1,7 +1,7 @@
1use ropey::RopeSlice;
1use ropey::RopeSlice;
2use smallvec::SmallVec;
2use smallvec::SmallVec;
3
3
4−use crate::{Range, Rope, Selection, Tendril};
4+use crate::{chars::char_is_word, Range, Rope, Selection, Tendril};
5use std::{borrow::Cow, iter::once};
5use std::{borrow::Cow, iter::once};
6
6
7/// (from, to, replacement)
7/// (from, to, replacement)
@@ -23,6 +23,30 @@ pub enum Operation {
23pub enum Assoc {
23pub enum Assoc {
24 Before,
24 Before,
25 After,
25 After,
26+ /// Acts like `After` if a word character is inserted
27+ /// after the position, otherwise acts like `Before`
28+ AfterWord,
29+ /// Acts like `Before` if a word character is inserted
30+ /// before the position, otherwise acts like `After`
31+ BeforeWord,
32+}
33+
34+impl Assoc {
35+ /// Whether to stick to gaps
36+ fn stay_at_gaps(self) -> bool {
37+ !matches!(self, Self::BeforeWord | Self::AfterWord)
38+ }
39+
40+ fn insert_offset(self, s: &str) -> usize {
41+ let chars = s.chars().count();
42+ match self {
43+ Assoc::After => chars,
44+ Assoc::AfterWord => s.chars().take_while(|&c| char_is_word(c)).count(),
45+ // return position before inserted text
46+ Assoc::Before => 0,
47+ Assoc::BeforeWord => chars - s.chars().rev().take_while(|&c| char_is_word(c)).count(),
48+ }
49+ }
26}
50}
27
51
28#[derive(Debug, Default, Clone, PartialEq, Eq)]
52#[derive(Debug, Default, Clone, PartialEq, Eq)]
@@ -415,8 +439,6 @@ impl ChangeSet {
415 map!(|pos, _| (old_end > pos).then_some(new_pos), i);
439 map!(|pos, _| (old_end > pos).then_some(new_pos), i);
416 }
440 }
417 Insert(s) => {
441 Insert(s) => {
418− let ins = s.chars().count();
419−
420 // a subsequent delete means a replace, consume it
442 // a subsequent delete means a replace, consume it
421 if let Some((_, Delete(len))) = iter.peek() {
443 if let Some((_, Delete(len))) = iter.peek() {
422 iter.next();
444 iter.next();
@@ -424,13 +446,13 @@ impl ChangeSet {
424 old_end = old_pos + len;
446 old_end = old_pos + len;
425 // in range of replaced text
447 // in range of replaced text
426 map!(
448 map!(
427− |pos, assoc| (old_end > pos).then(|| {
449+ |pos, assoc: Assoc| (old_end > pos).then(|| {
428 // at point or tracking before
450 // at point or tracking before
429− if pos == old_pos || assoc == Assoc::Before {
451+ if pos == old_pos && assoc.stay_at_gaps() {
430 new_pos
452 new_pos
431 } else {
453 } else {
432 // place to end of insert
454 // place to end of insert
433− new_pos + ins
455+ new_pos + assoc.insert_offset(s)
434 }
456 }
435 }),
457 }),
436 i
458 i
@@ -438,20 +460,15 @@ impl ChangeSet {
438 } else {
460 } else {
439 // at insert point
461 // at insert point
440 map!(
462 map!(
441− |pos, assoc| (old_pos == pos).then(|| {
463+ |pos, assoc: Assoc| (old_pos == pos).then(|| {
442 // return position before inserted text
464 // return position before inserted text
443− if assoc == Assoc::Before {
465+ new_pos + assoc.insert_offset(s)
444− new_pos
445− } else {
446− // after text
447− new_pos + ins
448− }
449 }),
466 }),
450 i
467 i
451 );
468 );
452 }
469 }
453
470
454− new_pos += ins;
471+ new_pos += s.chars().count();
455 }
472 }
456 }
473 }
457 old_pos = old_end;
474 old_pos = old_end;
@@ -884,6 +901,48 @@ mod test {
884 let mut positions = [4, 2];
901 let mut positions = [4, 2];
885 cs.update_positions(positions.iter_mut().map(|pos| (pos, Assoc::After)));
902 cs.update_positions(positions.iter_mut().map(|pos| (pos, Assoc::After)));
886 assert_eq!(positions, [4, 2]);
903 assert_eq!(positions, [4, 2]);
904+ // stays at word boundary
905+ let cs = ChangeSet {
906+ changes: vec![
907+ Retain(2), // <space><space>
908+ Insert(" ab".into()),
909+ Retain(2), // cd
910+ Insert("de ".into()),
911+ ],
912+ len: 4,
913+ len_after: 10,
914+ };
915+ assert_eq!(cs.map_pos(2, Assoc::BeforeWord), 3);
916+ assert_eq!(cs.map_pos(4, Assoc::AfterWord), 9);
917+ let cs = ChangeSet {
918+ changes: vec![
919+ Retain(1), // <space>
920+ Insert(" b".into()),
921+ Delete(1), // c
922+ Retain(1), // d
923+ Insert("e ".into()),
924+ Delete(1), // <space>
925+ ],
926+ len: 5,
927+ len_after: 7,
928+ };
929+ assert_eq!(cs.map_pos(1, Assoc::BeforeWord), 2);
930+ assert_eq!(cs.map_pos(3, Assoc::AfterWord), 5);
931+ let cs = ChangeSet {
932+ changes: vec![
933+ Retain(1), // <space>
934+ Insert("a".into()),
935+ Delete(2), // <space>b
936+ Retain(1), // d
937+ Insert("e".into()),
938+ Delete(1), // f
939+ Retain(1), // <space>
940+ ],
941+ len: 5,
942+ len_after: 7,
943+ };
944+ assert_eq!(cs.map_pos(2, Assoc::BeforeWord), 1);
945+ assert_eq!(cs.map_pos(4, Assoc::AfterWord), 4);
887 }
946 }
888
947
889 #[test]
948 #[test]
helix-core/tests/indent.rs+2−2
@@ -210,7 +210,6 @@ fn test_treesitter_indent(
210 let suggested_indent = treesitter_indent_for_pos(
210 let suggested_indent = treesitter_indent_for_pos(
211 indent_query,
211 indent_query,
212 &syntax,
212 &syntax,
213− &indent_style,
214 tab_width,
213 tab_width,
215 indent_style.indent_width(tab_width),
214 indent_style.indent_width(tab_width),
216 text,
215 text,
@@ -218,7 +217,8 @@ fn test_treesitter_indent(
218 text.line_to_char(i) + pos,
217 text.line_to_char(i) + pos,
219 false,
218 false,
220 )
219 )
221− .unwrap();
220+ .unwrap()
221+ .to_string(&indent_style, tab_width);
222 assert!(
222 assert!(
223 line.get_slice(..pos).map_or(false, |s| s == suggested_indent),
223 line.get_slice(..pos).map_or(false, |s| s == suggested_indent),
224 "Wrong indentation for file {:?} on line {}:\n\"{}\" (original line)\n\"{}\" (suggested indentation)\n",
224 "Wrong indentation for file {:?} on line {}:\n\"{}\" (original line)\n\"{}\" (suggested indentation)\n",
helix-loader/Cargo.toml+1−1
@@ -20,7 +20,7 @@ serde = { version = "1.0", features = ["derive"] }
20toml = "0.7"
20toml = "0.7"
21etcetera = "0.8"
21etcetera = "0.8"
22tree-sitter.workspace = true
22tree-sitter.workspace = true
23−once_cell = "1.18"
23+once_cell = "1.19"
24log = "0.4"
24log = "0.4"
25which = "5.0.0"
25which = "5.0.0"
26
26
helix-lsp/Cargo.toml+1−1
@@ -22,7 +22,7 @@ futures-executor = "0.3"
22futures-util = { version = "0.3", features = ["std", "async-await"], default-features = false }
22futures-util = { version = "0.3", features = ["std", "async-await"], default-features = false }
23globset = "0.4.14"
23globset = "0.4.14"
24log = "0.4"
24log = "0.4"
25−lsp-types = { version = "0.94" }
25+lsp-types = { version = "0.95" }
26serde = { version = "1.0", features = ["derive"] }
26serde = { version = "1.0", features = ["derive"] }
27serde_json = "1.0"
27serde_json = "1.0"
28thiserror = "1.0"
28thiserror = "1.0"
helix-lsp/src/client.rs+8−8
@@ -667,6 +667,7 @@ impl Client {
667 version: Some(String::from(VERSION_AND_GIT_HASH)),
667 version: Some(String::from(VERSION_AND_GIT_HASH)),
668 }),
668 }),
669 locale: None, // TODO
669 locale: None, // TODO
670+ work_done_progress_params: lsp::WorkDoneProgressParams::default(),
670 };
671 };
671
672
672 self.request::<lsp::request::Initialize>(params).await
673 self.request::<lsp::request::Initialize>(params).await
@@ -969,20 +970,19 @@ impl Client {
969 ) -> Option<impl Future<Output = Result<()>>> {
970 ) -> Option<impl Future<Output = Result<()>>> {
970 let capabilities = self.capabilities.get().unwrap();
971 let capabilities = self.capabilities.get().unwrap();
971
972
972− let include_text = match &capabilities.text_document_sync {
973+ let include_text = match &capabilities.text_document_sync.as_ref()? {
973− Some(lsp::TextDocumentSyncCapability::Options(lsp::TextDocumentSyncOptions {
974+ lsp::TextDocumentSyncCapability::Options(lsp::TextDocumentSyncOptions {
974− save: Some(options),
975+ save: options,
975 ..
976 ..
976− })) => match options {
977+ }) => match options.as_ref()? {
977 lsp::TextDocumentSyncSaveOptions::Supported(true) => false,
978 lsp::TextDocumentSyncSaveOptions::Supported(true) => false,
978 lsp::TextDocumentSyncSaveOptions::SaveOptions(lsp_types::SaveOptions {
979 lsp::TextDocumentSyncSaveOptions::SaveOptions(lsp_types::SaveOptions {
979 include_text,
980 include_text,
980 }) => include_text.unwrap_or(false),
981 }) => include_text.unwrap_or(false),
981− // Supported(false)
982+ lsp::TextDocumentSyncSaveOptions::Supported(false) => return None,
982− _ => return None,
983 },
983 },
984− // unsupported
984+ // see: https://github.com/microsoft/language-server-protocol/issues/288
985− _ => return None,
985+ lsp::TextDocumentSyncCapability::Kind(..) => false,
986 };
986 };
987
987
988 Some(self.notify::<lsp::notification::DidSaveTextDocument>(
988 Some(self.notify::<lsp::notification::DidSaveTextDocument>(
helix-term/Cargo.toml+2−2
@@ -32,7 +32,7 @@ helix-vcs = { path = "../helix-vcs" }
32helix-loader = { path = "../helix-loader" }
32helix-loader = { path = "../helix-loader" }
33
33
34anyhow = "1"
34anyhow = "1"
35−once_cell = "1.18"
35+once_cell = "1.19"
36
36
37which = "5.0.0"
37which = "5.0.0"
38
38
@@ -73,7 +73,7 @@ grep-searcher = "0.1.13"
73
73
74[target.'cfg(not(windows))'.dependencies] # https://github.com/vorner/signal-hook/issues/100
74[target.'cfg(not(windows))'.dependencies] # https://github.com/vorner/signal-hook/issues/100
75signal-hook-tokio = { version = "0.3", features = ["futures-v0_3"] }
75signal-hook-tokio = { version = "0.3", features = ["futures-v0_3"] }
76−libc = "0.2.150"
76+libc = "0.2.151"
77
77
78[target.'cfg(target_os = "macos")'.dependencies]
78[target.'cfg(target_os = "macos")'.dependencies]
79crossterm = { version = "0.27", features = ["event-stream", "use-dev-tty"] }
79crossterm = { version = "0.27", features = ["event-stream", "use-dev-tty"] }
helix-term/src/application.rs+80−38
@@ -1,6 +1,7 @@
1use arc_swap::{access::Map, ArcSwap};
1use arc_swap::{access::Map, ArcSwap};
2use futures_util::Stream;
2use futures_util::Stream;
3use helix_core::{
3use helix_core::{
4+ chars::char_is_word,
4 diagnostic::{DiagnosticTag, NumberOrString},
5 diagnostic::{DiagnosticTag, NumberOrString},
5 path::get_relative_path,
6 path::get_relative_path,
6 pos_at_coords, syntax, Selection,
7 pos_at_coords, syntax, Selection,
@@ -717,7 +718,7 @@ impl Application {
717 ));
718 ));
718 }
719 }
719 }
720 }
720− Notification::PublishDiagnostics(params) => {
721+ Notification::PublishDiagnostics(mut params) => {
721 let path = match params.uri.to_file_path() {
722 let path = match params.uri.to_file_path() {
722 Ok(path) => path,
723 Ok(path) => path,
723 Err(_) => {
724 Err(_) => {
@@ -731,31 +732,69 @@ impl Application {
731 return;
732 return;
732 }
733 }
733 let offset_encoding = language_server.offset_encoding();
734 let offset_encoding = language_server.offset_encoding();
734− let doc = self.editor.document_by_path_mut(&path).filter(|doc| {
735+ // have to inline the function because of borrow checking...
736+ let doc = self.editor.documents.values_mut()
737+ .find(|doc| doc.path().map(|p| p == &path).unwrap_or(false))
738+ .filter(|doc| {
735− if let Some(version) = params.version {
739+ if let Some(version) = params.version {
736− if version != doc.version() {
740+ if version != doc.version() {
737− log::info!("Version ({version}) is out of date for {path:?} (expected ({}), dropping PublishDiagnostic notification", doc.version());
741+ log::info!("Version ({version}) is out of date for {path:?} (expected ({}), dropping PublishDiagnostic notification", doc.version());
738− return false;
742+ return false;
743+ }
739 }
744 }
740− }
741−
742− true
745+ true
743− });
746+ });
744
747
745 if let Some(doc) = doc {
748 if let Some(doc) = doc {
746− let lang_conf = doc.language_config();
749+ let lang_conf = doc.language.clone();
747− let text = doc.text();
750+ let text = doc.text().clone();
751+
752+ let mut unchaged_diag_sources_ = Vec::new();
753+ if let Some(lang_conf) = &lang_conf {
754+ if let Some(old_diagnostics) =
755+ self.editor.diagnostics.get(&params.uri)
756+ {
757+ if !lang_conf.persistent_diagnostic_sources.is_empty() {
758+ // Sort diagnostics first by severity and then by line numbers.
759+ // Note: The `lsp::DiagnosticSeverity` enum is already defined in decreasing order
760+ params
761+ .diagnostics
762+ .sort_unstable_by_key(|d| (d.severity, d.range.start));
763+ }
764+ for source in &lang_conf.persistent_diagnostic_sources {
765+ let new_diagnostics = params
766+ .diagnostics
767+ .iter()
768+ .filter(|d| d.source.as_ref() == Some(source));
769+ let old_diagnostics = old_diagnostics
770+ .iter()
771+ .filter(|(d, d_server)| {
772+ *d_server == server_id
773+ && d.source.as_ref() == Some(source)
774+ })
775+ .map(|(d, _)| d);
776+ if new_diagnostics.eq(old_diagnostics) {
777+ unchaged_diag_sources_.push(source.clone())
778+ }
779+ }
780+ }
781+ }
748
782
783+ let unchaged_diag_sources = &unchaged_diag_sources_;
749− let diagnostics = params
784+ let diagnostics =
750− .diagnostics
751− .iter()
752− .filter_map(|diagnostic| {
785+ params.diagnostics.iter().filter_map(move |diagnostic| {
753 use helix_core::diagnostic::{Diagnostic, Range, Severity::*};
786 use helix_core::diagnostic::{Diagnostic, Range, Severity::*};
754 use lsp::DiagnosticSeverity;
787 use lsp::DiagnosticSeverity;
755
788
789+ if diagnostic.source.as_ref().map_or(false, |source| {
790+ unchaged_diag_sources.contains(source)
791+ }) {
792+ return None;
793+ }
794+
756 // TODO: convert inside server
795 // TODO: convert inside server
757 let start = if let Some(start) = lsp_pos_to_pos(
796 let start = if let Some(start) = lsp_pos_to_pos(
758− text,
797+ &text,
759 diagnostic.range.start,
798 diagnostic.range.start,
760 offset_encoding,
799 offset_encoding,
761 ) {
800 ) {
@@ -766,14 +805,13 @@ impl Application {
766 };
805 };
767
806
768 let end = if let Some(end) =
807 let end = if let Some(end) =
769− lsp_pos_to_pos(text, diagnostic.range.end, offset_encoding)
808+ lsp_pos_to_pos(&text, diagnostic.range.end, offset_encoding)
770 {
809 {
771 end
810 end
772 } else {
811 } else {
773 log::warn!("lsp position out of bounds - {:?}", diagnostic);
812 log::warn!("lsp position out of bounds - {:?}", diagnostic);
774 return None;
813 return None;
775 };
814 };
776−
777 let severity =
815 let severity =
778 diagnostic.severity.map(|severity| match severity {
816 diagnostic.severity.map(|severity| match severity {
779 DiagnosticSeverity::ERROR => Error,
817 DiagnosticSeverity::ERROR => Error,
@@ -786,7 +824,7 @@ impl Application {
786 ),
824 ),
787 });
825 });
788
826
789− if let Some(lang_conf) = lang_conf {
827+ if let Some(lang_conf) = &lang_conf {
790 if let Some(severity) = severity {
828 if let Some(severity) = severity {
791 if severity < lang_conf.diagnostic_severity {
829 if severity < lang_conf.diagnostic_severity {
792 return None;
830 return None;
@@ -825,8 +863,17 @@ impl Application {
825 Vec::new()
863 Vec::new()
826 };
864 };
827
865
866+ let ends_at_word = start != end
867+ && end != 0
868+ && text.get_char(end - 1).map_or(false, char_is_word);
869+ let starts_at_word = start != end
870+ && text.get_char(start).map_or(false, char_is_word);
871+
828 Some(Diagnostic {
872 Some(Diagnostic {
829 range: Range { start, end },
873 range: Range { start, end },
874+ ends_at_word,
875+ starts_at_word,
876+ zero_width: start == end,
830 line: diagnostic.range.start.line as usize,
877 line: diagnostic.range.start.line as usize,
831 message: diagnostic.message.clone(),
878 message: diagnostic.message.clone(),
832 severity,
879 severity,
@@ -836,38 +883,33 @@ impl Application {
836 data: diagnostic.data.clone(),
883 data: diagnostic.data.clone(),
837 language_server_id: server_id,
884 language_server_id: server_id,
838 })
885 })
839− })
886+ });
840− .collect();
841
887
842− doc.replace_diagnostics(diagnostics, server_id);
888+ doc.replace_diagnostics(diagnostics, unchaged_diag_sources, server_id);
843 }
889 }
844
890
845− let mut diagnostics = params
891+ let diagnostics = params.diagnostics.into_iter().map(|d| (d, server_id));
846− .diagnostics
847− .into_iter()
848− .map(|d| (d, server_id))
849− .collect();
850
892
851 // Insert the original lsp::Diagnostics here because we may have no open document
893 // Insert the original lsp::Diagnostics here because we may have no open document
852 // for diagnosic message and so we can't calculate the exact position.
894 // for diagnosic message and so we can't calculate the exact position.
853 // When using them later in the diagnostics picker, we calculate them on-demand.
895 // When using them later in the diagnostics picker, we calculate them on-demand.
854− match self.editor.diagnostics.entry(params.uri) {
896+ let diagnostics = match self.editor.diagnostics.entry(params.uri) {
855 Entry::Occupied(o) => {
897 Entry::Occupied(o) => {
856 let current_diagnostics = o.into_mut();
898 let current_diagnostics = o.into_mut();
857 // there may entries of other language servers, which is why we can't overwrite the whole entry
899 // there may entries of other language servers, which is why we can't overwrite the whole entry
858 current_diagnostics.retain(|(_, lsp_id)| *lsp_id != server_id);
900 current_diagnostics.retain(|(_, lsp_id)| *lsp_id != server_id);
859− current_diagnostics.append(&mut diagnostics);
901+ current_diagnostics.extend(diagnostics);
860− // Sort diagnostics first by severity and then by line numbers.
861− // Note: The `lsp::DiagnosticSeverity` enum is already defined in decreasing order
862 current_diagnostics
902 current_diagnostics
863− .sort_unstable_by_key(|(d, _)| (d.severity, d.range.start));
903+ // Sort diagnostics first by severity and then by line numbers.
864− }
865− Entry::Vacant(v) => {
866− diagnostics
867− .sort_unstable_by_key(|(d, _)| (d.severity, d.range.start));
868− v.insert(diagnostics);
869 }
904 }
905+ Entry::Vacant(v) => v.insert(diagnostics.collect()),
870 };
906 };
907+
908+ // Sort diagnostics first by severity and then by line numbers.
909+ // Note: The `lsp::DiagnosticSeverity` enum is already defined in decreasing order
910+ diagnostics.sort_unstable_by_key(|(d, server_id)| {
911+ (d.severity, d.range.start, *server_id)
912+ });
871 }
913 }
872 Notification::ShowMessage(params) => {
914 Notification::ShowMessage(params) => {
873 log::warn!("unhandled window/showMessage: {:?}", params);
915 log::warn!("unhandled window/showMessage: {:?}", params);
helix-term/src/commands.rs+3
@@ -3053,6 +3053,7 @@ fn insert_with_indent(cx: &mut Context, cursor_fallback: IndentFallbackPos) {
3053 let indent = indent::indent_for_newline(
3053 let indent = indent::indent_for_newline(
3054 language_config,
3054 language_config,
3055 syntax,
3055 syntax,
3056+ &doc.config.load().indent_heuristic,
3056 &doc.indent_style,
3057 &doc.indent_style,
3057 tab_width,
3058 tab_width,
3058 text,
3059 text,
@@ -3181,6 +3182,7 @@ fn open(cx: &mut Context, open: Open) {
3181 let indent = indent::indent_for_newline(
3182 let indent = indent::indent_for_newline(
3182 doc.language_config(),
3183 doc.language_config(),
3183 doc.syntax(),
3184 doc.syntax(),
3185+ &doc.config.load().indent_heuristic,
3184 &doc.indent_style,
3186 &doc.indent_style,
3185 doc.tab_width(),
3187 doc.tab_width(),
3186 text,
3188 text,
@@ -3720,6 +3722,7 @@ pub mod insert {
3720 let indent = indent::indent_for_newline(
3722 let indent = indent::indent_for_newline(
3721 doc.language_config(),
3723 doc.language_config(),
3722 doc.syntax(),
3724 doc.syntax(),
3725+ &doc.config.load().indent_heuristic,
3723 &doc.indent_style,
3726 &doc.indent_style,
3724 doc.tab_width(),
3727 doc.tab_width(),
3725 text,
3728 text,
helix-term/src/commands/dap.rs+7−2
@@ -8,7 +8,7 @@ use dap::{StackFrame, Thread, ThreadStates};
8use helix_core::syntax::{DebugArgumentValue, DebugConfigCompletion, DebugTemplate};
8use helix_core::syntax::{DebugArgumentValue, DebugConfigCompletion, DebugTemplate};
9use helix_dap::{self as dap, Client};
9use helix_dap::{self as dap, Client};
10use helix_lsp::block_on;
10use helix_lsp::block_on;
11−use helix_view::editor::Breakpoint;
11+use helix_view::{editor::Breakpoint, graphics::Margin};
12
12
13use serde_json::{to_value, Value};
13use serde_json::{to_value, Value};
14use tokio_stream::wrappers::UnboundedReceiverStream;
14use tokio_stream::wrappers::UnboundedReceiverStream;
@@ -581,7 +581,12 @@ pub fn dap_variables(cx: &mut Context) {
581 }
581 }
582
582
583 let contents = Text::from(tui::text::Text::from(variables));
583 let contents = Text::from(tui::text::Text::from(variables));
584+ let margin = if cx.editor.popup_border() {
585+ Margin::all(1)
586+ } else {
587+ Margin::none()
588+ };
584− let popup = Popup::new("dap-variables", contents);
589+ let popup = Popup::new("dap-variables", contents).margin(margin);
585 cx.replace_or_push_layer("dap-variables", popup);
590 cx.replace_or_push_layer("dap-variables", popup);
586}
591}
587
592
helix-term/src/commands/lsp.rs+11−1
@@ -23,6 +23,7 @@ use helix_core::{
23use helix_view::{
23use helix_view::{
24 document::{DocumentInlayHints, DocumentInlayHintsId, Mode},
24 document::{DocumentInlayHints, DocumentInlayHintsId, Mode},
25 editor::Action,
25 editor::Action,
26+ graphics::Margin,
26 theme::Style,
27 theme::Style,
27 Document, View,
28 Document, View,
28};
29};
@@ -744,7 +745,16 @@ pub fn code_action(cx: &mut Context) {
744 });
745 });
745 picker.move_down(); // pre-select the first item
746 picker.move_down(); // pre-select the first item
746
747
748+ let margin = if editor.menu_border() {
749+ Margin::vertical(1)
750+ } else {
751+ Margin::none()
752+ };
753+
747− let popup = Popup::new("code-action", picker).with_scrollbar(false);
754+ let popup = Popup::new("code-action", picker)
755+ .with_scrollbar(false)
756+ .margin(margin);
757+
748 compositor.replace_or_push("code-action", popup);
758 compositor.replace_or_push("code-action", popup);
749 };
759 };
750
760
helix-term/src/commands/typed.rs+4−5
@@ -6,6 +6,7 @@ use crate::job::Job;
6use super::*;
6use super::*;
7
7
8use helix_core::fuzzy::fuzzy_match;
8use helix_core::fuzzy::fuzzy_match;
9+use helix_core::indent::MAX_INDENT;
9use helix_core::{encoding, line_ending, path::get_canonicalized_path, shellwords::Shellwords};
10use helix_core::{encoding, line_ending, path::get_canonicalized_path, shellwords::Shellwords};
10use helix_lsp::{OffsetEncoding, Url};
11use helix_lsp::{OffsetEncoding, Url};
11use helix_view::document::DEFAULT_LANGUAGE_NAME;
12use helix_view::document::DEFAULT_LANGUAGE_NAME;
@@ -476,8 +477,7 @@ fn set_indent_style(
476 cx.editor.set_status(match style {
477 cx.editor.set_status(match style {
477 Tabs => "tabs".to_owned(),
478 Tabs => "tabs".to_owned(),
478 Spaces(1) => "1 space".to_owned(),
479 Spaces(1) => "1 space".to_owned(),
479− Spaces(n) if (2..=8).contains(&n) => format!("{} spaces", n),
480+ Spaces(n) => format!("{} spaces", n),
480− _ => unreachable!(), // Shouldn't happen.
481 });
481 });
482 return Ok(());
482 return Ok(());
483 }
483 }
@@ -489,7 +489,7 @@ fn set_indent_style(
489 Some(arg) => arg
489 Some(arg) => arg
490 .parse::<u8>()
490 .parse::<u8>()
491 .ok()
491 .ok()
492− .filter(|n| (1..=8).contains(n))
492+ .filter(|n| (1..=MAX_INDENT).contains(n))
493 .map(Spaces),
493 .map(Spaces),
494 _ => None,
494 _ => None,
495 };
495 };
@@ -574,7 +574,6 @@ fn set_line_ending(
574
574
575 Ok(())
575 Ok(())
576}
576}
577−
578fn earlier(
577fn earlier(
579 cx: &mut compositor::Context,
578 cx: &mut compositor::Context,
580 args: &[Cow<str>],
579 args: &[Cow<str>],
@@ -2606,7 +2605,7 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
2606 TypableCommand {
2605 TypableCommand {
2607 name: "indent-style",
2606 name: "indent-style",
2608 aliases: &[],
2607 aliases: &[],
2609− doc: "Set the indentation style for editing. ('t' for tabs or 1-8 for number of spaces.)",
2608+ doc: "Set the indentation style for editing. ('t' for tabs or 1-16 for number of spaces.)",
2610 fun: set_indent_style,
2609 fun: set_indent_style,
2611 signature: CommandSignature::none(),
2610 signature: CommandSignature::none(),
2612 },
2611 },
helix-term/src/ui/completion.rs+17−1
@@ -2,6 +2,7 @@ use crate::compositor::{Component, Context, Event, EventResult};
2use helix_view::{
2use helix_view::{
3 document::SavePoint,
3 document::SavePoint,
4 editor::CompleteAction,
4 editor::CompleteAction,
5+ graphics::Margin,
5 theme::{Modifier, Style},
6 theme::{Modifier, Style},
6 ViewId,
7 ViewId,
7};
8};
@@ -326,9 +327,18 @@ impl Completion {
326 }
327 }
327 };
328 };
328 });
329 });
330+
331+ let margin = if editor.menu_border() {
332+ Margin::vertical(1)
333+ } else {
334+ Margin::none()
335+ };
336+
329 let popup = Popup::new(Self::ID, menu)
337 let popup = Popup::new(Self::ID, menu)
330 .with_scrollbar(false)
338 .with_scrollbar(false)
331− .ignore_escape_key(true);
339+ .ignore_escape_key(true)
340+ .margin(margin);
341+
332 let mut completion = Self {
342 let mut completion = Self {
333 popup,
343 popup,
334 start_offset,
344 start_offset,
@@ -569,6 +579,12 @@ impl Component for Completion {
569 // clear area
579 // clear area
570 let background = cx.editor.theme.get("ui.popup");
580 let background = cx.editor.theme.get("ui.popup");
571 surface.clear_with(doc_area, background);
581 surface.clear_with(doc_area, background);
582+
583+ if cx.editor.popup_border() {
584+ use tui::widgets::{Block, Borders, Widget};
585+ Widget::render(Block::default().borders(Borders::ALL), doc_area, surface);
586+ }
587+
572 markdown_doc.render(doc_area, surface, cx);
588 markdown_doc.render(doc_area, surface, cx);
573 }
589 }
574}
590}
helix-term/src/ui/lsp.rs+3−1
@@ -92,7 +92,9 @@ impl Component for SignatureHelp {
92 Some(doc) => Markdown::new(doc.clone(), Arc::clone(&self.config_loader)),
92 Some(doc) => Markdown::new(doc.clone(), Arc::clone(&self.config_loader)),
93 };
93 };
94 let sig_doc = sig_doc.parse(Some(&cx.editor.theme));
94 let sig_doc = sig_doc.parse(Some(&cx.editor.theme));
95+ let sig_doc_area = area
95− let sig_doc_area = area.clip_top(sig_text_area.height + 2);
96+ .clip_top(sig_text_area.height + 2)
97+ .clip_bottom(u16::from(cx.editor.popup_border()));
96 let sig_doc_para = Paragraph::new(sig_doc)
98 let sig_doc_para = Paragraph::new(sig_doc)
97 .wrap(Wrap { trim: false })
99 .wrap(Wrap { trim: false })
98 .scroll((cx.scroll.unwrap_or_default() as u16, 0));
100 .scroll((cx.scroll.unwrap_or_default() as u16, 0));
helix-term/src/ui/menu.rs+34−13
@@ -7,11 +7,18 @@ use crate::{
7use helix_core::fuzzy::MATCHER;
7use helix_core::fuzzy::MATCHER;
8use nucleo::pattern::{Atom, AtomKind, CaseMatching};
8use nucleo::pattern::{Atom, AtomKind, CaseMatching};
9use nucleo::{Config, Utf32Str};
9use nucleo::{Config, Utf32Str};
10+use tui::{
10−use tui::{buffer::Buffer as Surface, widgets::Table};
11+ buffer::Buffer as Surface,
12+ widgets::{Block, Borders, Table, Widget},
13+};
11
14
12pub use tui::widgets::{Cell, Row};
15pub use tui::widgets::{Cell, Row};
13
16
17+use helix_view::{
14−use helix_view::{editor::SmartTabConfig, graphics::Rect, Editor};
18+ editor::SmartTabConfig,
19+ graphics::{Margin, Rect},
20+ Editor,
21+};
15use tui::layout::Constraint;
22use tui::layout::Constraint;
16
23
17pub trait Item: Sync + Send + 'static {
24pub trait Item: Sync + Send + 'static {
@@ -322,6 +329,15 @@ impl<T: Item + 'static> Component for Menu<T> {
322 let selected = theme.get("ui.menu.selected");
329 let selected = theme.get("ui.menu.selected");
323 surface.clear_with(area, style);
330 surface.clear_with(area, style);
324
331
332+ let render_borders = cx.editor.menu_border();
333+
334+ let area = if render_borders {
335+ Widget::render(Block::default().borders(Borders::ALL), area, surface);
336+ area.inner(&Margin::vertical(1))
337+ } else {
338+ area
339+ };
340+
325 let scroll = self.scroll;
341 let scroll = self.scroll;
326
342
327 let options: Vec<_> = self
343 let options: Vec<_> = self
@@ -362,15 +378,19 @@ impl<T: Item + 'static> Component for Menu<T> {
362 false,
378 false,
363 );
379 );
364
380
381+ let render_borders = cx.editor.menu_border();
382+
383+ if !render_borders {
365− if let Some(cursor) = self.cursor {
384+ if let Some(cursor) = self.cursor {
366− let offset_from_top = cursor - scroll;
385+ let offset_from_top = cursor - scroll;
367− let left = &mut surface[(area.left(), area.y + offset_from_top as u16)];
386+ let left = &mut surface[(area.left(), area.y + offset_from_top as u16)];
368− left.set_style(selected);
387+ left.set_style(selected);
369− let right = &mut surface[(
388+ let right = &mut surface[(
370− area.right().saturating_sub(1),
389+ area.right().saturating_sub(1),
371− area.y + offset_from_top as u16,
390+ area.y + offset_from_top as u16,
372− )];
391+ )];
373− right.set_style(selected);
392+ right.set_style(selected);
393+ }
374 }
394 }
375
395
376 let fits = len <= win_height;
396 let fits = len <= win_height;
@@ -385,12 +405,13 @@ impl<T: Item + 'static> Component for Menu<T> {
385 for i in 0..win_height {
405 for i in 0..win_height {
386 cell = &mut surface[(area.right() - 1, area.top() + i as u16)];
406 cell = &mut surface[(area.right() - 1, area.top() + i as u16)];
387
407
388− cell.set_symbol("▐"); // right half block
408+ let half_block = if render_borders { "▌" } else { "▐" };
389
409
390 if scroll_line <= i && i < scroll_line + scroll_height {
410 if scroll_line <= i && i < scroll_line + scroll_height {
391 // Draw scroll thumb
411 // Draw scroll thumb
412+ cell.set_symbol(half_block);
392 cell.set_fg(scroll_style.fg.unwrap_or(helix_view::theme::Color::Reset));
413 cell.set_fg(scroll_style.fg.unwrap_or(helix_view::theme::Color::Reset));
393− } else {
414+ } else if !render_borders {
394 // Draw scroll track
415 // Draw scroll track
395 cell.set_fg(scroll_style.bg.unwrap_or(helix_view::theme::Color::Reset));
416 cell.set_fg(scroll_style.bg.unwrap_or(helix_view::theme::Color::Reset));
396 }
417 }
helix-term/src/ui/popup.rs+27−7
@@ -3,7 +3,10 @@ use crate::{
3 compositor::{Callback, Component, Context, Event, EventResult},
3 compositor::{Callback, Component, Context, Event, EventResult},
4 ctrl, key,
4 ctrl, key,
5};
5};
6+use tui::{
6−use tui::buffer::Buffer as Surface;
7+ buffer::Buffer as Surface,
8+ widgets::{Block, Borders, Widget},
9+};
7
10
8use helix_core::Position;
11use helix_core::Position;
9use helix_view::{
12use helix_view::{
@@ -252,13 +255,29 @@ impl<T: Component> Component for Popup<T> {
252 let background = cx.editor.theme.get("ui.popup");
255 let background = cx.editor.theme.get("ui.popup");
253 surface.clear_with(area, background);
256 surface.clear_with(area, background);
254
257
258+ let render_borders = cx.editor.popup_border();
259+
260+ let inner = if self
261+ .contents
262+ .type_name()
263+ .starts_with("helix_term::ui::menu::Menu")
264+ {
265+ area
266+ } else {
255− let inner = area.inner(&self.margin);
267+ area.inner(&self.margin)
268+ };
269+
270+ let border = usize::from(render_borders);
271+ if render_borders {
272+ Widget::render(Block::default().borders(Borders::ALL), area, surface);
273+ }
274+
256 self.contents.render(inner, surface, cx);
275 self.contents.render(inner, surface, cx);
257
276
258 // render scrollbar if contents do not fit
277 // render scrollbar if contents do not fit
259 if self.has_scrollbar {
278 if self.has_scrollbar {
260− let win_height = inner.height as usize;
279+ let win_height = (inner.height as usize).saturating_sub(2 * border);
261− let len = self.child_size.1 as usize;
280+ let len = (self.child_size.1 as usize).saturating_sub(2 * border);
262 let fits = len <= win_height;
281 let fits = len <= win_height;
263 let scroll = self.scroll;
282 let scroll = self.scroll;
264 let scroll_style = cx.editor.theme.get("ui.menu.scroll");
283 let scroll_style = cx.editor.theme.get("ui.menu.scroll");
@@ -274,14 +293,15 @@ impl<T: Component> Component for Popup<T> {
274
293
275 let mut cell;
294 let mut cell;
276 for i in 0..win_height {
295 for i in 0..win_height {
277− cell = &mut surface[(inner.right() - 1, inner.top() + i as u16)];
296+ cell = &mut surface[(inner.right() - 1, inner.top() + (border + i) as u16)];
278
297
279− cell.set_symbol("▐"); // right half block
298+ let half_block = if render_borders { "▌" } else { "▐" };
280
299
281 if scroll_line <= i && i < scroll_line + scroll_height {
300 if scroll_line <= i && i < scroll_line + scroll_height {
282 // Draw scroll thumb
301 // Draw scroll thumb
302+ cell.set_symbol(half_block);
283 cell.set_fg(scroll_style.fg.unwrap_or(helix_view::theme::Color::Reset));
303 cell.set_fg(scroll_style.fg.unwrap_or(helix_view::theme::Color::Reset));
284− } else {
304+ } else if !render_borders {
285 // Draw scroll track
305 // Draw scroll track
286 cell.set_fg(scroll_style.bg.unwrap_or(helix_view::theme::Color::Reset));
306 cell.set_fg(scroll_style.bg.unwrap_or(helix_view::theme::Color::Reset));
287 }
307 }
helix-tui/Cargo.toml+1−1
@@ -24,5 +24,5 @@ unicode-segmentation = "1.10"
24crossterm = { version = "0.27", optional = true }
24crossterm = { version = "0.27", optional = true }
25termini = "1.0"
25termini = "1.0"
26serde = { version = "1", "optional" = true, features = ["derive"]}
26serde = { version = "1", "optional" = true, features = ["derive"]}
27−once_cell = "1.18"
27+once_cell = "1.19"
28log = "~0.4"
28log = "~0.4"
helix-vcs/Cargo.toml+1−1
@@ -19,7 +19,7 @@ tokio = { version = "1", features = ["rt", "rt-multi-thread", "time", "sync", "p
19parking_lot = "0.12"
19parking_lot = "0.12"
20arc-swap = { version = "1.6.0" }
20arc-swap = { version = "1.6.0" }
21
21
22−gix = { version = "0.55.0", default-features = false , optional = true }
22+gix = { version = "0.57.0", default-features = false , optional = true }
23imara-diff = "0.1.5"
23imara-diff = "0.1.5"
24anyhow = "1"
24anyhow = "1"
25
25
helix-vcs/src/git.rs+4−4
@@ -3,7 +3,7 @@ use arc_swap::ArcSwap;
3use std::path::Path;
3use std::path::Path;
4use std::sync::Arc;
4use std::sync::Arc;
5
5
6−use gix::objs::tree::EntryMode;
6+use gix::objs::tree::EntryKind;
7use gix::sec::trust::DefaultForLevel;
7use gix::sec::trust::DefaultForLevel;
8use gix::{Commit, ObjectId, Repository, ThreadSafeRepository};
8use gix::{Commit, ObjectId, Repository, ThreadSafeRepository};
9
9
@@ -128,12 +128,12 @@ fn find_file_in_commit(repo: &Repository, commit: &Commit, file: &Path) -> Resul
128 let tree_entry = tree
128 let tree_entry = tree
129 .lookup_entry_by_path(rel_path, &mut Vec::new())?
129 .lookup_entry_by_path(rel_path, &mut Vec::new())?
130 .context("file is untracked")?;
130 .context("file is untracked")?;
131− match tree_entry.mode() {
131+ match tree_entry.mode().kind() {
132 // not a file, everything is new, do not show diff
132 // not a file, everything is new, do not show diff
133− mode @ (EntryMode::Tree | EntryMode::Commit | EntryMode::Link) => {
133+ mode @ (EntryKind::Tree | EntryKind::Commit | EntryKind::Link) => {
134 bail!("entry at {} is not a file but a {mode:?}", file.display())
134 bail!("entry at {} is not a file but a {mode:?}", file.display())
135 }
135 }
136 // found a file
136 // found a file
137− EntryMode::Blob | EntryMode::BlobExecutable => Ok(tree_entry.object_id()),
137+ EntryKind::Blob | EntryKind::BlobExecutable => Ok(tree_entry.object_id()),
138 }
138 }
139}
139}
helix-view/Cargo.toml+1−1
@@ -27,7 +27,7 @@ anyhow = "1"
27crossterm = { version = "0.27", optional = true }
27crossterm = { version = "0.27", optional = true }
28
28
29# Conversion traits
29# Conversion traits
30−once_cell = "1.18"
30+once_cell = "1.19"
31url = "2.5.0"
31url = "2.5.0"
32
32
33arc-swap = { version = "1.6.0" }
33arc-swap = { version = "1.6.0" }
helix-view/src/document.rs+57−20
@@ -1213,23 +1213,40 @@ impl Document {
1213
1213
1214 let changes = transaction.changes();
1214 let changes = transaction.changes();
1215
1215
1216− changes.update_positions(
1217− self.diagnostics
1218− .iter_mut()
1219− .map(|diagnostic| (&mut diagnostic.range.start, Assoc::After)),
1220− );
1221− changes.update_positions(
1222− self.diagnostics
1223− .iter_mut()
1224− .map(|diagnostic| (&mut diagnostic.range.end, Assoc::After)),
1225− );
1226− // map state.diagnostics over changes::map_pos too
1216+ // map diagnostics over changes too
1217+ changes.update_positions(self.diagnostics.iter_mut().map(|diagnostic| {
1218+ let assoc = if diagnostic.starts_at_word {
1219+ Assoc::BeforeWord
1220+ } else {
1221+ Assoc::After
1222+ };
1223+ (&mut diagnostic.range.start, assoc)
1224+ }));
1225+ changes.update_positions(self.diagnostics.iter_mut().map(|diagnostic| {
1226+ let assoc = if diagnostic.ends_at_word {
1227+ Assoc::AfterWord
1228+ } else {
1229+ Assoc::Before
1230+ };
1231+ (&mut diagnostic.range.end, assoc)
1232+ }));
1227− for diagnostic in &mut self.diagnostics {
1233+ self.diagnostics.retain_mut(|diagnostic| {
1234+ if diagnostic.range.start > diagnostic.range.end
1235+ || (!diagnostic.zero_width && diagnostic.range.start == diagnostic.range.end)
1236+ {
1237+ return false;
1238+ }
1228 diagnostic.line = self.text.char_to_line(diagnostic.range.start);
1239 diagnostic.line = self.text.char_to_line(diagnostic.range.start);
1240+ true
1229− }
1241+ });
1230
1242
1231− self.diagnostics
1232− .sort_unstable_by_key(|diagnostic| diagnostic.range);
1243+ self.diagnostics.sort_unstable_by_key(|diagnostic| {
1244+ (
1245+ diagnostic.range,
1246+ diagnostic.severity,
1247+ diagnostic.language_server_id,
1248+ )
1249+ });
1233
1250
1234 // Update the inlay hint annotations' positions, helping ensure they are displayed in the proper place
1251 // Update the inlay hint annotations' positions, helping ensure they are displayed in the proper place
1235 let apply_inlay_hint_changes = |annotations: &mut Rc<[InlineAnnotation]>| {
1252 let apply_inlay_hint_changes = |annotations: &mut Rc<[InlineAnnotation]>| {
@@ -1706,13 +1723,33 @@ impl Document {
1706
1723
1707 pub fn replace_diagnostics(
1724 pub fn replace_diagnostics(
1708 &mut self,
1725 &mut self,
1709− mut diagnostics: Vec<Diagnostic>,
1726+ diagnostics: impl IntoIterator<Item = Diagnostic>,
1727+ unchanged_sources: &[String],
1710 language_server_id: usize,
1728 language_server_id: usize,
1711 ) {
1729 ) {
1730+ if unchanged_sources.is_empty() {
1712− self.clear_diagnostics(language_server_id);
1731+ self.clear_diagnostics(language_server_id);
1732+ } else {
1713− self.diagnostics.append(&mut diagnostics);
1733+ self.diagnostics.retain(|d| {
1714− self.diagnostics
1734+ if d.language_server_id != language_server_id {
1735+ return true;
1736+ }
1737+
1738+ if let Some(source) = &d.source {
1739+ unchanged_sources.contains(source)
1740+ } else {
1741+ false
1742+ }
1743+ });
1744+ }
1745+ self.diagnostics.extend(diagnostics);
1715− .sort_unstable_by_key(|diagnostic| diagnostic.range);
1746+ self.diagnostics.sort_unstable_by_key(|diagnostic| {
1747+ (
1748+ diagnostic.range,
1749+ diagnostic.severity,
1750+ diagnostic.language_server_id,
1751+ )
1752+ });
1716 }
1753 }
1717
1754
1718 pub fn clear_diagnostics(&mut self, language_server_id: usize) {
1755 pub fn clear_diagnostics(&mut self, language_server_id: usize) {
helix-view/src/editor.rs+28−3
@@ -42,10 +42,9 @@ use anyhow::{anyhow, bail, Error};
42pub use helix_core::diagnostic::Severity;
42pub use helix_core::diagnostic::Severity;
43use helix_core::{
43use helix_core::{
44 auto_pairs::AutoPairs,
44 auto_pairs::AutoPairs,
45− syntax::{self, AutoPairConfig, SoftWrap},
45+ syntax::{self, AutoPairConfig, IndentationHeuristic, SoftWrap},
46− Change, LineEnding, NATIVE_LINE_ENDING,
46+ Change, LineEnding, Position, Selection, NATIVE_LINE_ENDING,
47};
47};
48−use helix_core::{Position, Selection};
49use helix_dap as dap;
48use helix_dap as dap;
50use helix_lsp::lsp;
49use helix_lsp::lsp;
51
50
@@ -291,6 +290,11 @@ pub struct Config {
291 pub insert_final_newline: bool,
290 pub insert_final_newline: bool,
292 /// Enables smart tab
291 /// Enables smart tab
293 pub smart_tab: Option<SmartTabConfig>,
292 pub smart_tab: Option<SmartTabConfig>,
293+ /// Draw border around popups.
294+ pub popup_border: PopupBorderConfig,
295+ /// Which indent heuristic to use when a new line is inserted
296+ #[serde(default)]
297+ pub indent_heuristic: IndentationHeuristic,
294}
298}
295
299
296#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq, PartialOrd, Ord)]
300#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq, PartialOrd, Ord)]
@@ -794,6 +798,15 @@ impl From<LineEndingConfig> for LineEnding {
794 }
798 }
795}
799}
796
800
801+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
802+#[serde(rename_all = "kebab-case")]
803+pub enum PopupBorderConfig {
804+ None,
805+ All,
806+ Popup,
807+ Menu,
808+}
809+
797impl Default for Config {
810impl Default for Config {
798 fn default() -> Self {
811 fn default() -> Self {
799 Self {
812 Self {
@@ -841,6 +854,8 @@ impl Default for Config {
841 default_line_ending: LineEndingConfig::default(),
854 default_line_ending: LineEndingConfig::default(),
842 insert_final_newline: true,
855 insert_final_newline: true,
843 smart_tab: Some(SmartTabConfig::default()),
856 smart_tab: Some(SmartTabConfig::default()),
857+ popup_border: PopupBorderConfig::None,
858+ indent_heuristic: IndentationHeuristic::default(),
844 }
859 }
845 }
860 }
846}
861}
@@ -1060,6 +1075,16 @@ impl Editor {
1060 }
1075 }
1061 }
1076 }
1062
1077
1078+ pub fn popup_border(&self) -> bool {
1079+ self.config().popup_border == PopupBorderConfig::All
1080+ || self.config().popup_border == PopupBorderConfig::Popup
1081+ }
1082+
1083+ pub fn menu_border(&self) -> bool {
1084+ self.config().popup_border == PopupBorderConfig::All
1085+ || self.config().popup_border == PopupBorderConfig::Menu
1086+ }
1087+
1063 pub fn apply_motion<F: Fn(&mut Self) + 'static>(&mut self, motion: F) {
1088 pub fn apply_motion<F: Fn(&mut Self) + 'static>(&mut self, motion: F) {
1064 motion(self);
1089 motion(self);
1065 self.last_motion = Some(Box::new(motion));
1090 self.last_motion = Some(Box::new(motion));
languages.toml+57−3
@@ -3,6 +3,7 @@
3
3
4[language-server]
4[language-server]
5
5
6+als = { command = "als" }
6awk-language-server = { command = "awk-language-server" }
7awk-language-server = { command = "awk-language-server" }
7bash-language-server = { command = "bash-language-server", args = ["start"] }
8bash-language-server = { command = "bash-language-server", args = ["start"] }
8bass = { command = "bass", args = ["--lsp"] }
9bass = { command = "bass", args = ["--lsp"] }
@@ -190,6 +191,7 @@ auto-format = true
190comment-token = "//"
191comment-token = "//"
191language-servers = [ "rust-analyzer" ]
192language-servers = [ "rust-analyzer" ]
192indent = { tab-width = 4, unit = " " }
193indent = { tab-width = 4, unit = " " }
194+persistent-diagnostic-sources = ["rustc", "clippy"]
193
195
194[language.auto-pairs]
196[language.auto-pairs]
195'(' = ')'
197'(' = ')'
@@ -345,7 +347,8 @@ file-types = [
345 "jsonld",
347 "jsonld",
346 ".vuerc",
348 ".vuerc",
347 "composer.lock",
349 "composer.lock",
348− ".watchmanconfig"
350+ ".watchmanconfig",
351+ "avsc"
349]
352]
350language-servers = [ "vscode-json-language-server" ]
353language-servers = [ "vscode-json-language-server" ]
351auto-format = true
354auto-format = true
@@ -954,6 +957,19 @@ indent = { tab-width = 2, unit = " " }
954name = "java"
957name = "java"
955source = { git = "https://github.com/tree-sitter/tree-sitter-java", rev = "09d650def6cdf7f479f4b78f595e9ef5b58ce31e" }
958source = { git = "https://github.com/tree-sitter/tree-sitter-java", rev = "09d650def6cdf7f479f4b78f595e9ef5b58ce31e" }
956
959
960+[[language]]
961+name = "smali"
962+scope = "source.smali"
963+injection-regex = "smali"
964+file-types = ["smali"]
965+comment-token = "#"
966+roots = []
967+indent = { tab-width = 4, unit = " " }
968+
969+[[grammar]]
970+name = "smali"
971+source = { git = "https://github.com/amaanq/tree-sitter-smali", rev = "5ae51e15c4d1ac93cba6127caf3d1f0a072c140c" }
972+
957[[language]]
973[[language]]
958name = "ledger"
974name = "ledger"
959scope = "source.ledger"
975scope = "source.ledger"
@@ -1668,6 +1684,7 @@ source = { git = "https://github.com/r-lib/tree-sitter-r", rev = "cc04302e1bff76
1668[[language]]
1684[[language]]
1669name = "rmarkdown"
1685name = "rmarkdown"
1670scope = "source.rmd"
1686scope = "source.rmd"
1687+language-id = "rmd"
1671injection-regex = "(r|R)md"
1688injection-regex = "(r|R)md"
1672file-types = ["rmd", "Rmd"]
1689file-types = ["rmd", "Rmd"]
1673indent = { tab-width = 2, unit = " " }
1690indent = { tab-width = 2, unit = " " }
@@ -2220,7 +2237,7 @@ formatter = { command = "dfmt" }
2220
2237
2221[[grammar]]
2238[[grammar]]
2222name = "d"
2239name = "d"
2223−source = { git = "https://github.com/gdamore/tree-sitter-d", rev="601c4a1e8310fb2f3c43fa8a923d0d27497f3c04" }
2240+source = { git = "https://github.com/gdamore/tree-sitter-d", rev = "5566f8ce8fc24186fad06170bbb3c8d97c935d74" }
2224
2241
2225[[language]]
2242[[language]]
2226name = "vhs"
2243name = "vhs"
@@ -2304,7 +2321,8 @@ file-types = [
2304 "xpdl",
2321 "xpdl",
2305 "xul",
2322 "xul",
2306 "xoml",
2323 "xoml",
2307− "musicxml"
2324+ "musicxml",
2325+ "glif"
2308]
2326]
2309indent = { tab-width = 2, unit = " " }
2327indent = { tab-width = 2, unit = " " }
2310
2328
@@ -2696,6 +2714,7 @@ file-types = [ "cabal" ]
2696roots = ["cabal.project", "Setup.hs"]
2714roots = ["cabal.project", "Setup.hs"]
2697indent = { tab-width = 2, unit = " " }
2715indent = { tab-width = 2, unit = " " }
2698comment-token = "--"
2716comment-token = "--"
2717+language-servers = [ "haskell-language-server" ]
2699
2718
2700[[language]]
2719[[language]]
2701name = "hurl"
2720name = "hurl"
@@ -2919,6 +2938,32 @@ file-types = ["gmi"]
2919name = "gemini"
2938name = "gemini"
2920source = { git = "https://git.sr.ht/~nbsp/tree-sitter-gemini", rev = "3cc5e4bdf572d5df4277fc2e54d6299bd59a54b3" }
2939source = { git = "https://git.sr.ht/~nbsp/tree-sitter-gemini", rev = "3cc5e4bdf572d5df4277fc2e54d6299bd59a54b3" }
2921
2940
2941+[[language]]
2942+name = "agda"
2943+scope = "source.agda"
2944+injection-regex = "agda"
2945+file-types = ["agda"]
2946+roots = []
2947+comment-token = "--"
2948+# language-servers = [ "als" ]
2949+# the agda language server is of questionable functionality.
2950+auto-format = false
2951+indent = { tab-width = 2, unit = " " }
2952+
2953+[language.auto-pairs]
2954+'"' = '"'
2955+"'" = "'"
2956+'{' = '}'
2957+'(' = ')'
2958+'[' = ']'
2959+
2960+# [language.debugger]
2961+# ?? can this be used for proof assistant support? explore
2962+
2963+[[grammar]]
2964+name = "agda"
2965+source = { git = "https://github.com/tree-sitter/tree-sitter-agda", rev = "c21c3a0f996363ed17b8ac99d827fe5a4821f217" }
2966+
2922[[language]]
2967[[language]]
2923name = "templ"
2968name = "templ"
2924scope = "source.templ"
2969scope = "source.templ"
@@ -2952,3 +2997,12 @@ file-types = ["log"]
2952[[grammar]]
2997[[grammar]]
2953name = "log"
2998name = "log"
2954source = { git = "https://github.com/Tudyx/tree-sitter-log", rev = "62cfe307e942af3417171243b599cc7deac5eab9" }
2999source = { git = "https://github.com/Tudyx/tree-sitter-log", rev = "62cfe307e942af3417171243b599cc7deac5eab9" }
3000+
3001+[[language]]
3002+name = "janet"
3003+scope = "source.janet"
3004+injection-regex = "janet"
3005+file-types = ["janet"]
3006+comment-token = "#"
3007+indent = { tab-width = 2, unit = " " }
3008+grammar = "clojure"
logo.svg+1−1
@@ -1 +1 @@
1−<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;" viewBox="663.38 37.57 575.35 903.75"> <g transform="matrix(1,0,0,1,-31352.7,-1817.25)"> <g transform="matrix(1,0,0,1,31062.7,-20.8972)"> <g transform="matrix(1,0,0,1,-130.173,0.00185558)"> <path d="M1083.58,1875.72L1635.06,2194.12C1649.8,2202.63 1658.88,2218.37 1658.88,2235.39C1658.88,2264.98 1658.88,2311.74 1658.88,2341.33C1658.88,2349.84 1656.61,2358.03 1652.5,2365.16C1652.5,2365.16 1214.7,2112.4 1107.2,2050.33C1092.58,2041.89 1083.58,2026.29 1083.58,2009.41C1083.58,1963.5 1083.58,1875.72 1083.58,1875.72Z" style="fill:#706bc8;"></path> </g> <g transform="matrix(1,0,0,1,-130.173,0.00185558)"> <path d="M1635.26,2604.84C1649.88,2613.28 1658.88,2628.87 1658.88,2645.75C1658.88,2691.67 1658.88,2779.44 1658.88,2779.44L1107.41,2461.05C1092.66,2452.53 1083.58,2436.8 1083.58,2419.78C1083.58,2390.19 1083.58,2343.42 1083.58,2313.84C1083.58,2305.32 1085.85,2297.13 1089.96,2290.01C1089.96,2290.01 1527.76,2542.77 1635.26,2604.84Z" style="fill:#55c5e4;"></path> </g> <g transform="matrix(1,0,0,1,216.062,984.098)"> <path d="M790.407,1432.56C785.214,1435.55 780.717,1439.9 777.509,1445.46C767.862,1462.16 773.473,1483.76 790.004,1493.59L789.998,1493.59L761.173,1476.95C746.427,1468.44 737.344,1452.71 737.344,1435.68C737.344,1406.09 737.344,1359.33 737.344,1329.74C737.344,1312.71 746.427,1296.98 761.173,1288.47L1259.59,1000.74L1259.83,1000.6C1264.92,997.617 1269.33,993.314 1272.48,987.844C1282.13,971.136 1276.52,949.544 1259.99,939.707L1260,939.707L1288.82,956.349C1303.57,964.862 1312.65,980.595 1312.65,997.622C1312.65,1027.21 1312.65,1073.97 1312.65,1103.56C1312.65,1120.59 1303.57,1136.32 1288.82,1144.83L1259.19,1161.94L1259.59,1161.68L790.407,1432.56Z" style="fill:#84ddea;"></path> </g> <g transform="matrix(1,0,0,1,216.062,984.098)"> <path d="M790.407,1686.24C785.214,1689.23 780.717,1693.58 777.509,1699.13C767.862,1715.84 773.473,1737.43 790.004,1747.27L789.998,1747.27L761.173,1730.63C746.427,1722.12 737.344,1706.38 737.344,1689.36C737.344,1659.77 737.344,1613.01 737.344,1583.42C737.344,1566.39 746.427,1550.66 761.173,1542.15L1259.59,1254.42L1259.83,1254.28C1264.92,1251.29 1269.33,1246.99 1272.48,1241.52C1282.13,1224.81 1276.52,1203.22 1259.99,1193.38L1260,1193.38L1288.82,1210.03C1303.57,1218.54 1312.65,1234.27 1312.65,1251.3C1312.65,1280.89 1312.65,1327.65 1312.65,1357.24C1312.65,1374.26 1303.57,1390 1288.82,1398.51L1259.19,1415.61L1259.59,1415.36L790.407,1686.24Z" style="fill:#997bc8;"></path></g></g></g> </svg>
1+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;" viewBox="663.38 37.57 575.35 903.75"><g transform="matrix(1,0,0,1,-31352.7,-1817.25)"><g transform="matrix(1,0,0,1,31062.7,-20.8972)"><g transform="matrix(1,0,0,1,-130.173,0.00185558)"><path style="fill:#706bc8;" d="M1083.58,1875.72L1635.06,2194.12C1649.8,2202.63 1658.88,2218.37 1658.88,2235.39C1658.88,2264.98 1658.88,2311.74 1658.88,2341.33C1658.88,2349.84 1656.61,2358.03 1652.5,2365.16C1652.5,2365.16 1214.7,2112.4 1107.2,2050.33C1092.58,2041.89 1083.58,2026.29 1083.58,2009.41C1083.58,1963.5 1083.58,1875.72 1083.58,1875.72Z"></path></g><g transform="matrix(1,0,0,1,-130.173,0.00185558)"><path style="fill:#55c5e4;" d="M1635.26,2604.84C1649.88,2613.28 1658.88,2628.87 1658.88,2645.75C1658.88,2691.67 1658.88,2779.44 1658.88,2779.44L1107.41,2461.05C1092.66,2452.53 1083.58,2436.8 1083.58,2419.78C1083.58,2390.19 1083.58,2343.42 1083.58,2313.84C1083.58,2305.32 1085.85,2297.13 1089.96,2290.01C1089.96,2290.01 1527.76,2542.77 1635.26,2604.84Z"></path></g><g transform="matrix(1,0,0,1,216.062,984.098)"><path style="fill:#84ddea;" d="M790.407,1432.56C785.214,1435.55 780.717,1439.9 777.509,1445.46C767.862,1462.16 773.473,1483.76 790.004,1493.59L789.998,1493.59L761.173,1476.95C746.427,1468.44 737.344,1452.71 737.344,1435.68C737.344,1406.09 737.344,1359.33 737.344,1329.74C737.344,1312.71 746.427,1296.98 761.173,1288.47L1259.59,1000.74L1259.83,1000.6C1264.92,997.617 1269.33,993.314 1272.48,987.844C1282.13,971.136 1276.52,949.544 1259.99,939.707L1260,939.707L1288.82,956.349C1303.57,964.862 1312.65,980.595 1312.65,997.622C1312.65,1027.21 1312.65,1073.97 1312.65,1103.56C1312.65,1120.59 1303.57,1136.32 1288.82,1144.83L1259.19,1161.94L1259.59,1161.68L790.407,1432.56Z"></path></g><g transform="matrix(1,0,0,1,216.062,984.098)"><path style="fill:#997bc8;" d="M790.407,1686.24C785.214,1689.23 780.717,1693.58 777.509,1699.13C767.862,1715.84 773.473,1737.43 790.004,1747.27L789.998,1747.27L761.173,1730.63C746.427,1722.12 737.344,1706.38 737.344,1689.36C737.344,1659.77 737.344,1613.01 737.344,1583.42C737.344,1566.39 746.427,1550.66 761.173,1542.15L1259.59,1254.42L1259.83,1254.28C1264.92,1251.29 1269.33,1246.99 1272.48,1241.52C1282.13,1224.81 1276.52,1203.22 1259.99,1193.38L1260,1193.38L1288.82,1210.03C1303.57,1218.54 1312.65,1234.27 1312.65,1251.3C1312.65,1280.89 1312.65,1327.65 1312.65,1357.24C1312.65,1374.26 1303.57,1390 1288.82,1398.51L1259.19,1415.61L1259.59,1415.36L790.407,1686.24Z"></path></g></g></g></svg>
runtime/queries/_typescript/highlights.scm+3−3
@@ -39,11 +39,11 @@
39 (identifier) @variable.parameter))
39 (identifier) @variable.parameter))
40
40
41; (p?: t)
41; (p?: t)
42−; (p?: t = 1) // Invalid but still posible to hihglight.
42+; (p?: t = 1) // Invalid but still possible to highlight.
43(optional_parameter
43(optional_parameter
44 (identifier) @variable.parameter)
44 (identifier) @variable.parameter)
45
45
46−; (...p?: t) // Invalid but still posible to hihglight.
46+; (...p?: t) // Invalid but still possible to highlight.
47(optional_parameter
47(optional_parameter
48 (rest_pattern
48 (rest_pattern
49 (identifier) @variable.parameter))
49 (identifier) @variable.parameter))
@@ -59,7 +59,7 @@
59 (pair_pattern
59 (pair_pattern
60 value: (identifier) @variable.parameter)))
60 value: (identifier) @variable.parameter)))
61
61
62−; ([ p ]?: t[]) // Invalid but still posible to hihglight.
62+; ([ p ]?: t[]) // Invalid but still possible to highlight.
63(optional_parameter
63(optional_parameter
64 (array_pattern
64 (array_pattern
65 (identifier) @variable.parameter))
65 (identifier) @variable.parameter))
runtime/queries/agda/highlights.scmnew file+124
@@ -0,0 +1,124 @@
1+;; Punctuation
2+[ "." ";" ":"] @punctuation.delimiter
3+[ "(" ")" "{" "}" ] @punctuation.bracket
4+
5+;; Constants
6+(integer) @constant.numeric.integer
7+; (float) @constant.numeric.float
8+(literal) @string
9+
10+;; Pragmas and comments
11+(comment) @comment
12+(pragma) @attribute
13+(macro) @function.macro
14+
15+;; Imports
16+(module_name) @namespace
17+(import_directive (id) @namespace)
18+[(module) (import) (open)] @keyword.control.import
19+
20+;; Types
21+(typed_binding (expr) @type)
22+(record (expr) @type)
23+(data (expr) @type)
24+(signature (expr) @type)
25+(function (rhs (expr) @type))
26+; todo: these are too general. ideally, any nested (atom)
27+; https://github.com/tree-sitter/tree-sitter/issues/880
28+
29+;; Variables
30+(untyped_binding (atom) @variable)
31+(typed_binding (atom) @variable)
32+(field_name) @variable.other.member
33+
34+;; Functions
35+(function_name) @function
36+;(function (lhs
37+; . (atom) @function
38+; (atom) @variable.parameter))
39+; todo: currently fails to parse, upstream tree-sitter bug
40+
41+;; Data
42+[(data_name) (record_name)] @constructor
43+((atom) @constant.builtin.boolean
44+ (#any-of? @constant.builtin.boolean "true" "false" "True" "False"))
45+
46+"Set" @type.builtin
47+
48+; postulate
49+; type_signature
50+; pattern
51+; id
52+; bid
53+; typed_binding
54+; primitive
55+; private
56+; record_signature
57+; record_assignments
58+; field_assignment
59+; module_assignment
60+; renaming
61+; import_directive
62+; lambda
63+; let
64+; instance
65+; generalize
66+; record
67+; fields
68+; syntax
69+; hole_name
70+; data_signature
71+
72+;; Keywords
73+[
74+ "where"
75+ "data"
76+ "rewrite"
77+ "postulate"
78+ "public"
79+ "private"
80+ "tactic"
81+ "Prop"
82+ "quote"
83+ "renaming"
84+ "in"
85+ "hiding"
86+ "constructor"
87+ "abstract"
88+ "let"
89+ "field"
90+ "mutual"
91+ "infix"
92+ "infixl"
93+ "infixr"
94+ "record"
95+ "overlap"
96+ "instance"
97+ "do"
98+] @keyword
99+
100+[
101+ "="
102+] @operator
103+
104+; = | -> : ? \ .. ... λ ∀ →
105+; (_LAMBDA) (_FORALL) (_ARROW)
106+; "coinductive"
107+; "eta-equality"
108+; "field"
109+; "inductive"
110+; "interleaved"
111+; "macro"
112+; "no-eta-equality"
113+; "pattern"
114+; "primitive"
115+; "quoteTerm"
116+; "rewrite"
117+; "syntax"
118+; "unquote"
119+; "unquoteDecl"
120+; "unquoteDef"
121+; "using"
122+; "variable"
123+; "with"
124+
runtime/queries/c/indents.scm+13
@@ -36,3 +36,16 @@
36(parameter_list
36(parameter_list
37 . (parameter_declaration) @anchor
37 . (parameter_declaration) @anchor
38 (#set! "scope" "tail")) @align
38 (#set! "scope" "tail")) @align
39+(argument_list
40+ . (_) @anchor
41+ (#set! "scope" "tail")) @align
42+; These are a bit opinionated since some people just indent binary/ternary expressions spanning multiple lines.
43+; Since they are only triggered when a newline is inserted into an already complete binary/ternary expression,
44+; this should happen rarely, so it is not a big deal either way.
45+; Additionally, adding these queries has the advantage of preventing such continuation lines from being used
46+; as the baseline when the `hybrid` indent heuristic is used (which is desirable since their indentation is so inconsistent).
47+(binary_expression
48+ (#set! "scope" "tail")) @anchor @align
49+(conditional_expression
50+ "?" @anchor
51+ (#set! "scope" "tail")) @align
runtime/queries/janet/highlights.scmnew file+1
@@ -0,0 +1 @@
1+; inherits: clojure
runtime/queries/smali/folds.scmnew file+12
@@ -0,0 +1,12 @@
1+[
2+ (annotation_directive)
3+ (array_data_directive)
4+ (field_definition)
5+ (method_definition)
6+ (packed_switch_directive)
7+ (param_directive)
8+ (parameter_directive)
9+ (sparse_switch_directive)
10+ (subannotation_directive)
11+ (list)
12+] @fold
runtime/queries/smali/highlights.scmnew file+216
@@ -0,0 +1,216 @@
1+; Types
2+
3+(class_identifier
4+ (identifier) @type)
5+
6+(primitive_type) @type.builtin
7+
8+((class_identifier
9+ . (identifier) @_first @type.builtin
10+ (identifier) @type.builtin)
11+ (#match? @_first "^(android|dalvik|java|kotlinx)$"))
12+
13+((class_identifier
14+ . (identifier) @_first @type.builtin
15+ . (identifier) @_second @type.builtin
16+ (identifier) @type.builtin)
17+ (#eq? @_first "com")
18+ (#match? @_second "^(android|google)$"))
19+
20+; Methods
21+
22+(method_definition
23+ (method_signature (method_identifier) @function.method))
24+
25+(expression
26+ (opcode) @_invoke
27+ (body
28+ (full_method_signature
29+ (method_signature (method_identifier) @function.method)))
30+ (#match? @_invoke "^invoke"))
31+
32+(expression
33+ (opcode) @_field_access
34+ (body
35+ (field_identifier) @variable.other.member)
36+ (#match? @_field_access "^[is](get|put)-"))
37+
38+(method_handle
39+ (full_method_signature
40+ (method_signature (method_identifier) @function.method)))
41+
42+(custom_invoke
43+ . (identifier) @function.method
44+ (method_signature (method_identifier) @function.method))
45+
46+(annotation_value
47+ (body
48+ (method_signature (method_identifier) @function.method)))
49+
50+(annotation_value
51+ (body
52+ (full_method_signature
53+ (method_signature (method_identifier) @function.method))))
54+
55+(field_definition
56+ (body
57+ (method_signature (method_identifier) @function.method)))
58+
59+(field_definition
60+ (body
61+ (full_method_signature
62+ (method_signature (method_identifier) @function.method))))
63+
64+((method_identifier) @constructor
65+ (#match? @constructor "^(<init>|<clinit>)$"))
66+
67+"constructor" @constructor
68+
69+; Fields
70+
71+[
72+ (field_identifier)
73+ (annotation_key)
74+] @variable.other.member
75+
76+((field_identifier) @constant
77+ (#match? @constant "^[%u_]*$"))
78+
79+; Variables
80+
81+(variable) @variable.builtin
82+
83+(local_directive
84+ (identifier) @variable)
85+
86+; Parameters
87+
88+(parameter) @variable.parameter
89+(param_identifier) @variable.parameter
90+
91+; Labels
92+
93+[
94+ (label)
95+ (jmp_label)
96+] @label
97+
98+; Operators
99+
100+; (opcode) @keyword.operator
101+
102+((opcode) @keyword.control.return
103+ (#match? @keyword.control.return "^return"))
104+
105+((opcode) @keyword.control.conditional
106+ (#match? @keyword.control.conditional "^if"))
107+
108+((opcode) @keyword.control.conditional
109+ (#match? @keyword.control.conditional "^cmp"))
110+
111+((opcode) @keyword.control.exception
112+ (#match? @keyword.control.exception "^throw"))
113+
114+[
115+ "="
116+ ".."
117+] @operator
118+
119+; Keywords
120+
121+[
122+ ".class"
123+ ".super"
124+ ".implements"
125+ ".field"
126+ ".end field"
127+ ".annotation"
128+ ".end annotation"
129+ ".subannotation"
130+ ".end subannotation"
131+ ".param"
132+ ".end param"
133+ ".parameter"
134+ ".end parameter"
135+ ".local"
136+ ".end local"
137+ ".restart local"
138+ ".registers"
139+ ".packed-switch"
140+ ".end packed-switch"
141+ ".sparse-switch"
142+ ".end sparse-switch"
143+ ".array-data"
144+ ".end array-data"
145+ ".enum"
146+ (prologue_directive)
147+ (epilogue_directive)
148+] @keyword
149+
150+[
151+ ".source"
152+] @keyword.directive
153+
154+[
155+ ".method"
156+ ".end method"
157+] @keyword.function
158+
159+[
160+ ".catch"
161+ ".catchall"
162+] @keyword.control.exception
163+
164+; Literals
165+
166+(string) @string
167+(source_directive (string "\"" _ @string.special.url "\""))
168+(escape_sequence) @constant.character.escape
169+
170+(character) @constant.character
171+
172+"L" @punctuation
173+
174+(line_directive (number) @comment) @comment
175+(".locals" (number) @comment) @comment
176+
177+(number) @constant.numeric.integer
178+
179+[
180+ (float)
181+ (NaN)
182+ (Infinity)
183+] @constant.numeric.float
184+
185+(boolean) @constant.builtin.boolean
186+
187+(null) @constant.builtin
188+
189+; Misc
190+
191+(annotation_visibility) @keyword.storage.modifier
192+
193+(access_modifier) @keyword.storage.type
194+
195+(array_type
196+ "[" @punctuation.special)
197+
198+["{" "}"] @punctuation.bracket
199+
200+["(" ")"] @punctuation.bracket
201+
202+[
203+ "->"
204+ ","
205+ ":"
206+ ";"
207+ "@"
208+ "/"
209+] @punctuation.delimiter
210+
211+; Comments
212+
213+(comment) @comment
214+
215+(class_definition
216+ (comment) @comment.block.documentation)
runtime/queries/smali/indents.scmnew file+31
@@ -0,0 +1,31 @@
1+[
2+ (annotation_directive)
3+ (array_data_directive)
4+ (field_definition)
5+ (method_definition)
6+ (packed_switch_directive)
7+ (param_directive)
8+ (parameter_directive)
9+ (sparse_switch_directive)
10+ (subannotation_directive)
11+ (list)
12+ "\{"
13+] @indent
14+
15+[
16+ ".end annotation"
17+ ".end array-data"
18+ ".end field"
19+ ".end method"
20+ ".end packed-switch"
21+ ".end param"
22+ ".end parameter"
23+ ".end sparse-switch"
24+ ".end subannotation"
25+ "\}"
26+] @outdent
27+
28+[
29+ (ERROR)
30+ (comment)
31+] @indent.always
runtime/queries/smali/injections.scmnew file+2
@@ -0,0 +1,2 @@
1+((comment) @injection.content
2+ (#set! injection.language "comment"))
runtime/queries/smali/locals.scmnew file+42
@@ -0,0 +1,42 @@
1+[
2+ (class_directive)
3+ (expression)
4+ (annotation_directive)
5+ (array_data_directive)
6+ (method_definition)
7+ (packed_switch_directive)
8+ (sparse_switch_directive)
9+ (subannotation_directive)
10+] @local.scope
11+
12+[
13+ (identifier)
14+ (class_identifier)
15+ (label)
16+ (jmp_label)
17+] @local.reference
18+
19+(enum_reference
20+ (field_identifier) @local.definition)
21+
22+((field_definition
23+ (access_modifiers) @_mod
24+ (field_identifier) @local.definition)
25+ (#eq? @_mod "enum"))
26+
27+(field_definition
28+ (field_identifier) @local.definition
29+ (field_type) @local.definition)
30+
31+(annotation_key) @local.definition
32+
33+(method_definition
34+ (method_signature (method_identifier) @local.definition))
35+
36+(param_identifier) @local.definition
37+
38+(annotation_directive
39+ (class_identifier) @local.definition)
40+
41+(class_directive
42+ (class_identifier) @local.definition)
runtime/queries/yuck/highlights.scm+1−1
@@ -101,7 +101,7 @@
101(list
101(list
102 (symbol) @tag)
102 (symbol) @tag)
103
103
104−; Other stuff that has not been catched by the previous queries yet
104+; Other stuff that has not been caught by the previous queries yet
105
105
106(ident) @variable
106(ident) @variable
107(index) @variable
107(index) @variable
runtime/themes/darcula.toml+1−1
@@ -15,7 +15,7 @@
15"ui.statusline.select" = { bg = "orange", fg = "grey01" }
15"ui.statusline.select" = { bg = "orange", fg = "grey01" }
16"ui.help" = { fg = "grey04", bg = "grey01" }
16"ui.help" = { fg = "grey04", bg = "grey01" }
17"ui.cursor" = { fg = "grey04", modifiers = ["reversed"] }
17"ui.cursor" = { fg = "grey04", modifiers = ["reversed"] }
18−"ui.cursor.primary" = { fg = "grey05", modifiers = ["reversed"] }
18+"ui.cursor.primary" = { fg = "grey02", bg = "grey05" }
19"ui.cursor.match" = { fg = "white", modifiers = ["underlined"] }
19"ui.cursor.match" = { fg = "white", modifiers = ["underlined"] }
20"ui.cursorline.primary" = { bg = "grey02" }
20"ui.cursorline.primary" = { bg = "grey02" }
21"ui.cursorline.secondary" = { bg = "grey02" }
21"ui.cursorline.secondary" = { bg = "grey02" }
runtime/themes/horizon-dark.tomlnew file+80
@@ -0,0 +1,80 @@
1+# Syntax
2+string = "orange"
3+constant = "purple"
4+"constant.numeric" = "orange"
5+"constant.builtin" = "orange"
6+variable = "red"
7+comment = "light-gray"
8+special = "purple"
9+"punctuation.bracket" = "purple"
10+keyword = "purple"
11+function = "blue"
12+label = "orange"
13+type = "orange"
14+namespace = "orange"
15+
16+# User Interface
17+"ui.background" = { bg = "bg", fg = "gray" }
18+"ui.background.separator" = "light-gray"
19+"ui.text" = "white"
20+"ui.text.focus" = "green"
21+"ui.text.inactive" = "selection"
22+"ui.text.info" = "orange"
23+"ui.cursor" = { modifiers = ["reversed"] }
24+"ui.cursor.primary" = { modifiers = ["reversed"] }
25+"ui.cursor.match" = { bg = "selection" }
26+"ui.gutter" = "gray"
27+"ui.gutter.selected" = "light-gray"
28+"ui.selection" = { bg = "selection" }
29+"ui.virtual.indent-guide" = { fg = "gray"}
30+"ui.virtual.whitespace" = { fg = "light-gray"}
31+"ui.statusline" = { bg = "dark-bg", fg = "light-gray" }
32+"ui.popup" = { bg = "dark-bg", fg = "orange" }
33+"ui.help" = { bg = "dark-bg", fg = "orange" }
34+"ui.menu" = { bg = "dark-bg", fg = "light-gray" }
35+"ui.menu.selected" = { bg = "selection", fg = "orange" }
36+"ui.window" = "selection"
37+"ui.bufferline" = { bg = "dark-bg", fg = "light-gray" }
38+"ui.bufferline.active" = { bg = "dark-bg", fg = "orange" }
39+
40+# Diagnostics
41+"diagnostic" = { underline = { style = "curl" } }
42+"diagnostic.hint" = { underline = { color = "green", style = "curl" } }
43+"diagnostic.info" = { underline = { color = "blue", style = "curl" } }
44+"diagnostics.error" = { underline = { color = "red", style = "curl"} }
45+"diagnostics.warning" = { underline = { color = "orange", style = "curl"} }
46+
47+warning = { fg = "orange", modifiers = ["bold"] }
48+error = { fg = "red", modifiers = ["bold"] }
49+info = { fg = "blue", modifiers = ["bold"] }
50+hint = { fg = "green", modifiers = ["bold"] }
51+
52+# Markup
53+"markup.heading" = "pink"
54+"markup.list" = "light-gray"
55+"markup.list.checked" = "green"
56+"markup.list.unchecked" = "light-gray"
57+"markup.bold" = { modifiers = ["bold"] }
58+"markup.italic" = { modifiers = ["italic"] }
59+"markup.strikethrough" = { modifiers = ["crossed_out"] }
60+"markup.link" = "blue"
61+"markup.quote" = "orange"
62+"markup.raw" = "orange"
63+
64+"diff.plus" = "green"
65+"diff.minus" = "red"
66+"diff.delta" = "orange"
67+
68+[palette]
69+white = "#D5D8DA"
70+gray = "#2E303E"
71+light-gray = "#6C6F93"
72+bg = "#1C1E26"
73+dark-bg = "#16161C"
74+pink = "#EE64AE"
75+selection = "#353747"
76+green = "#27D796"
77+orange = "#FAB795"
78+purple = "#B877DB"
79+red = "#E95678"
80+blue = "#25B2BC"
runtime/themes/licenses/modus_vivendi.LICENSEnew file+5
@@ -0,0 +1,5 @@
1+Copyright (C) 2020-2023 Free Software Foundation, Inc.
2+
3+Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, with the Front-Cover Texts being “A GNU Manual,” and with the Back-Cover Texts as in (a) below. A copy of the license is included in the section entitled “GNU Free Documentation License.”
4+
5+(a) The FSF’s Back-Cover Text is: “You have the freedom to copy and modify this GNU manual.”
runtime/themes/licenses/modus_vivendi_deuteranopia.LICENSEnew file+5
@@ -0,0 +1,5 @@
1+Copyright (C) 2020-2023 Free Software Foundation, Inc.
2+
3+Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, with the Front-Cover Texts being “A GNU Manual,” and with the Back-Cover Texts as in (a) below. A copy of the license is included in the section entitled “GNU Free Documentation License.”
4+
5+(a) The FSF’s Back-Cover Text is: “You have the freedom to copy and modify this GNU manual.”
runtime/themes/licenses/modus_vivendi_tinted.LICENSEnew file+5
@@ -0,0 +1,5 @@
1+Copyright (C) 2020-2023 Free Software Foundation, Inc.
2+
3+Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, with the Front-Cover Texts being “A GNU Manual,” and with the Back-Cover Texts as in (a) below. A copy of the license is included in the section entitled “GNU Free Documentation License.”
4+
5+(a) The FSF’s Back-Cover Text is: “You have the freedom to copy and modify this GNU manual.”
runtime/themes/licenses/modus_vivendi_tritanopia.LICENSEnew file+5
@@ -0,0 +1,5 @@
1+Copyright (C) 2020-2023 Free Software Foundation, Inc.
2+
3+Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, with the Front-Cover Texts being “A GNU Manual,” and with the Back-Cover Texts as in (a) below. A copy of the license is included in the section entitled “GNU Free Documentation License.”
4+
5+(a) The FSF’s Back-Cover Text is: “You have the freedom to copy and modify this GNU manual.”
runtime/themes/modus_vivendi.tomlnew file+242
@@ -0,0 +1,242 @@
1+# Author: Matous Dzivjak <matousdzivjak@gmail.com>
2+# Adapted from https://protesilaos.com/emacs/modus-themes, by Protesilaos Stavrou
3+# Source: https://github.com/protesilaos/modus-themes/blob/main/modus-vivendi-theme.el
4+# Version 4.3.0
5+
6+# Syntax highlighting
7+# -------------------
8+"type" = "cyan-cooler"
9+"constructor" = "cyan-cooler"
10+
11+"constant" = "blue-cooler"
12+"constant.character.escape" = "magenta"
13+
14+"string" = "blue-warmer"
15+"string.regexp" = "magenta-faint"
16+"string.special" = "blue-faint" # used for colors in CSS
17+
18+"comment" = "fg-dim"
19+
20+"variable.parameter" = "cyan"
21+"variable.builtin" = "magenta-cooler"
22+"label" = "fg-dim" # used for language in markdown code blocks
23+"keyword" = "magenta-cooler"
24+"keyword.directive" = "red-cooler"
25+"function" = "magenta"
26+"function.macro" = "magenta-warmer"
27+
28+punctuation = "fg-dim"
29+"tag" = "magenta"
30+"attribute" = "cyan-cooler"
31+"namespace" = "blue-cooler"
32+"special" = "red-cooler"
33+
34+"markup.heading.marker" = "fg-dim"
35+"markup.heading.1" = { fg = "fg-main", modifiers = ["bold"] }
36+"markup.heading.2" = { fg = "yellow-faint", modifiers = ["bold"] }
37+"markup.heading.3" = { fg = "fg-alt", modifiers = ["bold"] }
38+"markup.heading.4" = { fg = "magenta", modifiers = ["bold"] }
39+"markup.heading.5" = { fg = "green-faint", modifiers = ["bold"] }
40+"markup.heading.6" = { fg = "red-faint", modifiers = ["bold"] }
41+"markup.list" = "fg-dim"
42+"markup.list.checked" = { fg = "yellow-warmer" }
43+"markup.list.unchecked" = { fg = "yellow-warmer" }
44+"markup.bold" = { modifiers = ["bold"] }
45+"markup.italic" = { modifiers = ["italic"] }
46+"markup.strikethrough" = { modifiers = ["crossed_out"] }
47+"markup.link.url" = { fg = "cyan" }
48+"markup.link.text" = { fg = "blue-warmer", modifiers = ["underlined"] }
49+"markup.raw.block" = { bg = "bg-dim" }
50+"markup.raw.inline" = { fg = "green-cooler" }
51+
52+"diff.plus" = { fg = "fg-added", bg = "bg-added" }
53+"diff.plus.gutter" = "green-intense"
54+"diff.minus" = { fg = "fg-removed", bg = "bg-removed" }
55+"diff.minus.gutter" = "red-intense"
56+"diff.delta" = { fg = "fg-changed", bg = "bg-changed" }
57+"diff.delta.gutter" = "yellow-intense"
58+
59+# User Interface
60+# --------------
61+
62+"ui.background" = { bg = "bg-main" }
63+
64+"ui.linenr" = { fg = "fg-dim", bg = "bg-dim" }
65+"ui.linenr.selected" = { fg = "fg-main", bg = "bg-active" }
66+
67+"ui.statusline" = { fg = "fg-mode-line-active", bg = "bg-mode-line-active" }
68+"ui.statusline.inactive" = { fg = "fg-mode-line-inactive", bg = "bg-mode-line-inactive" }
69+"ui.statusline.normal" = { fg = "blue-warmer" }
70+"ui.statusline.insert" = { fg = "green-warmer" }
71+"ui.statusline.select" = { fg = "magenta-warmer" }
72+
73+"ui.bufferline" = { bg = "bg-tab-bar" }
74+"ui.bufferline.active" = { bg = "bg-tab-current", fg = "fg-mode-line-active" }
75+
76+"ui.popup" = { fg = "fg-main", bg = "bg-dim" }
77+"ui.window" = { fg = "fg-dim" }
78+"ui.help" = { fg = "fg-main", bg = "bg-dim" }
79+"ui.gutter" = { bg = "bg-dim" }
80+"ui.text" = "fg-main"
81+"ui.text.focus" = { fg = "fg-main", bg = "bg-completion", modifiers = ["bold"] }
82+"ui.text.inactive" = { fg = "fg-dim" }
83+"ui.virtual" = "bg-active"
84+"ui.virtual.ruler" = { bg = "bg-dim" }
85+"ui.virtual.inlay-hint" = { fg = "fg-dim", modifiers = ["italic"] }
86+
87+"ui.selection" = { fg = "fg-main", bg = "bg-inactive" }
88+"ui.selection.primary" = { fg = "fg-main", bg = "bg-active" }
89+
90+"ui.cursor" = { fg = "bg-main", bg = "fg-main" }
91+"ui.cursor.primary" = { fg = "bg-main", bg = "fg-dim" }
92+"ui.cursor.match" = { bg = "bg-paren-match" }
93+"ui.cursorline.primary" = { bg = "bg-hl-line" }
94+
95+"ui.highlight" = { bg = "bg-hl-line" }
96+
97+"ui.menu" = { fg = "fg-main", bg = "bg-dim" }
98+"ui.menu.selected" = { fg = "fg-main", bg = "bg-completion", modifiers = ["bold"] }
99+"ui.menu.scroll" = { fg = "fg-dim", bg = "bg-cyan-intense" }
100+
101+"diagnostic.error" = { underline = { color = "red-intense", style = "curl" } }
102+"diagnostic.warning" = { underline = { color = "yellow-intense", style = "curl" } }
103+"diagnostic.info" = { underline = { color = "cyan-intense", style = "curl" } }
104+"diagnostic.hint" = { underline = { color = "blue-intense", style = "curl" } }
105+
106+error = "red"
107+warning = "yellow-warmer"
108+info = "cyan-cooler"
109+hint = "blue-cooler"
110+
111+[palette]
112+# Basic values
113+bg-main = "#000000"
114+bg-dim = "#1e1e1e"
115+fg-main = "#ffffff"
116+fg-dim = "#989898"
117+fg-alt = "#c6daff"
118+bg-active = "#535353"
119+bg-inactive = "#303030"
120+
121+# Common accent foregrounds
122+red = "#ff5f59"
123+red-warmer = "#ff6b55"
124+red-cooler = "#ff7f9f"
125+red-faint = "#ff9580"
126+red-intense = "#ff5f5f"
127+green = "#44bc44"
128+green-warmer = "#70b900"
129+green-cooler = "#00c06f"
130+green-faint = "#88ca9f"
131+green-intense = "#44df44"
132+yellow = "#d0bc00"
133+yellow-warmer = "#fec43f"
134+yellow-cooler = "#dfaf7a"
135+yellow-faint = "#d2b580"
136+yellow-intense = "#efef00"
137+blue = "#2fafff"
138+blue-warmer = "#79a8ff"
139+blue-cooler = "#00bcff"
140+blue-faint = "#82b0ec"
141+blue-intense = "#338fff"
142+magenta = "#feacd0"
143+magenta-warmer = "#f78fe7"
144+magenta-cooler = "#b6a0ff"
145+magenta-faint = "#caa6df"
146+magenta-intense = "#ff66ff"
147+cyan = "#00d3d0"
148+cyan-warmer = "#4ae2f0"
149+cyan-cooler = "#6ae4b9"
150+cyan-faint = "#9ac8e0"
151+cyan-intense = "#00eff0"
152+
153+# Uncommon accent foregrounds
154+rust = "#db7b5f"
155+gold = "#c0965b"
156+olive = "#9cbd6f"
157+slate = "#76afbf"
158+indigo = "#9099d9"
159+maroon = "#cf7fa7"
160+pink = "#d09dc0"
161+
162+# Common accent backgrounds
163+bg-red-intense = "#9d1f1f"
164+bg-green-intense = "#2f822f"
165+bg-yellow-intense = "#7a6100"
166+bg-blue-intense = "#1640b0"
167+bg-magenta-intense = "#7030af"
168+bg-cyan-intense = "#2266ae"
169+
170+bg-red-subtle = "#620f2a"
171+bg-green-subtle = "#00422a"
172+bg-yellow-subtle = "#4a4000"
173+bg-blue-subtle = "#242679"
174+bg-magenta-subtle = "#552f5f"
175+bg-cyan-subtle = "#004065"
176+
177+bg-red-nuanced = "#2c0614"
178+bg-green-nuanced = "#001904"
179+bg-yellow-nuanced = "#221000"
180+bg-blue-nuanced = "#0f0e39"
181+bg-magenta-nuanced = "#230631"
182+bg-cyan-nuanced = "#041529"
183+
184+# Uncommon accent backgrounds
185+bg-ochre = "#442c2f"
186+bg-lavender = "#38325c"
187+bg-sage = "#0f3d30"
188+
189+# Special purpose
190+bg-completion = "#2f447f"
191+bg-hover = "#45605e"
192+bg-hover-secondary = "#654a39"
193+bg-hl-line = "#2f3849"
194+bg-region = "#5a5a5a"
195+fg-region = "#ffffff"
196+
197+bg-char-0 = "#0050af"
198+bg-char-1 = "#7f1f7f"
199+bg-char-2 = "#625a00"
200+
201+bg-mode-line-active = "#505050"
202+fg-mode-line-active = "#ffffff"
203+border-mode-line-active = "#959595"
204+bg-mode-line-inactive = "#2d2d2d"
205+fg-mode-line-inactive = "#969696"
206+border-mode-line-inactive = "#606060"
207+
208+modeline-err = "#ffa9bf"
209+modeline-warning = "#dfcf43"
210+modeline-info = "#9fefff"
211+
212+bg-tab-bar = "#313131"
213+bg-tab-current = "#000000"
214+bg-tab-other = "#545454"
215+
216+# Diffs
217+bg-added = "#00381f"
218+bg-added-faint = "#002910"
219+bg-added-refine = "#034f2f"
220+bg-added-fringe = "#237f3f"
221+fg-added = "#a0e0a0"
222+fg-added-intense = "#80e080"
223+
224+bg-changed = "#363300"
225+bg-changed-faint = "#2a1f00"
226+bg-changed-refine = "#4a4a00"
227+bg-changed-fringe = "#8a7a00"
228+fg-changed = "#efef80"
229+fg-changed-intense = "#c0b05f"
230+
231+bg-removed = "#4f1119"
232+bg-removed-faint = "#380a0f"
233+bg-removed-refine = "#781a1f"
234+bg-removed-fringe = "#b81a1f"
235+fg-removed = "#ffbfbf"
236+fg-removed-intense = "#ff9095"
237+
238+bg-diff-context = "#1a1a1a"
239+
240+# Paren match
241+bg-paren-match = "#2f7f9f"
242+tg-paren-expression = "#453040"
runtime/themes/modus_vivendi_deuteranopia.tomlnew file+158
@@ -0,0 +1,158 @@
1+# Author: Matous Dzivjak <matousdzivjak@gmail.com>
2+# Adapted from https://protesilaos.com/emacs/modus-themes, by Protesilaos Stavrou
3+# Source: https://github.com/protesilaos/modus-themes/blob/main/modus-vivendi-deuteranopia-theme.el
4+# Version 4.3.0
5+
6+inherits = "modus_vivendi"
7+
8+"constant.character.escape" = "magenta"
9+"comment" = "yellow-cooler"
10+"function" = "magenta"
11+"tag" = "magenta"
12+
13+"ui.cursor" = { fg = "bg-main", bg = "blue-intense" }
14+"ui.cursor.primary.normal" = { fg = "bg-main", bg = "blue-intense" }
15+
16+"diagnostic.error" = { underline = { color = "yellow-intense", style = "curl" } }
17+"diagnostic.warning" = { underline = { color = "magenta-faint", style = "curl" } }
18+"diagnostic.info" = { underline = { color = "cyan", style = "curl" } }
19+"diagnostic.hint" = { underline = { color = "blue", style = "curl" } }
20+
21+error = "yellow-intense"
22+warning = "magenta-faint"
23+info = "cyan"
24+hint = "blue"
25+
26+[palette]
27+# Basic values
28+bg-main = "#000000"
29+bg-dim = "#1e1e1e"
30+fg-main = "#ffffff"
31+fg-dim = "#989898"
32+fg-alt = "#c6daff"
33+bg-active = "#535353"
34+bg-inactive = "#303030"
35+border = "#646464"
36+
37+# Common accent foregrounds
38+red = "#ff5f59"
39+red-warmer = "#ff6b55"
40+red-cooler = "#ff7f9f"
41+red-faint = "#ff9580"
42+red-intense = "#ff5f5f"
43+green = "#44bc44"
44+green-warmer = "#70b900"
45+green-cooler = "#00c06f"
46+green-faint = "#88ca9f"
47+green-intense = "#44df44"
48+yellow = "#cabf00"
49+yellow-warmer = "#ffa00f"
50+yellow-cooler = "#d8af7a"
51+yellow-faint = "#d2b580"
52+yellow-intense = "#efef00"
53+blue = "#2fafff"
54+blue-warmer = "#79a8ff"
55+blue-cooler = "#00bcff"
56+blue-faint = "#82b0ec"
57+blue-intense = "#338fff"
58+magenta = "#feacd0"
59+magenta-warmer = "#f78fe7"
60+magenta-cooler = "#b6a0ff"
61+magenta-faint = "#caa6df"
62+magenta-intense = "#ff66ff"
63+cyan = "#00d3d0"
64+cyan-warmer = "#4ae2f0"
65+cyan-cooler = "#6ae4b9"
66+cyan-faint = "#9ac8e0"
67+cyan-intense = "#00eff0"
68+
69+# Uncommon accent foregrounds
70+rust = "#db7b5f"
71+gold = "#c0965b"
72+olive = "#9cbd6f"
73+slate = "#76afbf"
74+indigo = "#9099d9"
75+maroon = "#cf7fa7"
76+pink = "#d09dc0"
77+
78+# Common accent backgrounds
79+bg-red-intense = "#9d1f1f"
80+bg-green-intense = "#2f822f"
81+bg-yellow-intense = "#7a6100"
82+bg-blue-intense = "#1640b0"
83+bg-magenta-intense = "#7030af"
84+bg-cyan-intense = "#2266ae"
85+
86+bg-red-subtle = "#620f2a"
87+bg-green-subtle = "#00422a"
88+bg-yellow-subtle = "#4a4000"
89+bg-blue-subtle = "#242679"
90+bg-magenta-subtle = "#552f5f"
91+bg-cyan-subtle = "#004065"
92+
93+bg-red-nuanced = "#2c0614"
94+bg-green-nuanced = "#001904"
95+bg-yellow-nuanced = "#221000"
96+bg-blue-nuanced = "#0f0e39"
97+bg-magenta-nuanced = "#230631"
98+bg-cyan-nuanced = "#041529"
99+
100+# Uncommon accent backgrounds
101+bg-ochre = "#442c2f"
102+bg-lavender = "#38325c"
103+bg-sage = "#0f3d30"
104+
105+# Special purpose
106+bg-completion = "#2f447f"
107+bg-hover = "#45605e"
108+bg-hover-secondary = "#654a39"
109+bg-hl-line = "#2f3849"
110+bg-region = "#5a5a5a"
111+fg-region = "#ffffff"
112+
113+bg-char-0 = "#0050af"
114+bg-char-1 = "#7f1f7f"
115+bg-char-2 = "#625a00"
116+
117+bg-mode-line-active = "#2a2a6a"
118+fg-mode-line-active = "#f0f0f0"
119+border-mode-line-active = "#8080a7"
120+bg-mode-line-inactive = "#2d2d2d"
121+fg-mode-line-inactive = "#969696"
122+border-mode-line-inactive = "#606060"
123+
124+modeline-err = "#e5bf00"
125+modeline-warning = "#c0cf35"
126+modeline-info = "#abeadf"
127+
128+bg-tab-bar = "#313131"
129+bg-tab-current = "#000000"
130+bg-tab-other = "#545454"
131+
132+# Diffs
133+bg-added = "#003066"
134+bg-added-faint = "#001a4f"
135+bg-added-refine = "#0f4a77"
136+bg-added-fringe = "#006fff"
137+fg-added = "#c4d5ff"
138+fg-added-intense = "#8080ff"
139+
140+bg-changed = "#2f123f"
141+bg-changed-faint = "#1f022f"
142+bg-changed-refine = "#3f325f"
143+bg-changed-fringe = "#7f55a0"
144+fg-changed = "#e3cfff"
145+fg-changed-intense = "#cf9fe2"
146+
147+bg-removed = "#3d3d00"
148+bg-removed-faint = "#281f00"
149+bg-removed-refine = "#555500"
150+bg-removed-fringe = "#d0c03f"
151+fg-removed = "#d4d48f"
152+fg-removed-intense = "#d0b05f"
153+
154+bg-diff-context = "#1a1a1a"
155+
156+# Paren match
157+bg-paren-match = "#2f7f9f"
158+bg-paren-expression = "#453040"
runtime/themes/modus_vivendi_tinted.tomlnew file+144
@@ -0,0 +1,144 @@
1+# Author: Matous Dzivjak <matousdzivjak@gmail.com>
2+# Adapted from: https://protesilaos.com/emacs/modus-themes, by Protesilaos Stavrou
3+# Source: https://github.com/protesilaos/modus-themes/blob/main/modus-vivendi-tinted-theme.el
4+# Version 4.3.0
5+
6+inherits = "modus_vivendi"
7+
8+"comment" = "red-faint"
9+
10+"ui.cursor.primary" = { fg = "bg-main", bg = "red" }
11+
12+[palette]
13+# Basic values
14+bg-main = "#0d0e1c"
15+bg-dim = "#1d2235"
16+fg-main = "#ffffff"
17+fg-dim = "#989898"
18+fg-alt = "#c6daff"
19+bg-active = "#4a4f69"
20+bg-inactive = "#2b3045"
21+border = "#61647a"
22+
23+# Common accent foregrounds
24+red = "#ff5f59"
25+red-warmer = "#ff6b55"
26+red-cooler = "#ff7f9f"
27+red-faint = "#ff9f80"
28+red-intense = "#ff5f5f"
29+green = "#44bc44"
30+green-warmer = "#70b900"
31+green-cooler = "#00c06f"
32+green-faint = "#88ca9f"
33+green-intense = "#44df44"
34+yellow = "#d0bc00"
35+yellow-warmer = "#fec43f"
36+yellow-cooler = "#dfaf7a"
37+yellow-faint = "#d2b580"
38+yellow-intense = "#efef00"
39+blue = "#2fafff"
40+blue-warmer = "#79a8ff"
41+blue-cooler = "#00bcff"
42+blue-faint = "#82b0ec"
43+blue-intense = "#338fff"
44+magenta = "#feacd0"
45+magenta-warmer = "#f78fe7"
46+magenta-cooler = "#b6a0ff"
47+magenta-faint = "#caa6df"
48+magenta-intense = "#ff66ff"
49+cyan = "#00d3d0"
50+cyan-warmer = "#4ae2f0"
51+cyan-cooler = "#6ae4b9"
52+cyan-faint = "#9ac8e0"
53+cyan-intense = "#00eff0"
54+
55+# Uncommon accent foregrounds
56+rust = "#db7b5f"
57+gold = "#c0965b"
58+olive = "#9cbd6f"
59+slate = "#76afbf"
60+indigo = "#9099d9"
61+maroon = "#cf7fa7"
62+pink = "#d09dc0"
63+
64+# Common accent backgrounds
65+bg-red-intense = "#9d1f1f"
66+bg-green-intense = "#2f822f"
67+bg-yellow-intense = "#7a6100"
68+bg-blue-intense = "#1640b0"
69+bg-magenta-intense = "#7030af"
70+bg-cyan-intense = "#2266ae"
71+
72+bg-red-subtle = "#620f2a"
73+bg-green-subtle = "#00422a"
74+bg-yellow-subtle = "#4a4000"
75+bg-blue-subtle = "#242679"
76+bg-magenta-subtle = "#552f5f"
77+bg-cyan-subtle = "#004065"
78+
79+bg-red-nuanced = "#350f14"
80+bg-green-nuanced = "#002718"
81+bg-yellow-nuanced = "#2c1f00"
82+bg-blue-nuanced = "#131c4d"
83+bg-magenta-nuanced = "#2f133f"
84+bg-cyan-nuanced = "#04253f"
85+
86+# Special purpose
87+bg-completion = "#483d8a"
88+bg-hover = "#45605e"
89+bg-hover-secondary = "#654a39"
90+bg-hl-line = "#303a6f"
91+bg-region = "#555a66"
92+fg-region = "#ffffff"
93+
94+bg-char-0 = "#0050af"
95+bg-char-1 = "#7f1f7f"
96+bg-char-2 = "#625a00"
97+
98+bg-mode-line-active = "#484d67"
99+fg-mode-line-active = "#ffffff"
100+border-mode-line-active = "#979797"
101+bg-mode-line-inactive = "#292d48"
102+fg-mode-line-inactive = "#969696"
103+border-mode-line-inactive = "#606270"
104+
105+modeline-err = "#ffa9bf"
106+modeline-warning = "#dfcf43"
107+modeline-info = "#9fefff"
108+
109+bg-tab-bar = "#2c3045"
110+bg-tab-current = "#0d0e1c"
111+bg-tab-other = "#4a4f6a"
112+
113+# Diffs
114+bg-added = "#003a2f"
115+bg-added-faint = "#002922"
116+bg-added-refine = "#035542"
117+bg-added-fringe = "#23884f"
118+fg-added = "#a0e0a0"
119+fg-added-intense = "#80e080"
120+
121+bg-changed = "#363300"
122+bg-changed-faint = "#2a1f00"
123+bg-changed-refine = "#4a4a00"
124+bg-changed-fringe = "#8f7a30"
125+fg-changed = "#efef80"
126+fg-changed-intense = "#c0b05f"
127+
128+bg-removed = "#4f1127"
129+bg-removed-faint = "#380a19"
130+bg-removed-refine = "#781a3a"
131+bg-removed-fringe = "#b81a26"
132+fg-removed = "#ffbfbf"
133+fg-removed-intense = "#ff9095"
134+
135+bg-diff-context = "#1a1f30"
136+
137+# Uncommon accent backgrounds
138+bg-ochre = "#442c2f"
139+bg-lavender = "#38325c"
140+bg-sage = "#0f3d30"
141+
142+# Paren match
143+bg-paren-match = "#2f7f9f"
144+bg-paren-expression = "#453040"
runtime/themes/modus_vivendi_tritanopia.tomlnew file+186
@@ -0,0 +1,186 @@
1+# Author: Matous Dzivjak <matousdzivjak@gmail.com>
2+# Adapted from https://protesilaos.com/emacs/modus-themes, by Protesilaos Stavrou
3+# Source: https://github.com/protesilaos/modus-themes/blob/main/modus-vivendi-tritanopia-theme.el
4+# Version 4.3.0
5+
6+inherits = "modus_vivendi"
7+
8+"type" = "blue-warmer"
9+"constructor" = "blue-warmer"
10+"constant" = "green-cooler"
11+"constant.character.escape" = "red-cooler"
12+"string" = "cyan"
13+"comment" = "red-faint"
14+"variable.parameter" = "cyan-cooler"
15+"keyword" = "red-cooler"
16+"keyword.directive" = "red-warmer"
17+"function" = "cyan-warmer"
18+"function.macro" = "magenta"
19+"tag" = "red-cooler"
20+
21+"markup.heading.2" = { fg = "red-faint", modifiers = ["bold"] }
22+"markup.heading.3" = { fg = "cyan-faint", modifiers = ["bold"] }
23+"markup.heading.6" = { fg = "magenta-faint", modifiers = ["bold"] }
24+"markup.link.url" = { fg = "cyan-cooler" }
25+"markup.link.text" = { fg = "cyan", modifiers = ["underlined"] }
26+
27+"ui.cursor" = { fg = "bg-main", bg = "red-intense" }
28+"ui.cursor.primary.normal" = { fg = "bg-main", bg = "red-intense" }
29+
30+"diagnostic.error" = { underline = { color = "red-warmer", style = "curl" } }
31+"diagnostic.warning" = { underline = { color = "magenta", style = "curl" } }
32+"diagnostic.info" = { underline = { color = "cyan", style = "curl" } }
33+"diagnostic.hint" = { underline = { color = "blue", style = "curl" } }
34+
35+error = "red-warmer"
36+warning = "magenta"
37+info = "cyan"
38+hint = "blue"
39+
40+[palette]
41+# Basic values
42+bg-main = "#000000"
43+bg-dim = "#1e1e1e"
44+fg-main = "#ffffff"
45+fg-dim = "#989898"
46+fg-alt = "#c6daff"
47+bg-active = "#535353"
48+bg-inactive = "#303030"
49+border = "#646464"
50+
51+# Common accent foregrounds
52+red = "#ff5f59"
53+red-warmer = "#ff6740"
54+red-cooler = "#ff6f9f"
55+red-faint = "#ff9070"
56+red-intense = "#ff5f5f"
57+green = "#44bc44"
58+green-warmer = "#70b900"
59+green-cooler = "#00c06f"
60+green-faint = "#88ca9f"
61+green-intense = "#44df44"
62+yellow = "#cabf00"
63+yellow-warmer = "#ffa00f"
64+yellow-cooler = "#d8af7a"
65+yellow-faint = "#d2b580"
66+yellow-intense = "#efef00"
67+blue = "#2fafff"
68+blue-warmer = "#79a8ff"
69+blue-cooler = "#00bcff"
70+blue-faint = "#82b0ec"
71+blue-intense = "#338fff"
72+magenta = "#feacd0"
73+magenta-warmer = "#f78fe7"
74+magenta-cooler = "#b6a0ff"
75+magenta-faint = "#caa6df"
76+magenta-intense = "#ef7fff"
77+cyan = "#00d3d0"
78+cyan-warmer = "#4ae2ff"
79+cyan-cooler = "#6ae4b9"
80+cyan-faint = "#7fdbdf"
81+cyan-intense = "#00eff0"
82+
83+# Uncommon accent foregrounds
84+rust = "#db7b5f"
85+gold = "#c0965b"
86+olive = "#9cbd6f"
87+slate = "#76afbf"
88+indigo = "#9099d9"
89+maroon = "#cf7fa7"
90+pink = "#d09dc0"
91+
92+# Common accent backgrounds
93+bg-red-intense = "#9d1f1f"
94+bg-green-intense = "#2f822f"
95+bg-yellow-intense = "#7a6100"
96+bg-blue-intense = "#1640b0"
97+bg-magenta-intense = "#7030af"
98+bg-cyan-intense = "#2266ae"
99+
100+bg-red-subtle = "#620f2a"
101+bg-green-subtle = "#00422a"
102+bg-yellow-subtle = "#4a4000"
103+bg-blue-subtle = "#242679"
104+bg-magenta-subtle = "#552f5f"
105+bg-cyan-subtle = "#004065"
106+
107+bg-red-nuanced = "#2c0614"
108+bg-green-nuanced = "#001904"
109+bg-yellow-nuanced = "#221000"
110+bg-blue-nuanced = "#0f0e39"
111+bg-magenta-nuanced = "#230631"
112+bg-cyan-nuanced = "#041529"
113+
114+# Uncommon accent backgrounds
115+bg-ochre = "#442c2f"
116+bg-lavender = "#38325c"
117+bg-sage = "#0f3d30"
118+
119+# Graphs
120+bg-graph-red-0 = "#b52c2c"
121+bg-graph-red-1 = "#702020"
122+bg-graph-green-0 = "#afd1c0"
123+bg-graph-green-1 = "#607a8f"
124+bg-graph-yellow-0 = "#facfd6"
125+bg-graph-yellow-1 = "#b57b85"
126+bg-graph-blue-0 = "#4f9fdf"
127+bg-graph-blue-1 = "#004559"
128+bg-graph-magenta-0 = "#b6427f"
129+bg-graph-magenta-1 = "#7f506f"
130+bg-graph-cyan-0 = "#57dfea"
131+bg-graph-cyan-1 = "#00808f"
132+
133+# Special purpose
134+bg-completion = "#004253"
135+bg-hover = "#8e3e3b"
136+bg-hover-secondary = "#00405f"
137+bg-hl-line = "#2f3849"
138+bg-region = "#5a5a5a"
139+fg-region = "#ffffff"
140+
141+bg-char-0 = "#922a00"
142+bg-char-1 = "#00709f"
143+bg-char-2 = "#5f3faf"
144+
145+bg-mode-line-active = "#003c52"
146+fg-mode-line-active = "#f0f0f0"
147+border-mode-line-active = "#5f8fb4"
148+bg-mode-line-inactive = "#2d2d2d"
149+fg-mode-line-inactive = "#969696"
150+border-mode-line-inactive = "#606060"
151+
152+modeline-err = "#ff7fbf"
153+modeline-warning = "#df9f93"
154+modeline-info = "#4fcfef"
155+
156+bg-tab-bar = "#313131"
157+bg-tab-current = "#000000"
158+bg-tab-other = "#545454"
159+
160+# Diffs
161+bg-added = "#004254"
162+bg-added-faint = "#003042"
163+bg-added-refine = "#004f7f"
164+bg-added-fringe = "#008fcf"
165+fg-added = "#9fdfdf"
166+fg-added-intense = "#50c0ef"
167+
168+bg-changed = "#2f123f"
169+bg-changed-faint = "#1f022f"
170+bg-changed-refine = "#3f325f"
171+bg-changed-fringe = "#7f55a0"
172+fg-changed = "#e3cfff"
173+fg-changed-intense = "#cf9fe2"
174+
175+bg-removed = "#4f1119"
176+bg-removed-faint = "#380a0f"
177+bg-removed-refine = "#781a1f"
178+bg-removed-fringe = "#b81a1f"
179+fg-removed = "#ffbfbf"
180+fg-removed-intense = "#ff9095"
181+
182+bg-diff-context = "#1a1a1a"
183+
184+# Paren match
185+bg-paren-match = "#2f7f9f"
186+bg-paren-expression = "#453040"
runtime/themes/noctis.toml+25−25
@@ -7,8 +7,8 @@
7
7
8'property' = { fg = "red" } # Regex group names.
8'property' = { fg = "red" } # Regex group names.
9"warning" = { fg ="yellow", modifiers = ["bold"] } # Editor warnings.
9"warning" = { fg ="yellow", modifiers = ["bold"] } # Editor warnings.
10−"error" = { bg = "mid-green", fg = "red", modifiers = ["bold"] } # Editor errors, like mis-typing a command.
10+"error" = { fg = "red", modifiers = ["bold"] } # Editor errors, like mis-typing a command.
11−"info" = { fg = "mid-blue", bg = "mid-green" } # Code diagnostic info in gutter (LSP).
11+"info" = { fg = "mid-blue" } # Code diagnostic info in gutter (LSP).
12# ? Difference between info and hint ?
12# ? Difference between info and hint ?
13"hint" = { fg = "light-green", modifiers = ["bold"] } # Code diagnostics hint in gutter (LSP).
13"hint" = { fg = "light-green", modifiers = ["bold"] } # Code diagnostics hint in gutter (LSP).
14
14
@@ -21,14 +21,14 @@
21# UI ==============================
21# UI ==============================
22# For styling helix itself.
22# For styling helix itself.
23
23
24−'ui.background' = { bg = "dark-green"} # Default background color.
24+'ui.background' = { fg = "autocomp-green", bg = "dark-green" } # Default background color.
25'ui.window' = { fg = "mid-green" } # Window border between splits.
25'ui.window' = { fg = "mid-green" } # Window border between splits.
26
26
27'ui.gutter' = { } # Left gutter for diagnostics and breakpoints.
27'ui.gutter' = { } # Left gutter for diagnostics and breakpoints.
28
28
29'ui.text' = { fg = "white" } # Default text color.
29'ui.text' = { fg = "white" } # Default text color.
30'ui.text.focus' = { fg = "white", bg = "mid-green", modifiers = ["bold"] } # Selection highlight in buffer-picker or file-picker.
30'ui.text.focus' = { fg = "white", bg = "mid-green", modifiers = ["bold"] } # Selection highlight in buffer-picker or file-picker.
31−'ui.text.info' = { } # Info popup contents (space mode menu).
31+'ui.text.info' = { fg = "white" } # Info popup contents (space mode menu).
32
32
33'ui.cursor' = { fg = "dark-green", bg = "white" } # Fallback cursor colour, non-primary cursors when there are multiple (shift-c).
33'ui.cursor' = { fg = "dark-green", bg = "white" } # Fallback cursor colour, non-primary cursors when there are multiple (shift-c).
34'ui.cursor.primary' = { fg = "dark-green", bg = "light-blue" } # The primary cursor when there are multiple (shift-c).
34'ui.cursor.primary' = { fg = "dark-green", bg = "light-blue" } # The primary cursor when there are multiple (shift-c).
@@ -40,8 +40,8 @@
40'ui.selection.primary' = { bg = "autocomp-green" } # The primary selection when there are multiple.
40'ui.selection.primary' = { bg = "autocomp-green" } # The primary selection when there are multiple.
41'ui.cursorline.primary' = { bg = 'mid-green' }
41'ui.cursorline.primary' = { bg = 'mid-green' }
42
42
43−'ui.linenr' = { bg = "dark-green", fg = "gray" } # Line numbers.
43+'ui.linenr' = { fg = "gray" } # Line numbers.
44−'ui.linenr.selected' = { bg = "mid-green", fg = "light-green", modifiers = [ "bold" ] } # Current line number.
44+'ui.linenr.selected' = { fg = "light-green", modifiers = [ "bold" ] } # Current line number.
45
45
46'ui.virtual' = { fg = "mid-green" } # Namespace for additions to the editing area.
46'ui.virtual' = { fg = "mid-green" } # Namespace for additions to the editing area.
47'ui.virtual.ruler' = { bg = "mid-green" } # Vertical rulers (colored columns in editing area).
47'ui.virtual.ruler' = { bg = "mid-green" } # Vertical rulers (colored columns in editing area).
@@ -75,9 +75,9 @@
75#'type.builtin' = { } # Primitive types of the language (string, int, float).
75#'type.builtin' = { } # Primitive types of the language (string, int, float).
76#'type.enum.variant' = { } # A variant of an enum.
76#'type.enum.variant' = { } # A variant of an enum.
77
77
78−'constructor' = { fg = "mid-blue", modifiers = ["bold"]} # Constructor method for a class or struct. And in some cases applies to module names
78+'constructor' = { fg = "light-blue", modifiers = ["bold"]} # Constructor method for a class or struct. And in some cases applies to module names
79
79
80−'constant' = { fg = "light-blue" } # Constant value
80+'constant' = { fg = "light-blue", modifiers = ["bold"] } # Constant value
81'constant.builtin' = { fg = "mid-blue", modifiers = ["bold"] } # Special constants like `true`, `false`, `none`, etc.
81'constant.builtin' = { fg = "mid-blue", modifiers = ["bold"] } # Special constants like `true`, `false`, `none`, etc.
82'constant.builtin.boolean' = { } # True or False.
82'constant.builtin.boolean' = { } # True or False.
83'constant.character' = { fg = "light-green"} # Constant of character type.
83'constant.character' = { fg = "light-green"} # Constant of character type.
@@ -88,21 +88,20 @@
88
88
89'string' = { fg = "light-green" } # String literal.
89'string' = { fg = "light-green" } # String literal.
90'string.regexp' = { } # Regular expression literal.
90'string.regexp' = { } # Regular expression literal.
91−'string.special' = { fg = "light-blue" } # Strings containing a path, URL, etc.
91+'string.special' = { fg = "red" } # Strings containing a path, URL, etc.
92−'string.special.path' = { } # String containing a file path.
92+# 'string.special.path' = { } # String containing a file path.
93−'string.special.url' = { } # String containing a web URL.
93+# 'string.special.url' = { } # String containing a web URL.
94−'string.special.symbol' = { } # Erlang/Elixir atoms, Ruby symbols, Clojure keywords.
94+# 'string.special.symbol' = { } # Erlang/Elixir atoms, Ruby symbols, Clojure keywords.
95
95
96'comment' = { fg = "gray", modifiers = ["italic"] } # This is a comment.
96'comment' = { fg = "gray", modifiers = ["italic"] } # This is a comment.
97'comment.line' = { } # Line comments, like this.
97'comment.line' = { } # Line comments, like this.
98'comment.block' = { } # Block comments, like /* this */ in some languages.
98'comment.block' = { } # Block comments, like /* this */ in some languages.
99'comment.block.documentation' = { } # Doc comments, e.g '///' in rust.
99'comment.block.documentation' = { } # Doc comments, e.g '///' in rust.
100
100
101−'variable' = { fg = "white" } # Variable names.
101+'variable' = { fg = "light-orange" } # Variable names.
102−'variable.builtin' = { } # Language reserved variables: `this`, `self`, `super`, etc.
102+# 'variable.builtin' = { } # Language reserved variables: `this`, `self`, `super`, etc.
103−'variable.parameter' = { } # Function parameters.
103+# 'variable.parameter' = { } # Function parameters.
104−'variable.other.member' = { } # Fields of composite data types (e.g. structs, unions).
104+'variable.other.member' = { fg = "orange" } # Fields of composite data types (e.g. structs, unions).
105−'variable.function' = { } # ?
106
105
107'label' = { fg = "purple" } # Loop labels in rust.
106'label' = { fg = "purple" } # Loop labels in rust.
108
107
@@ -113,17 +112,17 @@
113'keyword' = { fg = "pink", modifiers = ["bold"] } # Language reserved keywords.
112'keyword' = { fg = "pink", modifiers = ["bold"] } # Language reserved keywords.
114'keyword.control' = { fg = "pink", modifiers = ["bold"] } # Control keywords.
113'keyword.control' = { fg = "pink", modifiers = ["bold"] } # Control keywords.
115'keyword.control.conditional' = { fg = "pink", modifiers = ["bold"] } # 'if', 'else', 'elif'.
114'keyword.control.conditional' = { fg = "pink", modifiers = ["bold"] } # 'if', 'else', 'elif'.
116−'keyword.control.repeat' = { } # 'for', 'while', 'loop'.
115+# 'keyword.control.repeat' = { } # 'for', 'while', 'loop'.
117'keyword.control.import' = { fg = "pink", modifiers = ["italic"] } # 'import', 'export' ('use'?).
116'keyword.control.import' = { fg = "pink", modifiers = ["italic"] } # 'import', 'export' ('use'?).
118−'keyword.control.return' = { } # 'return' in most languages.
117+# 'keyword.control.return' = { } # 'return' in most languages.
119'keyword.control.exception' = {fg = "pink", modifiers = ["bold"] } # 'raise' in python.
118'keyword.control.exception' = {fg = "pink", modifiers = ["bold"] } # 'raise' in python.
120−'keyword.operator' = { } # 'or', 'and', 'in'.
119+'keyword.operator' = { fg = "pink" } # 'or', 'and', 'in'.
121'keyword.directive' = { fg = "purple" } # Preprocessor directives (#if in C).
120'keyword.directive' = { fg = "purple" } # Preprocessor directives (#if in C).
122−'keyword.function' = { } # The keyword to define a function: 'def', 'fun', 'fn'.
121+'keyword.function' = { fg= "red" } # The keyword to define a function: 'def', 'fun', 'fn'.
123
122
124'operator' = { fg = "pink", modifiers = ["bold"] } # Logical (&&, ||) and - I assume - Mathematical (+, %) operators
123'operator' = { fg = "pink", modifiers = ["bold"] } # Logical (&&, ||) and - I assume - Mathematical (+, %) operators
125
124
126−'function' = { fg = "dark-blue"}
125+'function' = { fg = "mid-blue"}
127'function.builtin' = { fg = "dark-blue" }
126'function.builtin' = { fg = "dark-blue" }
128'function.method' = { fg = "dark-blue" } # Class / Struct methods.
127'function.method' = { fg = "dark-blue" } # Class / Struct methods.
129'function.macro' = { fg = "purple" } # Like macros in rust.
128'function.macro' = { fg = "purple" } # Like macros in rust.
@@ -181,8 +180,8 @@
181# Version control changes.
180# Version control changes.
182
181
183'diff.plus' = "light-green" # { } # Additions.
182'diff.plus' = "light-green" # { } # Additions.
184−'diff.minus' = "yellow" # { } # Deletions.
183+'diff.minus' = "red" # { } # Deletions.
185−'diff.delta' = "red" # { } # Modifications.
184+'diff.delta' = "yellow" # { } # Modifications.
186# 'diff.delta.moved' = { } # Renamed or moved files / changes.
185# 'diff.delta.moved' = { } # Renamed or moved files / changes.
187
186
188
187
@@ -197,8 +196,9 @@ yellow = "#ffd800"
197purple = "#918cff"
196purple = "#918cff"
198white = "#b1cace"
197white = "#b1cace"
199orange = "#ffa864"
198orange = "#ffa864"
199+light-orange = "#fff2c5"
200gray = "#5b858b" # mainly for comments/background text
200gray = "#5b858b" # mainly for comments/background text
201−light-gray = "#354e51" # used when whitespace rendering is enabled and for indent-guides
201+light-gray = "#2a3c41" # used when whitespace rendering is enabled and for indent-guides
202red = "#e34e1b"
202red = "#e34e1b"
203
203
204dark-blue = "#19a2b7"
204dark-blue = "#19a2b7"
runtime/themes/nord.toml+9
@@ -134,6 +134,15 @@
134"ui.bufferline" = { fg = "nord5", bg = "nord1" }
134"ui.bufferline" = { fg = "nord5", bg = "nord1" }
135"ui.bufferline.active" = { fg = "nord6", bg = "nord2", underline = { color = "nord8", style = "line" }, modifiers = [ "italic" ] }
135"ui.bufferline.active" = { fg = "nord6", bg = "nord2", underline = { color = "nord8", style = "line" }, modifiers = [ "italic" ] }
136
136
137+# Markup
138+"markup.heading" = "nord8"
139+"markup.list" = "nord9"
140+"markup.bold" = { modifiers = ["bold"] }
141+"markup.italic" = { modifiers = ["italic"] }
142+"markup.strikethrough" = { modifiers = ["crossed_out"] }
143+"markup.link.text" = "nord8"
144+"markup.raw" = "nord7"
145+
137[palette]
146[palette]
138# Polar Night is made up of four darker colors that are commonly used for base elements like backgrounds or text color in bright ambiance designs.
147# Polar Night is made up of four darker colors that are commonly used for base elements like backgrounds or text color in bright ambiance designs.
139#
148#
runtime/tutor+154
@@ -1186,6 +1186,160 @@ multiple cursors, they won't be uncommented but commented again.
1186
1186
1187
1187
1188
1188
1189+=================================================================
1190+= 12.1 USING MATCH MODE JUMP =
1191+=================================================================
1192+
1193+To switch to match mode from normal mode, type m. This feature
1194+is particularly useful for handling bracket pairs and their
1195+contents.
1196+
1197+There are several actions that can be performed in match mode,
1198+as indicated by the help pop-up. To jump to a matching bracket pair,
1199+simply press mm. For example on the lines below (starting with
1200+-->), move the cursor in normal mode to (, and then press mm to jump
1201+to the matching ). You can do the same on the line below: for example
1202+move to ], and press mm to jump to [ .
1203+
1204+--> you can (jump between matching parenthesis)
1205+--> or between matching [ square brackets ]
1206+--> now { you know the drill: this works with brackets too }
1207+
1208+
1209+
1210+
1211+=================================================================
1212+= 12.2 USING MATCH MODE SELECT INSIDE =
1213+=================================================================
1214+
1215+Match mode also lets you select the "inside" content between a
1216+pair of brackets or other delimiters. In the lines below:
1217+
1218+- move to the --> line, put your cursor in normal mode at any
1219+location between the parenthesis, for example at 'x', and press
1220+mi( or mi) to select the whole content inside the parenthesis
1221+(parenthesis excluded). As usual, you can then do anything you want
1222+with the selection (for example, press c to change it)
1223+
1224+--> outside and (inside x parenthesis) - and outside again
1225+
1226+Test below that you can do the same with [], or {}, or with
1227+nested combinations of these (this will act on the immediately
1228+surrounding matching pair). This also works with "" and similar
1229+
1230+--> test [ with square brackets ] !
1231+--> try ( with nested [ pairs of ( parenthesis) and "brackets" ])
1232+
1233+=================================================================
1234+= 12.3 USING MATCH MODE SELECT AROUND =
1235+=================================================================
1236+
1237+You can also select the "around" content, i.e. both the inside
1238+content and the delimiters themselves, by using the ma select.
1239+For example, move to the line under, move your cursor in normal
1240+mode to any position between the (), and select the content of
1241+the (), including the surrounding (), by typing ma( or ma). As
1242+usual, you can do anything you want with the selection, for
1243+example delete it all with ma(d .
1244+
1245+--> you ( select x around ) to include delimiters in the select
1246+
1247+This naturally works with other delimiters too:
1248+
1249+--> try [ with 'square' brackets ] too!
1250+
1251+
1252+
1253+
1254+
1255+=================================================================
1256+= 12.4 USING MATCH MODE SURROUND =
1257+=================================================================
1258+
1259+The match mode can also be used to add surrounding around the
1260+current selection. For example, move to the line below, then:
1261+ * i) select the "select all of this" line segment (for example,
1262+move in normal mode the cursor to the start of select, then enter
1263+selection mode with v , then select the 4 next words with 4e ),
1264+ * ii) press ms( or ms) to surround the selection with a pair of
1265+parenthesis.
1266+
1267+--> so, select all of this, and surround it with ()
1268+
1269+You can do the same with other delimiters: for example, ms' on
1270+WORD below to surround it with a pair of ''. You can try also
1271+with adding a surrounding pair of "", or {}, or [].
1272+
1273+--> surround this WORD !
1274+
1275+
1276+
1277+=================================================================
1278+= 12.5 USING MATCH MODE DELETE SURROUND =
1279+=================================================================
1280+
1281+You can delete surrounding pair of delimiters with the md
1282+command. On the line below, move the cursor anywhere
1283+within the pair of (), for example to the 'x', then from there,
1284+in normal mode, press md( or md) to delete the surrounding
1285+pair of parenthesis.
1286+
1287+--> delete (the x pair of parenthesis) from within!
1288+
1289+You can naturally delete other kinds of surroundings:
1290+
1291+--> delete (nested [delimiters]): "this" will delete the nearest
1292+matching surrounding pair.
1293+--> delete "layers "of" quote marks" too: this will delete the
1294+nearest previous and following quote marks
1295+
1296+Trying to delete unexisting surrounding delimiters print an error
1297+at the bottom bar and does nothing.
1298+
1299+=================================================================
1300+= 12.6 USING MATCH MODE REPLACE SURROUND =
1301+=================================================================
1302+
1303+You can replace surrounding pairs of delimiters with the mr
1304+command. On the line below, move the cursor to
1305+anywhere within the pair of (), for example on the 'x', then in
1306+normal mode, press mr([ to replace the pair of () with a pair
1307+of [].
1308+
1309+--> replace the (pair from x within), with something else
1310+
1311+This command will act on the closest enclosing pair, so you
1312+can try replacing different surrounding in the following:
1313+
1314+--> some (nested surroundings [can be replaced])
1315+--> this "works with 'other surroundings' too"
1316+
1317+You can try to replace a non existing pair: this will show
1318+an error warning at the bottom bar and do nothing.
1319+
1320+
1321+=================================================================
1322+= CHAPTER 12 RECAP =
1323+=================================================================
1324+
1325+You can enter the match mode with the m key; this will show the
1326+actions available in a popup. This will allow you to:
1327+ * jump to matching pair of delimiters with mm (you must have a
1328+ delimiter belonging to a pair under your cursor)
1329+ * select inside a pair of delimiters surrounding your cursor
1330+ (i.e. select the content but not the delimiters) with mi(
1331+ and similar
1332+ * select around a pair of delimiters surrounding your cursor
1333+ (i.e. select the content and the delimiters) with ma( and
1334+ similar
1335+ * delete surrounding delimiters with md( and similar
1336+ * add surrounding delimiters around the selection with ms(
1337+ * replace a pair of delimiters surrounding your selection with
1338+ mr([ to replace for example surrounding () with []
1339+
1340+
1341+
1342+
1189=================================================================
1343=================================================================
1190 This tutorial is still a work-in-progress.
1344 This tutorial is still a work-in-progress.
1191 More sections are planned.
1345 More sections are planned.