1package mode 2 3import ( 4 "path/filepath" 5 "strconv" 6 "strings" 7) 8 9// Detect looks at the filename and tries to guess what could be an appropriate editor mode. 10func Detect(filename string) Mode { 11 12 // A list of the most common configuration filenames that does not have an extension 13 var ( 14 configFilenames = []string{"fstab", "config", "BUILD", "WORKSPACE", "passwd", "group", "environment", "shadow", "gshadow", "hostname", "hosts", "issue", "mirrorlist"} 15 mode Mode 16 ) 17 18 baseFilename := filepath.Base(filename) 19 ext := filepath.Ext(baseFilename) 20 21 // Check if we should be in a particular mode for a particular type of file 22 switch { 23 case baseFilename == "COMMIT_EDITMSG" || 24 baseFilename == "MERGE_MSG" || 25 (strings.HasPrefix(baseFilename, "git-") && 26 !strings.Contains(baseFilename, ".") && 27 strings.Count(baseFilename, "-") >= 2): 28 // Git mode 29 mode = Git 30 case ext == ".vimrc" || ext == ".vim" || ext == ".nvim": 31 mode = Vim 32 case strings.HasPrefix(baseFilename, "Makefile") || strings.HasPrefix(baseFilename, "makefile") || baseFilename == "GNUmakefile": 33 // NOTE: This one MUST come before the ext == "" check below! 34 mode = Makefile 35 case strings.HasSuffix(filename, ".git/config") || ext == ".ini" || ext == ".cfg" || ext == ".conf" || ext == ".service" || ext == ".target" || ext == ".socket" || strings.HasPrefix(ext, "rc"): 36 fallthrough 37 case ext == ".yml" || ext == ".toml" || ext == ".ini" || ext == ".bp" || ext == ".rule" || strings.HasSuffix(filename, ".git/config") || (ext == "" && (strings.HasSuffix(baseFilename, "file") || strings.HasSuffix(baseFilename, "rc") || hasS(configFilenames, baseFilename))): 38 mode = Config 39 case ext == ".sh" || ext == ".ksh" || ext == ".tcsh" || ext == ".bash" || ext == ".zsh" || ext == ".local" || ext == ".profile" || baseFilename == "PKGBUILD" || (strings.HasPrefix(baseFilename, ".") && strings.Contains(baseFilename, "sh")): // This last part covers .bashrc, .zshrc etc 40 mode = Shell 41 case ext == ".bzl" || baseFilename == "BUILD" || baseFilename == "WORKSPACE": 42 mode = Bazel 43 case baseFilename == "CMakeLists.txt" || ext == ".cmake": 44 mode = CMake 45 default: 46 switch ext { 47 case ".s", ".S", ".asm", ".inc": 48 // Go-style assembly (modeGoAssembly) is enabled if a mid-dot is discovered 49 mode = Assembly 50 //case ".s": 51 //mode = GoAssembly 52 case ".amber": 53 mode = Amber 54 case ".go": 55 mode = Go 56 case ".odin": 57 mode = Odin 58 case ".hs": 59 mode = Haskell 60 case ".sml": 61 mode = StandardML 62 case ".m4": 63 mode = M4 64 case ".ml": 65 mode = OCaml // or standard ML, if the file does not contain ";;" 66 case ".py": 67 mode = Python 68 case ".pl": 69 mode = Perl 70 case ".md": 71 // Markdown mode 72 mode = Markdown 73 case ".bts": 74 mode = Battlestar 75 case ".cpp", ".cc", ".c++", ".cxx", ".hpp", ".h": 76 // C++ mode 77 // TODO: Find a way to discover is a .h file is most likely to be C or C++ 78 mode = Cpp 79 case ".c": 80 // C mode 81 mode = C 82 case ".d": 83 // D mode 84 mode = D 85 case ".cs": 86 // C# mode 87 mode = CS 88 case ".adoc", ".rst", ".scdoc", ".scd": 89 // Markdown-like syntax highlighting 90 // TODO: Introduce a separate mode for these. 91 mode = Markdown 92 case ".txt", ".text", ".nfo", ".diz": 93 mode = Text 94 case ".clj", ".clojure", "cljs": 95 mode = Clojure 96 case ".lsp", ".emacs", ".el", ".elisp", ".lisp", ".cl", ".l": 97 mode = Lisp 98 case ".zig", ".zir": 99 mode = Zig 100 case ".v": 101 mode = V 102 case ".kt", ".kts": 103 mode = Kotlin 104 case ".java": 105 mode = Java 106 case ".gradle": 107 mode = Gradle 108 case ".hal": 109 mode = HIDL 110 case ".aidl": 111 mode = AIDL 112 case ".sql": 113 mode = SQL 114 case ".ok": 115 mode = Oak 116 case ".rs": 117 mode = Rust 118 case ".lua": 119 mode = Lua 120 case ".cr": 121 mode = Crystal 122 case ".nim": 123 mode = Nim 124 case ".pas", ".pp", ".lpr": 125 mode = ObjectPascal 126 case ".bat": 127 mode = Bat 128 case ".adb", ".gpr", ".ads", ".ada": 129 mode = Ada 130 case ".htm", ".html": 131 mode = HTML 132 case ".xml": 133 mode = XML 134 case ".te": 135 mode = PolicyLanguage 136 case ".1", ".2", ".3", ".4", ".5", ".6", ".7", ".8": 137 mode = Nroff 138 case ".scala": 139 mode = Scala 140 case ".json", ".ipynb": 141 mode = JSON 142 case ".js": 143 mode = JavaScript 144 case ".ts": 145 mode = TypeScript 146 default: 147 mode = Blank 148 } 149 } 150 151 if mode == Text { 152 mode = Markdown 153 } 154 155 // If the mode is not set, and there is no extensions 156 if mode == Blank && !strings.Contains(baseFilename, ".") { 157 if baseFilename == strings.ToUpper(baseFilename) { 158 // If the filename is all uppercase and no ".", use mode.Markdown 159 mode = Markdown 160 } else if len(baseFilename) > 2 && baseFilename[2] == '-' { 161 // Could it be a rule-file, that starts with ie. "90-" ? 162 if _, err := strconv.Atoi(baseFilename[:2]); err == nil { // success 163 // Yes, assume this is a shell-like configuration file 164 mode = Config 165 } 166 } 167 } 168 169 return mode 170} 171