commit e009fb6f8aa3b7086b1892838525f36d9fe7e8ac Author: CoeJoder Date: Thu Feb 11 11:27:58 2021 -0800 Initial commit diff --git a/.lessfilter b/.lessfilter new file mode 100644 index 0000000..26a6226 --- /dev/null +++ b/.lessfilter @@ -0,0 +1,113 @@ +#!/bin/bash +# uses Pygments v2.7.4 for syntax highlighting on applicable file types +for path in "$@"; do + # match by known filenames + filename=$(basename "$path") + case "$filename" in + .bash_aliases|.bash_environment|.bash_login|.bash_logout|\ + .bash_profile|.cshdirs|.cshrc|.kshrc|.profile|.tcshrc|.zlogin|\ + .zlogout|.zprofile|bash.bashrc|csh.cshrc|csh.login|csh.logout|\ + ksh.kshrc|zlogin|zlogout|zprofile) + # shell lexer + pygmentize -f 256 -O style="$PYGMENTIZE_STYLE" -l sh "$path" + ;; + .Renviron|.Rhistory|.Rprofile|.bashrc|.exrc|.gvimrc|.htaccess|.vimrc|\ + .zshrc|BUCK|BUILD|BUILD.bazel|CMakeLists.txt|Dockerfile|GNUmakefile|\ + Gemfile|Kconfig*|Makefile|Makefile.*|PKGBUILD|Pipfile|Pipfile.lock|\ + Rakefile|SConscript|SConstruct|Singularity|WORKSPACE|apache.conf|\ + apache2.conf|autodelegate|autohandler|bashrc|control|dhandler|exrc|\ + external.in*|gvimrc|makefile|nginx.conf|pacman.conf|poetry.lock|\ + sources.list|squid.conf|standard-modules.in|termcap|termcap.src|\ + terminfo|terminfo.src|todo.txt|vimrc|xorg.conf|zshrc) + # filename recognized + pygmentize -f 256 -O style="$PYGMENTIZE_STYLE" "$path" + ;; + *) + ext=$([[ "$filename" = *.* ]] && echo ".${filename##*.}" || echo '') + case "$ext" in + .6pl|.6pm|.ABAP|.ASM|.BAS|.C|.CBL|.COB|.CPP|.CPY|.F|.F03|.F90|\ + .G|.H|.ICON|.PRG|.R|.Rd|.Rout|.S|.SAS|.VBS|.[1234567]|.abap|\ + .abnf|.ada|.adb|.adl|.adlf|.adls|.adlx|.ado|.ads|.agda|.aheui|\ + .ahk|.ahkl|.aj|.als|.apl|.applescript|.arexx|.arw|.as|.asax|\ + .ascx|.ashx|.asm|.asmx|.aspx|.asy|.at|.au3|.aug|.aux|.awk|\ + .axd|.b|.bare|.bas|.bash|.bat|.bb|.bbc|.bc|.befunge|.bf|.bib|\ + .bmx|.bnf|.boa|.boo|.bpl|.bro|.bst|.bug|.bzl|.c|.c++|\ + .c++-objdump|.c-objdump|.cadl|.camkes|.capnp|.cbl|.cc|.cdf|\ + .cdl|.ceylon|.cf|.cfc|.cfg|.cfm|.cfml|.chai|.chpl|.ci|.cirru|\ + .cl|.clay|.clj|.cljs|.cls|.cmake|.cmd|.cob|.coffee|.cp|.cpp|\ + .cpp-objdump|.cps|.cpsa|.cpy|.cr|.crmsh|.croc|.cry|.cs|.csd|\ + .csh|.css|.css.in|.cu|.cuh|.cw|.cxx|.cxx-objdump|.cyp|.cypher|\ + .d|.d-objdump|.darcspatch|.dart|.dasm|.dasm16|.dcl|.decls|\ + .def|.dg|.di|.diff|.dmesg|.do|.docker|.dot|.dpatch|.dpr|.dtd|\ + .dts|.dtsi|.duby|.duel|.dyl|.dylan|.dylan-console|.e|.ebnf|\ + .ebuild|.ec|.ecl|.eclass|.edp|.eex|.eg|.eh|.el|.elm|.eml|.eps|\ + .erl|.erl-sh|.es|.escript|.evoque|.ex|.exec|.exheres-0|.exlib|\ + .exs|.ezt|.f|.f03|.f90|.factor|.fan|.fancypack|.feature|\ + .fhtml|.fish|.flo|.flx|.flxh|.fnl|.frag|.frt|.fs|.fsi|.fst|\ + .fsti|.fun|.fy|.g|.gap|.gd|.gdc|.gemspec|.geo|.gi|.go|.golo|\ + .gradle|.graph|.groovy|.gs|.gsp|.gst|.gsx|.gv|.h|.h++|.haml|\ + .handlebars|.hbs|.hdp|.hh|.hlsl|.hlsli|.hpp|.hrl|.hs|.hsail|\ + .htm|.html|.hx|.hxml|.hxsl|.hxx|.hy|.hyb|.i|.i6t|.i7x|.icl|\ + .icn|.icon|.idc|.idl4|.idr|.ijs|.ik|.inc|.inf|.ini|.ino|\ + .instances|.intr|.io|.ipf|.isa|.j|.jade|.jag|.java|.jbst|.jcl|\ + .jl|.js|.js.in|.jsgf|.jsm|.json|.jsonld|.jsp|.juttle|.jy|.kal|\ + .kid|.kif|.kk|.kki|.kmsg|.ksh|.kt|.kts|.lagda|.lasso|\ + .lasso[89]|.lcry|.lean|.less|.lgt|.lhs|.lid|.lidr|.liquid|\ + .lisp|.ll|.load|.logtalk|.ls|.lsl|.lsp|.lua|.m|.ma|.mac|.mak|\ + .man|.mao|.maql|.markdown|.mask|.mc|.md|.mhtml|.mi|.mir|.mjs|\ + .mk|.ml|.mli|.mll|.mly|.mm|.mo|.mod|.monkey|.moo|.moon|.mos|\ + .mq4|.mq5|.mqh|.ms|.msc|.mt|.mu|.mxml|.myt|.n|.nb|.nbp|.nc|\ + .ncl|.ng2|.ni|.nim|.nimrod|.nit|.nix|.nl|.nqp|.ns2|.nsh|.nsi|\ + .objdump|.objdump-intel|.odin|.ooc|.opa|.orc|.p|.p6|.p6l|.p6m|\ + .pan|.pas|.patch|.pc|.pcmk|.peg|.perl|.php|.php[345]|.phtml|\ + .pig|.pike|.pl|.pl6|.plot|.plt|.pm|.pm6|.pmod|.po|.pony|.pot|\ + .pov|.pp|.praat|.prg|.pro|.proc|.prolog|.promql|.properties|\ + .proto|.ps|.ps1|.psc|.psi|.psl|.psm1|.ptls|.pug|.pwn|.pxd|\ + .pxi|.py|.py2tb|.py3tb|.pypylog|.pytb|.pyw|.pyx|.qbs|.qml|\ + .qvto|.r|.r3|.rake|.raku|.rakudoc|.rakumod|.rakutest|.rb|.rbw|\ + .rbx|.re|.reb|.red|.reds|.reg|.rei|.rest|.rex|.rexx|.rhtml|\ + .ride|.rkt|.rktd|.rktl|.rl|.rnc|.robot|.rpf|.rq|.rql|.rs|\ + .rs.in|.rsl|.rss|.rst|.rts|.run|.rvt|.rx|.s|.sage|.sarl|.sas|\ + .sass|.sbl|.sc|.scala|.scaml|.scd|.scdoc|.sce|.sci|.scm|.sco|\ + .scss|.sgf|.sh|.sh-session|.shell-session|.shen|.shex|.sieve|\ + .sig|.sil|.siv|.sl|.sla|.slim|.sls|.smali|.sml|.smv|.snobol|\ + .sol|.sp|.sparql|.spec|.spt|.sql|.sqlite3-console|.ss|.ssp|\ + .st|.stan|.sv|.svh|.swg|.swift|.t|.tac|.tap|.tasm|.tcl|.tcsh|\ + .tea|.tex|.tf|.thrift|.thy|.tid|.tmpl|.tnt|.toc|.todotxt|\ + .toml|.tpl|.treetop|.ts|.tst|.tsx|.tt|.ttl|.twig|.txt|\ + .typoscript|.u|.u1|.u2|.udo|.usd|.usda|.v|.vala|.vapi|.vark|\ + .vb|.vbs|.vcl|.vert|.vhd|.vhdl|.vim|.vm|.vpr|.wdiff|.webidl|\ + .weechatlog|.whiley|.wlua|.wsdl|.wsf|.x|.x10|.xhtml|.xi|.xm|\ + .xmi|.xml|.xpl|.xq|.xql|.xqm|.xquery|.xqy|.xsd|.xsl|.xslt|\ + .xtend|.xtm|.xul.in|.yaml|.yang|.yml|.zeek|.zep|.zig|.zsh|\ + Config.in*) + # extension recognized + pygmentize -f 256 -O style="$PYGMENTIZE_STYLE" "$path" + ;; + *) + # unrecognized filename/extension + # attempt to parse the lexer from the shebang if it exists + lexer=$(head -n 1 "$path" |grep "^#\!" |awk -F" " \ +'match($1, /\/(\w*)$/, a) {if (a[1]!="env") {print a[1]} else {print $2}}') + case "$lexer" in + node|nodejs) + # workaround for lack of Node.js lexer alias + pygmentize -f 256 -O style="$PYGMENTIZE_STYLE" \ + -l js "$path" + ;; + "") + # fall-back to plain text + exit 1 + ;; + *) + # use lexer alias parsed from the shebang + pygmentize -f 256 -O style="$PYGMENTIZE_STYLE" \ + -l "$lexer" "$path" + ;; + esac + ;; + esac + ;; + esac +done +exit 0 diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..76156ac --- /dev/null +++ b/Pipfile @@ -0,0 +1,15 @@ +[[source]] +name = "pypi" +url = "https://pypi.org/simple" +verify_ssl = true + +[dev-packages] + +[packages] +lxml = "*" +requests = "*" +cssselect = "*" +jinja2 = "*" + +[requires] +python_version = "3.8" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..58f8053 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,177 @@ +{ + "_meta": { + "hash": { + "sha256": "a5fedff923bbea6dc7dcee92b7dfb8b98d2250b5ad69a0b6167a7dd14695983d" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.8" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "certifi": { + "hashes": [ + "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c", + "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830" + ], + "version": "==2020.12.5" + }, + "chardet": { + "hashes": [ + "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa", + "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==4.0.0" + }, + "cssselect": { + "hashes": [ + "sha256:f612ee47b749c877ebae5bb77035d8f4202c6ad0f0fc1271b3c18ad6c4468ecf", + "sha256:f95f8dedd925fd8f54edb3d2dfb44c190d9d18512377d3c1e2388d16126879bc" + ], + "index": "pypi", + "version": "==1.1.0" + }, + "idna": { + "hashes": [ + "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6", + "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.10" + }, + "jinja2": { + "hashes": [ + "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419", + "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6" + ], + "index": "pypi", + "version": "==2.11.3" + }, + "lxml": { + "hashes": [ + "sha256:0448576c148c129594d890265b1a83b9cd76fd1f0a6a04620753d9a6bcfd0a4d", + "sha256:127f76864468d6630e1b453d3ffbbd04b024c674f55cf0a30dc2595137892d37", + "sha256:1471cee35eba321827d7d53d104e7b8c593ea3ad376aa2df89533ce8e1b24a01", + "sha256:2363c35637d2d9d6f26f60a208819e7eafc4305ce39dc1d5005eccc4593331c2", + "sha256:2e5cc908fe43fe1aa299e58046ad66981131a66aea3129aac7770c37f590a644", + "sha256:2e6fd1b8acd005bd71e6c94f30c055594bbd0aa02ef51a22bbfa961ab63b2d75", + "sha256:366cb750140f221523fa062d641393092813b81e15d0e25d9f7c6025f910ee80", + "sha256:42ebca24ba2a21065fb546f3e6bd0c58c3fe9ac298f3a320147029a4850f51a2", + "sha256:4e751e77006da34643ab782e4a5cc21ea7b755551db202bc4d3a423b307db780", + "sha256:4fb85c447e288df535b17ebdebf0ec1cf3a3f1a8eba7e79169f4f37af43c6b98", + "sha256:50c348995b47b5a4e330362cf39fc503b4a43b14a91c34c83b955e1805c8e308", + "sha256:535332fe9d00c3cd455bd3dd7d4bacab86e2d564bdf7606079160fa6251caacf", + "sha256:535f067002b0fd1a4e5296a8f1bf88193080ff992a195e66964ef2a6cfec5388", + "sha256:5be4a2e212bb6aa045e37f7d48e3e1e4b6fd259882ed5a00786f82e8c37ce77d", + "sha256:60a20bfc3bd234d54d49c388950195d23a5583d4108e1a1d47c9eef8d8c042b3", + "sha256:648914abafe67f11be7d93c1a546068f8eff3c5fa938e1f94509e4a5d682b2d8", + "sha256:681d75e1a38a69f1e64ab82fe4b1ed3fd758717bed735fb9aeaa124143f051af", + "sha256:68a5d77e440df94011214b7db907ec8f19e439507a70c958f750c18d88f995d2", + "sha256:69a63f83e88138ab7642d8f61418cf3180a4d8cd13995df87725cb8b893e950e", + "sha256:6e4183800f16f3679076dfa8abf2db3083919d7e30764a069fb66b2b9eff9939", + "sha256:6fd8d5903c2e53f49e99359b063df27fdf7acb89a52b6a12494208bf61345a03", + "sha256:791394449e98243839fa822a637177dd42a95f4883ad3dec2a0ce6ac99fb0a9d", + "sha256:7a7669ff50f41225ca5d6ee0a1ec8413f3a0d8aa2b109f86d540887b7ec0d72a", + "sha256:7e9eac1e526386df7c70ef253b792a0a12dd86d833b1d329e038c7a235dfceb5", + "sha256:7ee8af0b9f7de635c61cdd5b8534b76c52cd03536f29f51151b377f76e214a1a", + "sha256:8246f30ca34dc712ab07e51dc34fea883c00b7ccb0e614651e49da2c49a30711", + "sha256:8c88b599e226994ad4db29d93bc149aa1aff3dc3a4355dd5757569ba78632bdf", + "sha256:923963e989ffbceaa210ac37afc9b906acebe945d2723e9679b643513837b089", + "sha256:94d55bd03d8671686e3f012577d9caa5421a07286dd351dfef64791cf7c6c505", + "sha256:97db258793d193c7b62d4e2586c6ed98d51086e93f9a3af2b2034af01450a74b", + "sha256:a9d6bc8642e2c67db33f1247a77c53476f3a166e09067c0474facb045756087f", + "sha256:cd11c7e8d21af997ee8079037fff88f16fda188a9776eb4b81c7e4c9c0a7d7fc", + "sha256:d8d3d4713f0c28bdc6c806a278d998546e8efc3498949e3ace6e117462ac0a5e", + "sha256:e0bfe9bb028974a481410432dbe1b182e8191d5d40382e5b8ff39cdd2e5c5931", + "sha256:f4822c0660c3754f1a41a655e37cb4dbbc9be3d35b125a37fab6f82d47674ebc", + "sha256:f83d281bb2a6217cd806f4cf0ddded436790e66f393e124dfe9731f6b3fb9afe", + "sha256:fc37870d6716b137e80d19241d0e2cff7a7643b925dfa49b4c8ebd1295eb506e" + ], + "index": "pypi", + "version": "==4.6.2" + }, + "markupsafe": { + "hashes": [ + "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", + "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", + "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", + "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", + "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42", + "sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f", + "sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39", + "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", + "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", + "sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014", + "sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f", + "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", + "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", + "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", + "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", + "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b", + "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", + "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15", + "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", + "sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85", + "sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1", + "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", + "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", + "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", + "sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850", + "sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0", + "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", + "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", + "sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb", + "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", + "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", + "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", + "sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1", + "sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2", + "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", + "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", + "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", + "sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7", + "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", + "sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8", + "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", + "sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193", + "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", + "sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b", + "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", + "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2", + "sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5", + "sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c", + "sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032", + "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7", + "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be", + "sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.1.1" + }, + "requests": { + "hashes": [ + "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804", + "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e" + ], + "index": "pypi", + "version": "==2.25.1" + }, + "urllib3": { + "hashes": [ + "sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80", + "sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", + "version": "==1.26.3" + } + }, + "develop": {} +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..57dc556 --- /dev/null +++ b/README.md @@ -0,0 +1,62 @@ +# Syntax highlighter for `less` +Uses [Pygments](https://pygments.org/) to provide syntax highlighting +for supported file types. The `.lessfilter` is generated by parsing the Pygments documentation website. +![screenshot](screenshot.png) + +## Installation + +### 1. Install Pygments & gawk +Ensure that you have the latest version of Pygments installed by using [pip](https://pypi.org/project/pip/), as your +distro's packaged version is likely outdated. You'll also need `gawk`. +```shell +# remove any installed packaged versions +sudo apt autoremove python-pygments python3-pygments + +# install latest version +sudo pip install Pygments --upgrade + +# install gawk +sudo apt install gawk +``` + +### 2. Install lesspipe (recommended) +Most Linux distros already have `lesspipe` enabled, but you can check for certain by running: +```shell +echo $LESSOPEN +``` +If you don't see `lesspipe` or `lessfile` in the output, install [lesspipe](https://github.com/wofr06/lesspipe). + +### 3. Configure Environment Variables +Add the following to `~/.bashrc` +```shell +# sets LESSOPEN and LESSCLOSE variables +eval "$(SHELL=/bin/sh lesspipe)" + +# interpret color characters +export LESS='-R' + +# to list available styles: `pygmentize -L styles` +export PYGMENTIZE_STYLE='paraiso-dark' + +# optional +alias ls='ls --color=always' +alias grep='grep --color=always' +``` +If you don't have or want `lesspipe`, replace the above `eval` statement with: +```shell +export LESSOPEN='|~/.lessfilter %s' +``` + +### 4. Create ~/.lessfilter +Run [main.py](main.py) to generate the latest `.lessfilter` file and place it in your home directory. +Or you can download a pre-generated [.lessfilter](.lessfilter) which is currently at version `2.7.4`. +Check your program version by running `pygmentize -V` to ensure it matches. + +### 5. Make ~/.lessfilter executable +```shell +chmod +x ~/.lessfilter +``` +All set! Test it out by running `less ~/.lessfilter` + +## Notes +The first time `pygmentize` is run in a shell session, you'll likely experience a cold-start delay. Subsequent invocations are much faster. diff --git a/main.py b/main.py new file mode 100644 index 0000000..45cec30 --- /dev/null +++ b/main.py @@ -0,0 +1,128 @@ +from typing import List, Iterable + +import re +import requests +import os +from lxml import html +from lxml.html import HtmlElement +from lxml.cssselect import CSSSelector +from pathlib import Path +from jinja2 import FileSystemLoader, Environment +from itertools import chain + +URL_HOMEPAGE = 'https://pygments.org/' +URL_LEXERS = 'https://pygments.org/docs/lexers/' +SELECT_LEXERS = CSSSelector('.py.class') +SELECT_LEXER_DESCRIPTION = CSSSelector('.field-list') +SELECT_LEXER_NAME = CSSSelector('.sig-name') +SELECT_HOMEPAGE_VERSION = CSSSelector('.sphinxsidebarwrapper b') +REGEXP_FILENAMES = re.compile(r'.*?Filenames\s+?(.+?)$', re.MULTILINE | re.DOTALL) +PATH_PROJECT = Path(__file__).parent +TEMPLATE_LESSFILTER = 'template.lessfilter.sh' +TEMPLATE_OUTPUT = '.lessfilter' +INDENT = 4 +INDENT_DOUBLE = INDENT * 2 +INDENT_QUADRUPLE = INDENT * 4 +MAX_COL_SIZE = 80 + +# any known shell filenames that don't have specific lexers will use the `sh` lexer +misc_shell_filenames = [".bashrc", "bash.bashrc", ".bash_aliases", ".bash_environment", ".bash_profile", ".bash_login", + ".bash_logout", ".profile", ".zprofile", ".zshrc", ".zlogin", ".zlogout", "zprofile", "zshrc", + "zlogin", "zlogout", ".cshrc", ".cshdirs", "csh.cshrc", "csh.login", "csh.logout", ".tcshrc", + ".kshrc", "ksh.kshrc"] +recognized_filenames = {} +recognized_extensions = {} + + +def main(): + print("Fetching Pygments version number from homepage...", end='') + version = fetch_version() + print(f'v{version}') + print('Fetching lexers from documentation page...') + fetch_lexers() + print(f' Supported filenames:\n {recognized_filenames}') + print(f' Supported extensions:\n {recognized_extensions}') + render_template(version) + print(f'Done.') + + +def fetch_version() -> str: + page = requests.get(URL_HOMEPAGE) + doc: HtmlElement = html.fromstring(page.content) + version: List[HtmlElement] = SELECT_HOMEPAGE_VERSION(doc) + if len(version) != 1: + raise Exception('Unable to find Pygments version number.') + return version[0].text_content() + + +def fetch_lexers(): + page = requests.get(URL_LEXERS) + doc: HtmlElement = html.fromstring(page.content) + lexers: List[HtmlElement] = SELECT_LEXERS(doc) + for lexer in lexers: + name = next(iter(SELECT_LEXER_NAME(lexer)), None) + name = name.text_content() if name is not None else 'None' + description = next(iter(SELECT_LEXER_DESCRIPTION(lexer)), None) + description = description.text_content() if description is not None else 'None' + m = REGEXP_FILENAMES.match(description) + if m is not None and len(m.groups()) == 1: + filenames: List[str] = m.group(1).split(',') + for filename in filenames: + filename = filename.strip() + if '\\' not in filename: + if filename.startswith('*'): + ext_values = recognized_extensions.setdefault(name, []) + ext_values.append(filename[1:]) + elif filename != 'None': + filename_values = recognized_filenames.setdefault(name, []) + filename_values.append(filename) + # specific lexer exists; no need to use the `sh` lexer + try: + misc_shell_filenames.remove(filename) + except ValueError: + pass + else: + raise Exception(f'Aborted due to an unrecognized "{name}" lexer description format: {description}') + + +def render_template(version: str): + env = Environment(loader=FileSystemLoader(PATH_PROJECT)) + template = env.get_template(TEMPLATE_LESSFILTER) + template_vars = { + 'pygments_version': version, + 'misc_shell_filenames': to_bar_separated_string(chain(misc_shell_filenames), + INDENT_DOUBLE), + 'recognized_filenames': to_bar_separated_string(chain.from_iterable(recognized_filenames.values()), + INDENT_DOUBLE), + 'recognized_extensions': to_bar_separated_string(chain.from_iterable(recognized_extensions.values()), + INDENT_QUADRUPLE) + } + output = template.render(**template_vars) + output_path = PATH_PROJECT.joinpath(TEMPLATE_OUTPUT) + with output_path.open(mode='w', encoding='utf-8', newline='\n') as f: + f.write(output) + output_path.chmod(0o755) + + +def to_bar_separated_string(values: Iterable[str], indent: int): + # remove duplicates and sort lexicographically + values = sorted(set(values)) + # first line already indented in template + result = '' + line_len = indent + for i in range(0, len(values)): + value = values[i] + value_len = len(value) + if i > 0: + result += '|' + line_len += 1 + if line_len + value_len + 2 >= MAX_COL_SIZE: + result += '\\\n' + (' ' * indent) + line_len = indent + result += value + line_len += value_len + return result + + +if __name__ == '__main__': + main() diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000..9c2ae9a Binary files /dev/null and b/screenshot.png differ diff --git a/template.lessfilter.sh b/template.lessfilter.sh new file mode 100644 index 0000000..77ac1d6 --- /dev/null +++ b/template.lessfilter.sh @@ -0,0 +1,49 @@ +#!/bin/bash +# uses Pygments v{{ pygments_version }} for syntax highlighting on applicable file types +for path in "$@"; do + # match by known filenames + filename=$(basename "$path") + case "$filename" in + {{ misc_shell_filenames }}) + # shell lexer + pygmentize -f 256 -O style="$PYGMENTIZE_STYLE" -l sh "$path" + ;; + {{ recognized_filenames }}) + # filename recognized + pygmentize -f 256 -O style="$PYGMENTIZE_STYLE" "$path" + ;; + *) + ext=$([[ "$filename" = *.* ]] && echo ".${filename##*.}" || echo '') + case "$ext" in + {{ recognized_extensions }}) + # extension recognized + pygmentize -f 256 -O style="$PYGMENTIZE_STYLE" "$path" + ;; + *) + # unrecognized filename/extension + # attempt to parse the lexer from the shebang if it exists + lexer=$(head -n 1 "$path" |grep "^#\!" |awk -F" " \ +'match($1, /\/(\w*)$/, a) {if (a[1]!="env") {print a[1]} else {print $2}}') + case "$lexer" in + node|nodejs) + # workaround for lack of Node.js lexer alias + pygmentize -f 256 -O style="$PYGMENTIZE_STYLE" \ + -l js "$path" + ;; + "") + # fall-back to plain text + exit 1 + ;; + *) + # use lexer alias parsed from the shebang + pygmentize -f 256 -O style="$PYGMENTIZE_STYLE" \ + -l "$lexer" "$path" + ;; + esac + ;; + esac + ;; + esac +done +exit 0 +