├── .gitignore ├── LICENSE.md ├── README.md ├── lib └── editor-settings.coffee ├── menus └── editor-settings.cson └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | npm-debug.log 3 | node_modules 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Jack Polgar 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # editor-settings package 2 | 3 | Adds support for per-language, file extension and directory editor settings. 4 | 5 | ## How to use it 6 | ### Global configuration 7 | 8 | Open a file you'd like to configure the settings for, ensure that its grammar is correctly 9 | detected (look at the grammar selector in the bottom right corner of Atom or press ⌃⇧L) 10 | and open the command palette (⌘⇧P or ⌃⇧P). Type `Editor Settings: Open Grammar Config` 11 | and press ⏎. 12 | 13 | A new `.cson` file for the grammar of the file you had open should show with the current 14 | editor settings for the given grammar. Edit it and save. 15 | 16 | The language/grammar settings files are saved in the `grammar-config` directory located 17 | in the main Atom configuration directory with a lower-case file name format. 18 | 19 | For example, the config file for CoffeeScript would be `coffeescript.cson`. 20 | 21 | ### Local configuration 22 | 23 | For a per directory based configuration, you can create a `.editor-settings` file in the 24 | concerned directories. 25 | 26 | ### Supported settings 27 | 28 | The API for setting editor settings currently only supports: 29 | 30 | - Tab length (tabLength) 31 | - Soft/hard tabs (softTabs) 32 | - Soft wrap (softWrap) 33 | - Encoding (encoding) 34 | - Atom and Syntax theme (themes) 35 | 36 | ### Example configuration 37 | 38 | The following example is for CoffeeScript, it sets the tab length to `2`, but if 39 | the file extension is `.cson` it sets it to `4`. 40 | 41 | tabLength: 2 42 | extensionConfig: 43 | cson: 44 | tabLength: 4 45 | 46 | #### Experimental settings: 47 | 48 | fontFamily: 'Source Code Pro' 49 | grammarConfig: 50 | 'GitHub Markdown': 51 | fontFamily: 'monospace' 52 | 53 | 54 | #### Example project and directory configuration 55 | 56 | All options not nested under a specific grammar are used for all grammar and extensions. 57 | 58 | tabLength: 2 59 | themes: ['atom-light-ui', 'atom-light-syntax'] 60 | grammarConfig: 61 | 'PHP': 62 | tabLength: 4 63 | softTabs: true 64 | extensionConfig: 65 | phtml: 66 | softTabs: false 67 | 68 | ## Features 69 | 70 | - Per-language support 71 | - Per-file extension support 72 | - Per-project support 73 | - Per-directory support 74 | 75 | ### Planned 76 | 77 | - Change configuration directory 78 | 79 | If there is a feature you'd like added simply create an issue or fork and implement it and send a pull request. 80 | -------------------------------------------------------------------------------- /lib/editor-settings.coffee: -------------------------------------------------------------------------------- 1 | fs = require 'fs' 2 | path = require 'path' 3 | CSONParser = require 'cson-parser' 4 | 5 | # Config file examples: 6 | # 7 | # CoffeeScript grammar config example: 8 | # tabLength: 2 9 | # extensionConfig: 10 | # cson: 11 | # tabLength: 4 12 | # 13 | # Project / Directory config example: 14 | # tabLength: 2 15 | # grammarConfig: 16 | # 'CoffeeScript': 17 | # tabLength 4 18 | # extensionConfig: 19 | # cson: 20 | # tabLength 8 21 | 22 | module.exports = 23 | config: 24 | debug: 25 | type: 'boolean' 26 | default: false 27 | 28 | activate: -> 29 | console.log 'activate editor-settings' 30 | 31 | @watching = [] 32 | @configDir = atom.getConfigDirPath() + "/grammar-config" 33 | 34 | # Create config directory if it doesn't exist. 35 | if not fs.existsSync @configDir 36 | fs.mkdirSync @configDir 37 | 38 | @registerCommands() 39 | 40 | atom.workspace.onDidChangeActivePaneItem => 41 | @reconfigureCurrentEditor() 42 | 43 | atom.workspace.observeTextEditors (editor) => 44 | editor.observeGrammar => 45 | @reconfigureCurrentEditor() 46 | 47 | @reconfigureCurrentEditor() 48 | 49 | debug: (msg) -> 50 | if atom.config.get('editor-settings.debug') 51 | console.log msg 52 | 53 | registerCommands: -> 54 | atom.commands.add 'atom-text-editor', 55 | 'editor-settings:open-grammar-config': => @editCurrentGrammarConfig() 56 | 57 | # Reconfigure the current editor 58 | reconfigureCurrentEditor: -> 59 | editor = atom.workspace.getActiveTextEditor() 60 | 61 | @debug "reconfigure current editor" 62 | 63 | if editor? 64 | config = @loadAllConfigFiles(editor.getGrammar().name) 65 | 66 | editor.setTabLength config.tabLength if config.tabLength? 67 | editor.setSoftTabs config.softTabs if config.softTabs? 68 | editor.setSoftWrapped config.softWrap if config.softWrap? 69 | atom.config.settings.core.themes = [config.themes[0], config.themes[1]] if config.themes? 70 | 71 | if editor.buffer? 72 | buffer = editor.buffer 73 | buffer.setEncoding config.encoding if config.encoding 74 | 75 | view = atom.views.getView(editor) 76 | if view? 77 | view.style.fontFamily = config.fontFamily if config.fontFamily? 78 | view.style.fontSize = config.fontSize if config.fontSize? 79 | 80 | # Load the contents of all config files: 81 | # - grammar 82 | # - project 83 | # - current file directory 84 | loadAllConfigFiles: (grammarName) -> 85 | editor = atom.workspace.getActiveTextEditor() 86 | 87 | # File extesion 88 | # Deprecation notice showed when new window w/o grammar is opened 89 | # Easy fix, set untitled window to plain text 90 | if 'undefined' == typeof editor.getPath() 91 | @debug 'new window without grammar, setting plan text' 92 | fileExtension = 'txt' 93 | else 94 | fileExtension = path.extname(editor.getPath()).substring(1) 95 | 96 | @debug 'current editor file extension: ' + fileExtension 97 | 98 | config = {} 99 | 100 | # Default and current Atom settings 101 | defaults = @merge atom.config.defaultSettings.editor, 102 | atom.config.settings.editor 103 | 104 | config = @merge config, defaults 105 | 106 | # Grammar settings 107 | if fs.existsSync @grammarConfigPath(grammarName) 108 | grammarConfig = @loadConfig(@grammarConfigPath(grammarName)) 109 | @debug 'loading grammar config: ' + grammarName 110 | config = @merge config, grammarConfig 111 | else 112 | @debug 'no grammar config for: ' + grammarName 113 | 114 | # Project settings 115 | if atom.project?.getPaths() && editor.buffer.file?.getParent() 116 | projectPaths = atom.project.getPaths() 117 | 118 | if editor.buffer.file? 119 | directoryPath = editor.buffer.file.getParent().path 120 | 121 | for i of projectPaths 122 | if directoryPath.indexOf(projectPaths[i]) == 0 123 | projectConfigPath = projectPaths[i] + '/.editor-settings' 124 | break 125 | 126 | if projectConfig = @loadConfig(projectConfigPath) 127 | @debug 'loading project config: ' + projectConfigPath 128 | config = @merge config, projectConfig 129 | 130 | # Directory settings 131 | if editor.buffer?.file?.getParent()?.path? 132 | directoryPath = editor.buffer.file.getParent().path 133 | directoryConfigPath = directoryPath + "/.editor-settings" 134 | 135 | if directoryConfig = @loadConfig(directoryConfigPath) 136 | @debug 'loading directory config: ' + directoryConfigPath 137 | config = @merge config, directoryConfig 138 | 139 | if config.grammarConfig?[grammarName]? 140 | @debug 'merging grammar config: ' + grammarName 141 | config = @merge config, config.grammarConfig[grammarName] 142 | 143 | if config.extensionConfig?[fileExtension]? and fileExtension.length > 0 144 | @debug 'merging file extension config: ' + fileExtension 145 | config = @merge config, config.extensionConfig[fileExtension] 146 | 147 | return config 148 | 149 | # Merge two objects 150 | merge: (first, second) -> 151 | config = {} 152 | 153 | for a, b of first 154 | if typeof b is "object" 155 | config[a] = @merge {}, b 156 | else 157 | config[a] = b 158 | 159 | for c, d of second 160 | if typeof d is "object" 161 | config[c] = @merge {}, d 162 | else 163 | config[c] = d 164 | 165 | return config 166 | 167 | # Open current editors grammar config file 168 | editCurrentGrammarConfig: -> 169 | workspace = atom.workspace? 170 | 171 | return unless workspace 172 | 173 | editor = atom.workspace.getActiveTextEditor() 174 | 175 | if editor? 176 | grammar = editor.getGrammar() 177 | filePath = @grammarConfigPath(grammar.name) 178 | 179 | if not fs.existsSync filePath 180 | fs.writeFileSync filePath, '' 181 | 182 | @watchFile filePath 183 | atom.workspace.open filePath 184 | 185 | # Watch file 186 | watchFile: (path) -> 187 | unless @watching[path] 188 | fs.watch path, => 189 | @debug 'watched file updated: ' + path 190 | @reconfigureCurrentEditor() 191 | 192 | @debug 'watching: ' + path 193 | @watching[path] = true 194 | 195 | # Converts the grammar name to a file name. 196 | fileNameFor: (text) -> 197 | text.replace(/\s+/gi, '-').toLowerCase() 198 | 199 | # Returns full config file path for specified grammar 200 | grammarConfigPath: (name) -> 201 | fileName = @fileNameFor(name) 202 | return @configDir + "/" + fileName + ".cson" 203 | 204 | loadConfig: (path) -> 205 | if fs.existsSync(path) 206 | contents = fs.readFileSync(path) 207 | @watchFile path 208 | 209 | if contents.length > 1 210 | try 211 | return CSONParser.parse(contents) 212 | catch error 213 | console.log error 214 | -------------------------------------------------------------------------------- /menus/editor-settings.cson: -------------------------------------------------------------------------------- 1 | 'menu': [ 2 | { 3 | 'label': 'Packages' 4 | 'submenu': [ 5 | { 6 | 'label': 'Editor settings', 7 | 'submenu': [ 8 | { 9 | 'label': 'Edit grammar config' 10 | 'command': 'editor-settings:open-grammar-config' 11 | } 12 | ] 13 | } 14 | ] 15 | } 16 | ] 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "editor-settings", 3 | "main": "./lib/editor-settings", 4 | "version": "1.1.3", 5 | "description": "Editor settings per-language, extension and directory.", 6 | "repository": "https://github.com/nirix/atom-editor-settings", 7 | "license": "MIT", 8 | "keywords": [ 9 | "editor", 10 | "settings", 11 | "syntax", 12 | "grammar", 13 | "language", 14 | "project", 15 | "directory", 16 | "specific", 17 | "context" 18 | ], 19 | "engines": { 20 | "atom": ">0.50.0" 21 | }, 22 | "dependencies": { 23 | "cson-parser": "~1.0.0" 24 | } 25 | } 26 | --------------------------------------------------------------------------------