├── .gitignore ├── LICENSE ├── admin ├── check-order.el └── languages.sh └── language-id.el /.gitignore: -------------------------------------------------------------------------------- 1 | *.elc 2 | /admin/languages.yml 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019, 2020 Lassi Kortela 2 | 3 | Permission to use, copy, modify, and distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 8 | WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 9 | WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 10 | AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 11 | DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 12 | PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 13 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | PERFORMANCE OF THIS SOFTWARE. 15 | -------------------------------------------------------------------------------- /admin/check-order.el: -------------------------------------------------------------------------------- 1 | ;; If a child mode is derived from a parent mode, the child must be 2 | ;; listed before the parent. Otherwise the parent will shadow the 3 | ;; child and the child will never match. 4 | 5 | ;; Note that information about parent-child relationships is only 6 | ;; available for modes that have been loaded into Emacs. For a 7 | ;; comprehensive check, all modes recognized by language-id need to be 8 | ;; loaded into Emacs. 9 | 10 | (cl-reduce (lambda (modes-so-far language) 11 | (let ((language-modes (cdr language))) 12 | (cl-reduce (lambda (modes-so-far mode) 13 | (message "%S" modes-so-far) 14 | (let ((mode (if (listp mode) (car mode) mode))) 15 | (cl-assert (symbolp mode)) 16 | (let ((parent (apply #'provided-mode-derived-p 17 | mode modes-so-far))) 18 | (when (and parent (not (equal parent mode))) 19 | (error "%S is shadowed by %S" mode parent))) 20 | (cons mode modes-so-far))) 21 | language-modes 22 | :initial-value modes-so-far))) 23 | language-id--definitions 24 | :initial-value '()) 25 | -------------------------------------------------------------------------------- /admin/languages.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -eu 3 | cd "$(dirname "$0")" 4 | echo "Entering directory '$PWD'" 5 | set -x 6 | wget -O languages.yml https://raw.githubusercontent.com/github/linguist/master/lib/linguist/languages.yml 7 | -------------------------------------------------------------------------------- /language-id.el: -------------------------------------------------------------------------------- 1 | ;;; language-id.el --- Library to work with programming language identifiers -*- lexical-binding: t -*- 2 | 3 | ;; Author: Lassi Kortela 4 | ;; URL: https://github.com/lassik/emacs-language-id 5 | ;; Version: 0.20 6 | ;; Package-Requires: ((emacs "24.3")) 7 | ;; Keywords: languages util 8 | ;; SPDX-License-Identifier: ISC 9 | 10 | ;; This file is not part of GNU Emacs. 11 | 12 | ;;; Commentary: 13 | 14 | ;; language-id is a small, focused library that helps other Emacs 15 | ;; packages identify the programming languages and markup languages 16 | ;; used in Emacs buffers. The main point is that it contains an 17 | ;; evolving table of language definitions that doesn't need to be 18 | ;; replicated in other packages. 19 | 20 | ;; Right now there is only one public function, `language-id-buffer'. 21 | ;; It looks at the major mode and other variables and returns the 22 | ;; language's GitHub Linguist identifier. We can add support for 23 | ;; other kinds of identifiers if there is demand. 24 | 25 | ;; This library does not do any statistical text matching to guess the 26 | ;; language. 27 | 28 | ;;; Code: 29 | 30 | (require 'cl-lib) 31 | 32 | (defvar language-id--file-name-extension nil 33 | "Internal variable for file name extension during lookup.") 34 | 35 | ;; 36 | (defconst language-id--definitions 37 | '( 38 | 39 | ;;; Definitions that need special attention to precedence order. 40 | 41 | ;; It is not uncommon for C++ mode to be used when writing Cuda. 42 | ;; In this case, the only way to correctly identify Cuda is by 43 | ;; looking at the extension. 44 | ("Cuda" 45 | (c++-mode 46 | (language-id--file-name-extension ".cu")) 47 | (c++-mode 48 | (language-id--file-name-extension ".cuh"))) 49 | 50 | ;; mint-mode is derived from js-jsx-mode. 51 | ("Mint" mint-mode) 52 | 53 | ;; json-mode is derived from javascript-mode. 54 | ("JSON5" 55 | (json-mode 56 | (language-id--file-name-extension ".json5")) 57 | (web-mode 58 | (web-mode-content-type "json") 59 | (web-mode-engine "none") 60 | (language-id--file-name-extension ".json5"))) 61 | ("JSON" 62 | json-mode 63 | jsonian-mode 64 | json-ts-mode 65 | (web-mode 66 | (web-mode-content-type "json") 67 | (web-mode-engine "none"))) 68 | 69 | ;; php-mode is derived from c-mode. 70 | ("PHP" 71 | php-mode 72 | php-ts-mode 73 | (web-mode 74 | (web-mode-content-type "html") 75 | (web-mode-engine "php"))) 76 | 77 | ;; scss-mode is derived from css-mode. 78 | ("SCSS" scss-mode) 79 | 80 | ;; solidity-mode is derived from c-mode. 81 | ("Solidity" solidity-mode) 82 | 83 | ;; svelte-mode is derived from html-mode. 84 | ("Svelte" 85 | svelte-mode 86 | (web-mode 87 | (web-mode-content-type "html") 88 | (web-mode-engine "svelte"))) 89 | 90 | ;; terraform-mode is derived from hcl-mode. 91 | ("Terraform" terraform-mode) 92 | 93 | ;; TypeScript/TSX need to come before JavaScript/JSX because in 94 | ;; web-mode we can tell them apart by file name extension only. 95 | ;; 96 | ;; This implies that we inconsistently classify unsaved temp 97 | ;; buffers using TypeScript/TSX as JavaScript/JSX. 98 | ("TSX" 99 | typescript-tsx-mode 100 | tsx-ts-mode 101 | (web-mode 102 | (web-mode-content-type "jsx") 103 | (web-mode-engine "none") 104 | (language-id--file-name-extension ".tsx"))) 105 | ("TypeScript" 106 | typescript-mode 107 | typescript-ts-mode 108 | (web-mode 109 | (web-mode-content-type "javascript") 110 | (web-mode-engine "none") 111 | (language-id--file-name-extension ".ts"))) 112 | 113 | ;; ReScript needs to come before Reason because in reason-mode 114 | ;; we can tell them apart by file name extension only. 115 | ("ReScript" 116 | (reason-mode 117 | (language-id--file-name-extension ".res"))) 118 | ("ReScript" 119 | (reason-mode 120 | (language-id--file-name-extension ".resi"))) 121 | ("ReScript" rescript-mode) 122 | ("Reason" reason-mode) 123 | 124 | ;; vue-html-mode is derived from html-mode. 125 | ("Vue" 126 | vue-mode 127 | vue-html-mode 128 | (web-mode 129 | (web-mode-content-type "html") 130 | (web-mode-engine "vue"))) 131 | 132 | ;;; The rest of the definitions are in alphabetical order. 133 | 134 | ("Assembly" asm-mode nasm-mode) 135 | ("ATS" ats-mode) 136 | ("Awk" awk-mode) 137 | ("Bazel" bazel-mode) 138 | ("BibTeX" bibtex-mode) 139 | ("C" c-mode c-ts-mode) 140 | ("C#" csharp-mode csharp-ts-mode) 141 | ("C++" c++-mode c++-ts-mode) 142 | ("Cabal Config" haskell-cabal-mode) 143 | ("Clojure" clojurescript-mode clojurec-mode clojure-mode) 144 | ("CMake" cmake-mode cmake-ts-mode) 145 | ("Common Lisp" lisp-mode) 146 | ("Crystal" crystal-mode) 147 | ("CSS" 148 | css-mode 149 | css-ts-mode 150 | (web-mode 151 | (web-mode-content-type "css") 152 | (web-mode-engine "none"))) 153 | ("Cuda" cuda-mode) 154 | ("D" d-mode) 155 | ("Dart" dart-mode) 156 | ("Dhall" dhall-mode) 157 | ("Dockerfile" dockerfile-mode dockerfile-ts-mode) 158 | ("EJS" 159 | (web-mode 160 | (web-mode-content-type "html") 161 | (web-mode-engine "ejs"))) 162 | ("Elixir" elixir-mode elixir-ts-mode) 163 | ("Elm" elm-mode) 164 | ("Emacs Lisp" emacs-lisp-mode) 165 | ("Erlang" erlang-mode) 166 | ("F#" fsharp-mode) 167 | ("Fish" fish-mode) 168 | ("Fortran" fortran-mode) 169 | ("Fortran Free Form" f90-mode) 170 | ("GLSL" glsl-mode) 171 | ("Go" go-mode go-ts-mode) 172 | ("GraphQL" graphql-mode) 173 | ("Haskell" haskell-mode) 174 | ("HCL" hcl-mode) 175 | ("HLSL" hlsl-mode) 176 | ("HTML" 177 | html-helper-mode 178 | mhtml-mode 179 | html-mode 180 | nxhtml-mode 181 | (web-mode 182 | (web-mode-content-type "html") 183 | (web-mode-engine "none"))) 184 | ("HTML+EEX" 185 | heex-ts-mode 186 | (web-mode 187 | (web-mode-content-type "html") 188 | (web-mode-engine "elixir"))) 189 | ("HTML+ERB" 190 | (web-mode 191 | (web-mode-content-type "html") 192 | (web-mode-engine "erb"))) 193 | ("Hy" hy-mode) 194 | ("Java" java-mode java-ts-mode) 195 | ("JavaScript" 196 | js-ts-mode 197 | (js-mode 198 | (flow-minor-mode nil)) 199 | (js2-mode 200 | (flow-minor-mode nil)) 201 | (js3-mode 202 | (flow-minor-mode nil)) 203 | (web-mode 204 | (web-mode-content-type "javascript") 205 | (web-mode-engine "none"))) 206 | ("JavaScript+ERB" 207 | (web-mode 208 | (web-mode-content-type "javascript") 209 | (web-mode-engine "erb"))) 210 | ("JSON" 211 | json-mode 212 | json-ts-mode 213 | (web-mode 214 | (web-mode-content-type "json") 215 | (web-mode-engine "none"))) 216 | ("Jsonnet" jsonnet-mode) 217 | ("JSX" 218 | js2-jsx-mode 219 | jsx-mode 220 | rjsx-mode 221 | react-mode 222 | (web-mode 223 | (web-mode-content-type "jsx") 224 | (web-mode-engine "none"))) 225 | ("Kotlin" kotlin-mode) 226 | ("LaTeX" latex-mode) 227 | ("Less" less-css-mode) 228 | ("Literate Haskell" literate-haskell-mode) 229 | ("Lua" lua-mode) 230 | ("Markdown" gfm-mode markdown-mode) 231 | ("Meson" meson-mode) 232 | ("Nix" nix-mode nix-ts-mode) 233 | ("Objective-C" objc-mode) 234 | ("OCaml" caml-mode tuareg-mode) 235 | ("Perl" cperl-mode perl-mode) 236 | ("Protocol Buffer" protobuf-mode) 237 | ("Puppet" puppet-mode) 238 | ("PureScript" purescript-mode) 239 | ("Python" python-mode python-ts-mode) 240 | ("R" 241 | ess-r-mode 242 | (ess-mode 243 | (ess-dialect "R"))) 244 | ("Racket" racket-mode) 245 | ("Ruby" enh-ruby-mode ruby-mode ruby-ts-mode) 246 | ("Rust" rust-mode rustic-mode rust-ts-mode) 247 | ("Scala" scala-mode scala-ts-mode) 248 | ("Scheme" scheme-mode) 249 | ("Shell" sh-mode bash-ts-mode) 250 | ("SQL" sql-mode) 251 | ("Swift" swift-mode swift3-mode) 252 | ("TOML" toml-mode conf-toml-mode toml-ts-mode) 253 | ("V" v-mode) 254 | ("Verilog" verilog-mode) 255 | ("XML" 256 | nxml-mode 257 | xml-mode 258 | (web-mode 259 | (web-mode-content-type "xml") 260 | (web-mode-engine "none"))) 261 | ("YAML" yaml-mode yaml-ts-mode) 262 | ("Zig" zig-mode)) 263 | "Internal table of programming language definitions.") 264 | 265 | (defun language-id--mode-match-p (mode) 266 | "Interal helper to match current buffer against MODE." 267 | (let ((mode (if (listp mode) mode (list mode)))) 268 | (cl-destructuring-bind (wanted-major-mode &rest variables) mode 269 | (and (derived-mode-p wanted-major-mode) 270 | (cl-every 271 | (lambda (variable) 272 | (cl-destructuring-bind (symbol wanted-value) variable 273 | (equal wanted-value 274 | (if (boundp symbol) (symbol-value symbol) nil)))) 275 | variables))))) 276 | 277 | ;;;###autoload 278 | (defun language-id-buffer () 279 | "Get GitHub Linguist language name for current buffer. 280 | 281 | Return the name of the programming language or markup language 282 | used in the current buffer. The name is a string from the GitHub 283 | Linguist language list. The language is determined by looking at 284 | the active `major-mode'. Some major modes support more than one 285 | language. In that case minor modes and possibly other variables 286 | are consulted to disambiguate the language. 287 | 288 | In addition to the modes bundled with GNU Emacs, many third-party 289 | modes are recognized. No statistical text matching or other 290 | heuristics are used in detecting the language. 291 | 292 | The language definitions live inside the language-id library and 293 | are updated in new releases of the library. 294 | 295 | If the language is not unambiguously recognized, the function 296 | returns nil." 297 | (interactive) 298 | (let ((language-id 299 | (let ((language-id--file-name-extension 300 | (downcase (file-name-extension (or (buffer-file-name) "") 301 | t)))) 302 | (cl-some (lambda (definition) 303 | (cl-destructuring-bind (language-id &rest modes) 304 | definition 305 | (when (cl-some #'language-id--mode-match-p modes) 306 | language-id))) 307 | language-id--definitions)))) 308 | (when (called-interactively-p 'interactive) 309 | (message "%s" (or language-id "Unknown"))) 310 | language-id)) 311 | 312 | (provide 'language-id) 313 | 314 | ;;; language-id.el ends here 315 | --------------------------------------------------------------------------------