vis.ftdetect = {} vis.ftdetect.ignoresuffixes = { "~$", "%.orig$", "%.bak$", "%.old$", "%.new$" } vis.ftdetect.filetypes = { actionscript = { ext = { "%.as$", "%.asc$" }, }, ada = { ext = { "%.adb$", "%.ads$" }, }, ansi_c = { ext = { "%.c$", "%.C$", "%.h$" }, mime = { "text/x-c" }, }, antlr = { ext = { "%.g$", "%.g4$" }, }, apdl = { ext = { "%.ans$", "%.inp$", "%.mac$" }, }, apl = { ext = { "%.apl$" } }, applescript = { ext = { "%.applescript$" }, }, asm = { ext = { "%.asm$", "%.ASM$", "%.s$", "%.S$" }, }, asp = { ext = { "%.asa$", "%.asp$", "%.hta$" }, }, autoit = { ext = { "%.au3$", "%.a3x$" }, }, awk = { hashbang = { "^/usr/bin/[mng]awk%s+%-f" }, utility = { "^[mgn]?awk$", "^goawk$" }, ext = { "%.awk$" }, }, bash = { utility = { "^[db]ash$", "^sh$","^t?csh$","^zsh$" }, ext = { "%.bash$", "%.csh$", "%.sh$", "%.zsh$" ,"^APKBUILD$", "%.ebuild$", "^.bashrc$", "^.bash_profile$" }, mime = { "text/x-shellscript", "application/x-shellscript" }, }, batch = { ext = { "%.bat$", "%.cmd$" }, }, bibtex = { ext = { "%.bib$" }, }, boo = { ext = { "%.boo$" }, }, caml = { ext = { "%.caml$", "%.ml$", "%.mli$", "%.mll$", "%.mly$" }, }, chuck = { ext = { "%.ck$" }, }, clojure = { ext = { "%.clj$", "%.cljc$", "%.cljs$", "%.edn$" } }, cmake = { ext = { "%.cmake$", "%.cmake.in$", "%.ctest$", "%.ctest.in$" }, }, coffeescript = { ext = { "%.coffee$" }, mime = { "text/x-coffee" }, }, cpp = { ext = { "%.cpp$", "%.cxx$", "%.c++$", "%.cc$", "%.hh$", "%.hpp$", "%.hxx$", "%.h++$" }, mime = { "text/x-c++" }, }, crontab = { ext = { "^crontab.*$" }, cmd = { "set savemethod inplace" }, }, crystal = { ext = { "%.cr$" }, }, csharp = { ext = { "%.cs$" }, }, css = { ext = { "%.css$" }, mime = { "text/x-css" }, }, cuda = { ext = { "%.cu$", "%.cuh$" }, }, dart = { ext = { "%.dart$" }, }, desktop = { ext = { "%.desktop$" }, }, diff = { ext = { "%.diff$", "%.patch$", "%.rej$", "^COMMIT_EDITMSG$" }, cmd = { "set colorcolumn 72" }, }, dmd = { ext = { "%.d$", "%.di$" }, }, dockerfile = { ext = { "^Dockerfile$", "%.Dockerfile$" }, }, dot = { ext = { "%.dot$" }, }, dsv = { ext = { "^group$", "^gshadow$", "^passwd$", "^shadow$" }, }, eiffel = { ext = { "%.e$", "%.eif$" }, }, elixir = { ext = { "%.ex$", "%.exs$" }, }, elm = { ext = { "%.elm$" }, }, erlang = { ext = { "%.erl$", "%.hrl$" }, }, fantom = { ext = { "%.fan$" }, }, faust = { ext = { "%.dsp$" }, }, fennel = { ext = { "%.fnl$" }, }, fish = { utility = { "^fish$" }, ext = { "%.fish$" }, }, forth = { ext = { "%.forth$", "%.frt$", "%.fs$", "%.fth$" }, }, fortran = { ext = { "%.f$", "%.for$", "%.ftn$", "%.fpp$", "%.f77$", "%.f90$", "%.f95$", "%.f03$", "%.f08$" }, }, fsharp = { ext = { "%.fs$" }, }, fstab = { ext = { "fstab" }, }, gap = { ext = { "%.g$", "%.gd$", "%.gi$", "%.gap$" }, }, gemini = { ext = { "%.gmi" }, mime = { "text/gemini" }, }, gettext = { ext = { "%.po$", "%.pot$" }, }, gherkin = { ext = { "%.feature$" }, }, ['git-rebase'] = { ext = { "git%-rebase%-todo" }, }, glsl = { ext = { "%.glslf$", "%.glslv$" }, }, gnuplot = { ext = { "%.dem$", "%.plt$" }, }, go = { ext = { "%.go$" }, }, groovy = { ext = { "%.groovy$", "%.gvy$", "^Jenkinsfile$" }, }, gtkrc = { ext = { "%.gtkrc$" }, }, hare = { ext = { "%.ha$" } }, haskell = { ext = { "%.hs$" }, mime = { "text/x-haskell" }, }, html = { ext = { "%.htm$", "%.html$", "%.shtm$", "%.shtml$", "%.xhtml$" }, mime = { "text/x-html" }, }, icon = { ext = { "%.icn$" }, }, idl = { ext = { "%.idl$", "%.odl$" }, }, inform = { ext = { "%.inf$", "%.ni$" }, }, ini = { ext = { "%.cfg$", "%.cnf$", "%.conf$", "%.inf$", "%.ini$", "%.reg$" }, }, io_lang = { ext = { "%.io$" }, }, java = { ext = { "%.bsh$", "%.java$" }, }, javascript = { ext = { "%.cjs$", "%.js$", "%.jsfl$", "%.mjs$", "%.ts$", "%.jsx$", "%.tsx$" }, }, json = { ext = { "%.json$" }, mime = { "text/x-json" }, }, jsp = { ext = { "%.jsp$" }, }, julia = { ext = { "%.jl$" }, }, latex = { ext = { "%.bbl$", "%.cls$", "%.dtx$", "%.ins$", "%.ltx$", "%.tex$", "%.sty$" }, mime = { "text/x-tex" }, }, ledger = { ext = { "%.ledger$", "%.journal$" }, }, less = { ext = { "%.less$" }, }, lilypond = { ext = { "%.ily$", "%.ly$" }, }, lisp = { ext = { "%.cl$", "%.el$", "%.lisp$", "%.lsp$" }, mime = { "text/x-lisp" }, }, litcoffee = { ext = { "%.litcoffee$" }, }, logtalk = { ext = { "%.lgt$" }, }, lua = { utility = {"^lua%-?5?%d?$", "^lua%-?5%.%d$" }, ext = { "%.lua$" }, mime = { "text/x-lua" }, }, makefile = { hashbang = {"^#!/usr/bin/make"}, utility = {"^make$"}, ext = { "%.iface$", "%.mak$", "%.mk$", "GNUmakefile", "makefile", "Makefile" }, mime = { "text/x-makefile" }, }, man = { ext = { "%.1$", "%.2$", "%.3$", "%.4$", "%.5$", "%.6$", "%.7$", "%.8$", "%.9$", "%.1x$", "%.2x$", "%.3x$", "%.4x$", "%.5x$", "%.6x$", "%.7x$", "%.8x$", "%.9x$" }, }, markdown = { ext = { "%.md$", "%.markdown$" }, mime = { "text/x-markdown" }, }, meson = { ext = { "^meson%.build$" }, }, moonscript = { ext = { "%.moon$" }, mime = { "text/x-moon" }, }, myrddin = { ext = { "%.myr$" }, }, nemerle = { ext = { "%.n$" }, }, networkd = { ext = { "%.link$", "%.network$", "%.netdev$" }, }, nim = { ext = { "%.nim$" }, }, nsis = { ext = { "%.nsh$", "%.nsi$", "%.nsis$" }, }, objective_c = { ext = { "%.m$", "%.mm$", "%.objc$" }, mime = { "text/x-objc" }, }, pascal = { ext = { "%.dpk$", "%.dpr$", "%.p$", "%.pas$" }, }, perl = { ext = { "%.al$", "%.perl$", "%.pl$", "%.pm$", "%.pod$" }, mime = { "text/x-perl" }, }, php = { ext = { "%.inc$", "%.php$", "%.php3$", "%.php4$", "%.phtml$" }, }, pico8 = { ext = { "%.p8$" }, }, pike = { ext = { "%.pike$", "%.pmod$" }, }, pkgbuild = { ext = { "^PKGBUILD$", "%.PKGBUILD$" }, }, pony = { ext = { "%.pony$" }, }, powershell = { ext = { "%.ps1$" }, }, prolog = { ext = { "%.pl$", "%.pro$", "%.prolog$" }, }, props = { ext = { "%.props$", "%.properties$" }, }, protobuf = { ext = { "%.proto$" }, }, ps = { ext = { "%.eps$", "%.ps$" }, }, pure = { ext = { "%.pure$" }, }, python = { utility = { "^python%d?" }, ext = { "%.sc$", "%.py$", "%.pyw$" }, mime = { "text/x-python", "text/x-script.python" }, }, reason = { ext = { "%.re$" }, }, rc = { utility = {"^rc$"}, ext = { "%.rc$", "%.es$" }, }, rebol = { ext = { "%.r$", "%.reb$" }, }, rest = { ext = { "%.rst$" }, }, rexx = { ext = { "%.orx$", "%.rex$" }, }, rhtml = { ext = { "%.erb$", "%.rhtml$" }, }, routeros = { ext = { "%.rsc" }, detect = function(_, data) return data:match("^#.* by RouterOS") end }, rstats = { ext = { "%.R$", "%.Rout$", "%.Rhistory$", "%.Rt$", "Rout.save", "Rout.fail" }, }, ruby = { ext = { "%.Rakefile$", "%.rake$", "%.rb$", "%.rbw$", "^Vagrantfile$" }, mime = { "text/x-ruby" }, }, rust = { ext = { "%.rs$" }, mime = { "text/x-rust" }, }, sass = { ext = { "%.sass$", "%.scss$" }, mime = { "text/x-sass", "text/x-scss" }, }, scala = { ext = { "%.scala$" }, mime = { "text/x-scala" }, }, scheme = { ext = { "%.rkt$", "%.sch$", "%.scm$", "%.sld$", "%.sls$", "%.ss$" }, }, smalltalk = { ext = { "%.changes$", "%.st$", "%.sources$" }, }, sml = { ext = { "%.sml$", "%.fun$", "%.sig$" }, }, snobol4 = { ext = { "%.sno$", "%.SNO$" }, }, spin = { ext = { "%.spin$" } }, sql= { ext = { "%.ddl$", "%.sql$" }, }, strace = { detect = function(_, data) return data:match("^execve%(") end }, systemd = { ext = { "%.automount$", "%.device$", "%.mount$", "%.path$", "%.scope$", "%.service$", "%.slice$", "%.socket$", "%.swap$", "%.target$", "%.timer$" }, }, taskpaper = { ext = { "%.taskpaper$" }, }, tcl = { utility = {"^tclsh$", "^jimsh$" }, ext = { "%.tcl$", "%.tk$" }, }, texinfo = { ext = { "%.texi$" }, }, text = { ext = { "%.txt$" }, -- Do *not* list mime "text/plain" here, it is covered below, -- see 'try text lexer as a last resort' }, toml = { ext = { "%.toml$" }, }, vala = { ext = { "%.vala$" } }, vb = { ext = { "%.asa$", "%.bas$", "%.ctl$", "%.dob$", "%.dsm$", "%.dsr$", "%.frm$", "%.pag$", "%.vb$", "%.vba$", "%.vbs$" }, }, vcard = { ext = { "%.vcf$", "%.vcard$" }, }, verilog = { ext = { "%.v$", "%.ver$", "%.sv$" }, }, vhdl = { ext = { "%.vh$", "%.vhd$", "%.vhdl$" }, }, wsf = { ext = { "%.wsf$" }, }, xs = { ext = { "%.xs$", "^%.xsin$", "^%.xsrc$" }, }, xml = { ext = { "%.dtd$", "%.glif$", "%.plist$", "%.svg$", "%.xml$", "%.xsd$", "%.xsl$", "%.xslt$", "%.xul$" }, }, xtend = { ext = {"%.xtend$" }, }, yaml = { ext = { "%.yaml$", "%.yml$" }, mime = { "text/x-yaml" }, }, zig = { ext = { "%.zig$" }, }, } vis.events.subscribe(vis.events.WIN_OPEN, function(win) local set_filetype = function(syntax, filetype) for _, cmd in pairs(filetype.cmd or {}) do vis:command(cmd) end win:set_syntax(syntax) end local name = win.file.name -- remove ignored suffixes from filename local sanitizedfn = name if sanitizedfn ~= nil then sanitizedfn = sanitizedfn:gsub('^.*/', '') repeat local changed = false for _, pattern in pairs(vis.ftdetect.ignoresuffixes) do local start = sanitizedfn:find(pattern) if start then sanitizedfn = sanitizedfn:sub(1, start-1) changed = true end end until not changed end -- detect filetype by filename ending with a configured extension if sanitizedfn ~= nil then for lang, ft in pairs(vis.ftdetect.filetypes) do for _, pattern in pairs(ft.ext or {}) do if sanitizedfn:match(pattern) then set_filetype(lang, ft) return end end end end -- run file(1) to determine mime type local mime if name ~= nil then local file = io.popen(string.format("file -bL --mime-type -- '%s'", name:gsub("'", "'\\''"))) if file then mime = file:read('*all') file:close() if mime then mime = mime:gsub('%s*$', '') end if mime and #mime > 0 then for lang, ft in pairs(vis.ftdetect.filetypes) do for _, ft_mime in pairs(ft.mime or {}) do if mime == ft_mime then set_filetype(lang, ft) return end end end end end end -- pass first few bytes of file to custom file type detector functions local file = win.file local data = file:content(0, 256) if data and #data > 0 then for lang, ft in pairs(vis.ftdetect.filetypes) do if type(ft.detect) == 'function' and ft.detect(file, data) then set_filetype(lang, ft) return end end --[[ hashbang check hashbangs only have command argument if /env, find utility in args discard first arg if /-[^S]*S/; and all subsequent /=/ NOTE: this means you can't have a command with /^-|=/ return first field, which should be the utility. NOTE: long-options unsupported --]] local fullhb, utility = data:match"^#![ \t]*(/+[^/\n]+[^\n]*)" if fullhb then local i, field = 1, {} for m in fullhb:gmatch"%g+" do field[i],i = m,i+1 end -- NOTE: executables should not have a space (or =, see below) if field[1]:match"/env$" then table.remove(field,1) -- it is assumed that the first argument are short options, with -S inside if string.match(field[1] or "", "^%-[^S-]*S") then -- -S found table.remove(field,1) -- skip all name=value while string.match(field[1] or "","=") do table.remove(field,1) end -- (hopefully) whatever is left in field[1] should be the utility or nil end end utility = string.match(field[1] or "", "[^/]+$") -- remove filepath end local function searcher(tbl, subject) for i, pattern in ipairs(tbl or {}) do if string.match(subject, pattern) then return true end end return false end if utility or fullhb then for lang, ft in pairs(vis.ftdetect.filetypes) do if utility and searcher(ft.utility, utility) or fullhb and searcher(ft.hashbang, fullhb) then set_filetype(lang, ft) return end end end end -- try text lexer as a last resort if (mime or 'text/plain'):match('^text/.+$') then set_filetype('text', vis.ftdetect.filetypes.text) return end win:set_syntax(nil) end)