├── D.sublime-build ├── DKit.py ├── DKit.sublime-commands ├── DKit.sublime-settings ├── Default (Linux).sublime-keymap ├── Default (OSX).sublime-keymap ├── Default (Window).sublime-keymap ├── Main.sublime-menu ├── Preferences.sublime-settings └── README.md /D.sublime-build: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": ["dmd", "-g", "-debug", "$file"], 3 | "file_regex": "^(.*?)\\(([0-9]+),?([0-9]+)?\\): (.*)", 4 | "selector": "source.d", 5 | 6 | "variants": 7 | [ 8 | { 9 | "name": "Run", 10 | "cmd": ["rdmd", "-g", "-debug", "$file"] 11 | }, 12 | { 13 | "name": "dub", 14 | "working_dir": "$project_path", 15 | "cmd": ["dub"] 16 | }, 17 | { 18 | "name": "dub (single file)", 19 | "working_dir": "$project_path", 20 | "cmd": ["dub", "--single", "$file"] 21 | }, 22 | { 23 | "name": "dub build", 24 | "working_dir": "$project_path", 25 | "cmd": ["dub", "build"] 26 | }, 27 | { 28 | "name": "dub build (single file)", 29 | "working_dir": "$project_path", 30 | "cmd": ["dub", "build", "--single", "$file"] 31 | }, 32 | { 33 | "name": "dub test", 34 | "working_dir": "$project_path", 35 | "cmd": ["dub", "test"] 36 | } 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /DKit.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import sublime, sublime_plugin 4 | from subprocess import Popen, PIPE, call 5 | import os 6 | import json 7 | import sys 8 | import functools 9 | import re 10 | 11 | plugin_settings = None 12 | server_port = 9166 13 | server_path = None 14 | client_path = "" 15 | 16 | server_process = None 17 | 18 | ON_LOAD = sublime_plugin.all_callbacks['on_load'] 19 | 20 | def get_shell_args(args): 21 | return ' '.join(args) 22 | 23 | def read_settings(key, default): 24 | global plugin_settings 25 | if plugin_settings is None: 26 | plugin_settings = sublime.load_settings('DKit.sublime-settings') 27 | 28 | return sublime.active_window().active_view().settings().get(key, plugin_settings.get(key, default)) 29 | 30 | def read_all_settings(key): 31 | global plugin_settings 32 | if plugin_settings is None: 33 | plugin_settings = sublime.load_settings('DKit.sublime-settings') 34 | 35 | result = plugin_settings.get(key, []) 36 | result.extend(sublime.active_window().active_view().settings().get(key, [])) 37 | return result 38 | 39 | def normalize_from_project_dir(file): 40 | project_dir = os.path.dirname(sublime.active_window().project_file_name()) 41 | return os.path.normpath(os.path.join(project_dir, file)) 42 | 43 | def open_file(filename): 44 | if sys.platform == 'win32': 45 | os.startfile(filename) 46 | else: 47 | opener = 'open' if sys.platform == 'darwin' else 'xdg-open' 48 | call([opener, filename]) 49 | 50 | def goto_offset(view, offset): 51 | region = sublime.Region(offset) 52 | view.sel().clear() 53 | view.sel().add(region) 54 | view.show_at_center(region) 55 | 56 | def on_load(path=None, window=None, encoded_row_col=True, begin_edit=False): 57 | """Decorator to open or switch to a file. 58 | 59 | Opens and calls the "decorated function" for the file specified by path, 60 | or the current file if no path is specified. In the case of the former, if 61 | the file is open in another tab that tab will gain focus, otherwise the 62 | file will be opened in a new tab with a requisite delay to allow the file 63 | to open. In the latter case, the "decorated function" will be called on 64 | the currently open file. 65 | 66 | :param path: path to a file 67 | :param window: the window to open the file in 68 | :param encoded_row_col: the ``sublime.ENCODED_POSITION`` flag for 69 | ``sublime.Window.open_file`` 70 | :param begin_edit: if editing the file being opened 71 | 72 | :returns: None 73 | """ 74 | window = window or sublime.active_window() 75 | 76 | def wrapper(f): 77 | # if no path, tag is in current open file, return that 78 | if not path: 79 | return f(window.active_view()) 80 | # else, open the relevant file 81 | view = window.open_file(os.path.normpath(path), encoded_row_col) 82 | 83 | def wrapped(): 84 | # if editing the open file 85 | if begin_edit: 86 | with sublime.Edit(view): 87 | f(view) 88 | else: 89 | f(view) 90 | 91 | # if buffer is still loading, wait for it to complete then proceed 92 | if view.is_loading(): 93 | 94 | class set_on_load(): 95 | callbacks = ON_LOAD 96 | 97 | def __init__(self): 98 | # append self to callbacks 99 | self.callbacks.append(self) 100 | 101 | def remove(self): 102 | # remove self from callbacks, hence disconnecting it 103 | self.callbacks.remove(self) 104 | 105 | def on_load(self, view): 106 | # on file loading 107 | try: 108 | wrapped() 109 | finally: 110 | # disconnect callback 111 | self.remove() 112 | 113 | set_on_load() 114 | # else just proceed (file was likely open already in another tab) 115 | else: 116 | wrapped() 117 | 118 | return wrapper 119 | 120 | def start_server(): 121 | global server_process 122 | global client_path 123 | 124 | out = call(client_path + " -q", shell=True, stdout=PIPE) 125 | if out == 0: 126 | print("Already running!") 127 | return 128 | 129 | global plugin_settings 130 | plugin_settings = sublime.load_settings('DKit.sublime-settings') 131 | 132 | global server_port 133 | server_port = read_settings('dcd_port', 9166) 134 | dcd_path = read_settings('dcd_path', '') 135 | 136 | global server_path 137 | server_path = os.path.join(dcd_path, 'dcd-server' + ('.exe' if sys.platform == 'win32' else '')) 138 | client_path = os.path.join(dcd_path, 'dcd-client' + ('.exe' if sys.platform == 'win32' else '')) 139 | server_path = sublime.expand_variables(server_path, sublime.active_window().extract_variables()) 140 | client_path = sublime.expand_variables(client_path, sublime.active_window().extract_variables()) 141 | 142 | if not os.path.exists(server_path): 143 | sublime.error_message('DCD server doesn\'t exist in the path specified:\n' + server_path + '\n\nSetup the path in DCD package settings and then restart sublime to get things working.') 144 | return False 145 | 146 | if not os.path.exists(client_path): 147 | sublime.error_message('DCD client doesn\'t exist in the path specified:\n' + client_path + '\n\nSetup the path in DCD package settings and then restart sublime to get things working.') 148 | return False 149 | 150 | include_paths = read_all_settings('include_paths') 151 | include_paths = ['-I"' + p + '"' for p in include_paths] 152 | 153 | args = ['"%s"' % server_path] 154 | args.extend(include_paths) 155 | args.extend(['-p' + str(server_port)]) 156 | 157 | print('Restarting DCD server...') 158 | server_process = Popen(get_shell_args(args), shell=True) 159 | return True 160 | 161 | def update_project(view, package_file): 162 | dub = Popen(get_shell_args(['dub', 'describe']), stdin=PIPE, stdout=PIPE, shell=True, cwd=os.path.dirname(package_file)) 163 | description = dub.communicate() 164 | description = description[0].decode('utf-8') 165 | 166 | if description.startswith('Checking dependencies'): 167 | end_of_line = description.find('\n') 168 | description = description[end_of_line:] 169 | 170 | try: 171 | description = json.loads(description) 172 | except ValueError: 173 | sublime.error_message('Please run DUB at least once to figure out dependencies before trying again. Aborting.') #TODO: change into something more user-friendly 174 | return False 175 | 176 | include_paths = set() 177 | package_paths = [] 178 | 179 | #new project set up 180 | project_window = view.window() 181 | project_settings = project_window.project_data() 182 | 183 | #if we have no project and no folder open, create new project data 184 | if project_settings is None: 185 | project_settings = {} 186 | 187 | if 'folders' not in project_settings: 188 | project_settings['folders'] = [] 189 | 190 | current_folders = set([folder['path'] for folder in project_settings['folders']]) 191 | 192 | #get dub project info 193 | for index, package in enumerate(description['packages']): 194 | base_path = os.path.abspath(package['path']) 195 | 196 | #skip all but the top level package when suppressing folders 197 | if index == 0 or not read_settings("suppress_dependency_folders", False): 198 | if base_path not in current_folders: 199 | package_paths.append({'path': base_path, 'name': package['name']}) 200 | 201 | for f in package['importPaths']: 202 | folder = os.path.join(base_path, f) 203 | include_paths.add(folder) 204 | 205 | settings = {'include_paths': [f for f in include_paths], 'package_file': package_file} 206 | 207 | project_settings['folders'].extend(package_paths) 208 | project_settings.update({'settings': settings}) 209 | 210 | #update the window with the new project data 211 | project_window.set_project_data(project_settings) 212 | 213 | return True 214 | 215 | class DCD(sublime_plugin.EventListener): 216 | def __exit__(self, type, value, traceback): 217 | global server_process 218 | if not (server_process is None) and server_process.poll() is None: 219 | server_process.terminate() 220 | 221 | def on_window_command(self, window, command_name, args): 222 | if command_name == "exit": 223 | Popen(client_path + " --shutdown", shell=True) 224 | 225 | def on_query_completions(self, view, prefix, locations): 226 | if not view.scope_name(locations[0]).startswith('source.d'): 227 | return 228 | 229 | global server_process 230 | if server_process is None: 231 | start_server() 232 | 233 | position = locations[0] 234 | position = position - len(prefix) 235 | if (view.substr(position) != '.'): 236 | position = locations[0] 237 | 238 | response = self.request_completions(view.substr(sublime.Region(0, view.size())), position) 239 | return (response, sublime.INHIBIT_WORD_COMPLETIONS | sublime.INHIBIT_EXPLICIT_COMPLETIONS) 240 | 241 | def request_completions(self, file, position): 242 | args = ['"%s"' % client_path, '-c', str(position), '-p', str(server_port)] 243 | client = Popen(get_shell_args(args), stdin=PIPE, stdout=PIPE, shell=True) 244 | 245 | output = client.communicate(file.encode()) 246 | output = output[0].decode('utf-8').splitlines() 247 | if len(output) == 0: 248 | return [] 249 | 250 | completions_type = output[0] 251 | del output[0] 252 | 253 | if (completions_type == 'identifiers'): 254 | output = [self.parse_identifiers(line) for line in output] 255 | elif (completions_type == 'calltips'): 256 | output = [self.parse_calltips(line) for line in output] 257 | else: 258 | output = [] 259 | 260 | return output 261 | 262 | def parse_identifiers(self, line): 263 | parts = line.split('\t') 264 | 265 | if len(parts) == 2: 266 | cmap = { 267 | 'c': 'class', 268 | 'i': 'interface', 269 | 's': 'struct', 270 | 'u': 'union', 271 | 'v': 'variable', 272 | 'm': 'member variable', 273 | 'k': 'keyword', 274 | 'f': 'function', 275 | 'g': 'enum', 276 | 'e': 'enum member', 277 | 'P': 'package', 278 | 'M': 'module', 279 | 'a': 'array', 280 | 'A': 'associative array', 281 | 'l': 'alias', 282 | 't': 'template', 283 | 'T': 'mixin template'} 284 | 285 | visible_name = parts[0] + '\t' + cmap.get(parts[1], ' ') 286 | text = parts[0] 287 | 288 | return visible_name, text 289 | 290 | else: 291 | return None 292 | 293 | def parse_calltips(self, line): 294 | index = line.find('(') 295 | if index >= 0: 296 | visible_name = line 297 | text = line[index + 1 : -1] 298 | else: 299 | visible_name = line 300 | text = line 301 | return visible_name, text 302 | 303 | class DcdStartServerCommand(sublime_plugin.ApplicationCommand): 304 | def run(self): 305 | start_server() 306 | 307 | class DcdUpdateIncludePathsCommand(sublime_plugin.TextCommand): 308 | def run(self, edit): 309 | global client_path 310 | Popen(get_shell_args(['"%s"' % client_path, '--clearCache']), shell=True).wait() 311 | 312 | include_paths = set() 313 | for path in self.view.settings().get('include_paths', []): 314 | include_paths.add(path) 315 | 316 | if self.view.file_name(): 317 | include_paths.add(os.path.dirname(self.view.file_name())) 318 | 319 | if len(include_paths) > 0: 320 | args = ['"%s"' % client_path, '-p', str(server_port)] 321 | args.extend(['-I' + p for p in include_paths]) 322 | 323 | Popen(get_shell_args(args), shell=True).wait() 324 | 325 | class DcdGotoDefinitionCommand(sublime_plugin.TextCommand): 326 | def run(self, edit): 327 | global client_path 328 | if (len(self.view.sel()) != 1): 329 | sublime.error_message('Please set the cursor on the token to check.') 330 | return 331 | 332 | pos = self.view.sel()[0].a 333 | args = ['"%s"' % client_path, '--symbolLocation', '-c', str(pos), '-p', str(server_port)] 334 | 335 | client = Popen(get_shell_args(args), stdin=PIPE, stdout=PIPE, shell=True) 336 | contents = self.view.substr(sublime.Region(0, self.view.size())) 337 | output = client.communicate(contents.encode()) 338 | output = output[0].decode('utf-8').strip() 339 | if len(output) == 0 or output == 'Not found': 340 | sublime.error_message('No symbol definition found.') 341 | return 342 | 343 | output = output.split('\t', 1) 344 | path = output[0].strip() 345 | offset = int(output[1].strip()) 346 | 347 | if path == 'stdin': 348 | goto_offset(self.view, offset) 349 | else: 350 | @on_load(path) 351 | def and_then(view): 352 | sublime.set_timeout(functools.partial(goto_offset, view, offset), 10) 353 | 354 | 355 | class DcdShowDocumentationCommand(sublime_plugin.TextCommand): 356 | _REGEX = re.compile(r'(?[\\\\\\\'"abfnrtv])|' 358 | '(?P[0-7]{1,3})|' 359 | 'x(?P[0-9a-fA-F]{1,2})|' 360 | 'u(?P[0-9a-fA-F]{4})|' 361 | 'U(?P[0-9a-fA-F]{8}))') 362 | 363 | def run(self, edit): 364 | global client_path 365 | if (len(self.view.sel()) != 1): 366 | sublime.error_message('Please set the cursor on the token to check.') 367 | return 368 | 369 | pos = self.view.sel()[0].a 370 | args = ['"%s"' % client_path, '--doc', '-c', str(pos), '-p', str(server_port)] 371 | 372 | client = Popen(get_shell_args(args), stdin=PIPE, stdout=PIPE, shell=True) 373 | contents = self.view.substr(sublime.Region(0, self.view.size())) 374 | output = client.communicate(contents.encode()) 375 | output = output[0].decode('utf-8').strip() 376 | if len(output) == 0 or output == 'Not found': 377 | sublime.error_message('No documentation found.') 378 | return 379 | 380 | docs = self._REGEX.sub(self._process_escape_codes, output.replace('\n', '\n\n')) 381 | 382 | panel = sublime.active_window().create_output_panel('ddoc') 383 | panel.insert(edit, 0, docs) 384 | sublime.active_window().run_command("show_panel", {"panel": "output.ddoc"}) 385 | 386 | _ESCAPE_CODES = { 387 | '\\': '\\', 388 | "'": "'", 389 | '"': '"', 390 | 'a': '\a', 391 | 'f': '\f', 392 | 'n': '\n', 393 | 'r': '\r', 394 | 't': '\t', 395 | 'v': '\v', 396 | } 397 | 398 | def _process_escape_codes(self, match): 399 | c = match.group('code') 400 | if c is not None: 401 | return self._ESCAPE_CODES[c] 402 | o = match.group('oct') 403 | if c is not None: 404 | return chr(int(o, 8)) 405 | x = match.group('hex') or match.group('uni') or match.group('UNI') 406 | if x is not None: 407 | return chr(int(o, 16)) 408 | return match.group() 409 | 410 | class DubListInstalledCommand(sublime_plugin.TextCommand): 411 | def run(self, edit): 412 | try: 413 | dub = Popen(get_shell_args(['dub', 'list']), stdin=PIPE, stdout=PIPE, shell=True) 414 | output = dub.communicate() 415 | output = output[0].splitlines() 416 | del output[0] 417 | output = [o.decode('utf-8').strip().partition(': ')[0] for o in output] 418 | output = [o for o in output if len(o) > 0] 419 | self.view.window().show_quick_panel(output, None) 420 | except OSError: 421 | sublime.error_message('Unable to run DUB. Make sure that it is installed correctly and on your PATH environment variable') 422 | 423 | class DubCreatePackageCommand(sublime_plugin.WindowCommand): 424 | def run(self): 425 | view = self.window.new_file() 426 | view.set_name('dub.json') 427 | view.set_syntax_file('Packages/JavaScript/JSON.tmLanguage') 428 | view.run_command('dub_create_package_text') 429 | 430 | class DubCreatePackageTextCommand(sublime_plugin.TextCommand): 431 | def run(self, edit): 432 | package = """{ 433 | "name": "project-name", 434 | "description": "An example project skeleton", 435 | "homepage": "http://example.org", 436 | "copyright": "Copyright (c) 2014, Your Name", 437 | "authors": [], 438 | "dependencies": {}, 439 | "targetType": "executable" 440 | }""" 441 | self.view.insert(edit, 0, package) 442 | 443 | class DubCreateProjectFromPackageCommand(sublime_plugin.TextCommand): 444 | def run(self, edit): 445 | view = self.view 446 | if view.file_name(): 447 | package_file = view.file_name() 448 | package_filename = os.path.basename(view.file_name()) 449 | 450 | if package_filename != 'dub.json' and package_filename != 'dub.sdl' and package_filename != 'package.json' : 451 | sublime.error_message('Please open the `dub.json`, `dub.sdl` or `package.json` file and then run the command again.') 452 | return 453 | else: 454 | sublime.error_message('Please open the `dub.json`, `dub.sdl` or `package.json` file and then run the command again.') 455 | return 456 | 457 | if update_project(view, package_file): 458 | view.window().run_command('save_project_as') 459 | 460 | class DubUpdateProjectCommand(sublime_plugin.TextCommand): 461 | def run(self, edit): 462 | package_file = read_settings("package_file", None) 463 | if not package_file: 464 | sublime.error_message("The active project does not specify the path to the DUB package file.") 465 | return 466 | 467 | package_file = normalize_from_project_dir(package_file) 468 | update_project(self.view, package_file) 469 | -------------------------------------------------------------------------------- /DKit.sublime-commands: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "caption": "DKit: Restart DCD Autocompletion Server", 4 | "command": "dcd_start_server" 5 | }, 6 | { 7 | "caption": "DKit: Update Import Paths", 8 | "command": "dcd_update_include_paths" 9 | }, 10 | { 11 | "caption": "DKit: List Installed DUB Packages", 12 | "command": "dub_list_installed" 13 | }, 14 | { 15 | "caption": "DKit: Create DUB Package File", 16 | "command": "dub_create_package" 17 | }, 18 | { 19 | "caption": "DKit: Create Project From DUB Package File", 20 | "command": "dub_create_project_from_package" 21 | }, 22 | { 23 | "caption": "DKit: Update Project", 24 | "command": "dub_update_project" 25 | }, 26 | { 27 | "caption": "DKit: Goto Definition", 28 | "command": "dcd_goto_definition" 29 | }, 30 | { 31 | "caption": "DKit: Show Documentation", 32 | "command": "dcd_show_documentation" 33 | } 34 | ] 35 | -------------------------------------------------------------------------------- /DKit.sublime-settings: -------------------------------------------------------------------------------- 1 | { 2 | // Path to the directory containing both dcd-client and dcd-server executables 3 | "dcd_path": "/setup/my/path", 4 | // Port to use for DCD client and server 5 | "dcd_port": 9166, 6 | // Include path for existing source files. 7 | // DCD will also provide completion for dub dependencies 8 | "include_paths": [ 9 | "/usr/include/d/" 10 | ], 11 | "suppress_dependency_folders": false 12 | } 13 | -------------------------------------------------------------------------------- /Default (Linux).sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | { "keys": ["ctrl+shift+g"], "command": "dcd_goto_definition" }, 3 | { "keys": ["ctrl+\\"], "command": "dcd_show_documentation" } 4 | ] -------------------------------------------------------------------------------- /Default (OSX).sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | { "keys": ["super+shift+g"], "command": "dcd_goto_definition" }, 3 | { "keys": ["super+\\"], "command": "dcd_show_documentation" } 4 | ] -------------------------------------------------------------------------------- /Default (Window).sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | { "keys": ["ctrl+shift+g"], "command": "dcd_goto_definition" }, 3 | { "keys": ["ctrl+\\"], "command": "dcd_show_documentation" } 4 | ] -------------------------------------------------------------------------------- /Main.sublime-menu: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "caption": "Preferences", 4 | "mnemonic": "n", 5 | "id": "preferences", 6 | "children": [ 7 | { 8 | "caption": "Package Settings", 9 | "mnemonic": "P", 10 | "id": "package-settings", 11 | "children": [ 12 | { 13 | "caption": "DKit", 14 | "children": [ 15 | { 16 | "command": "open_file", 17 | "args": {"file": "${packages}/DKit/DKit.sublime-settings"}, 18 | "caption": "Settings – Default" 19 | }, 20 | { 21 | "command": "open_file", 22 | "args": {"file": "${packages}/User/DKit.sublime-settings"}, 23 | "caption": "Settings – User" 24 | } 25 | ] 26 | } 27 | ] 28 | } 29 | ] 30 | } 31 | ] 32 | -------------------------------------------------------------------------------- /Preferences.sublime-settings: -------------------------------------------------------------------------------- 1 | { 2 | "auto_complete_triggers": [ 3 | { 4 | "characters": ".", 5 | "selector": "source.d" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | DKit for Sublime Text 2 | --------------------- 3 | 4 | DKit is a package to aid developing D programs using Sublime Text 3. 5 | 6 | Features 7 | -------- 8 | 9 | - Autocompletion using [DCD](https://github.com/Hackerpilot/DCD) (D Completion Daemon). 10 | - Integration with [DUB](http://code.dlang.org/) build system. 11 | - Simple build systems using `rdmd` and `dub`. 12 | 13 | TODO 14 | ---- 15 | 16 | - Find a better way to show call tips (there are some sublime text limitations). 17 | - Better DUB integration 18 | 19 | Installation 20 | ------------ 21 | 22 | Currently I would like to postpone supporting Sublime Package Control installation until things stabilize with the plugin and proper testing is done on all platforms. 23 | Only Linux has been tested so far. Although it should work properly on other operating systems. (I welcome any feedback) 24 | 25 | To install the plugin: 26 | 27 | 1. Clone this repository into your Sublime Text packages folder. You can find where your folder is located by clicking on 'Preferences -> Browse Packages'. Probable places: 28 | * Linux - `~/.config/sublime-text-3/Packages/` 29 | * Windows - `sublime-text-3\Data\Packages` 30 | * Mac OSX - `~/Library/Application Support/Sublime Text 3/Packages` 31 | 32 | 2. You'll need to install [DCD](https://github.com/Hackerpilot/DCD) first. Follow the steps in DCD's [readme](https://github.com/Hackerpilot/DCD/blob/master/README.md#setup). 33 | 1. Do not run the server. DKit will automatically run the server when needed. 34 | 2. Go to 'Preferences -> Package Settings -> DKit -> Settings - User', and set it up like 'Settings - Default' to match your system. Notably you need to set dcd_path to your DCD's installation path. 35 | 3. Setup include_paths to your DMD installation. 36 | + On Linux, the default path to dlang includes are either (`/usr/include/dmd/phobos` and `/usr/include/dmd/druntime/import`) or (`/usr/include/dlang/dmd/`) on Arch-based distributions 37 | + On Windows, you should have your includes point to `d\\src\\phobos` and `d\\src\\druntime\\import` 38 | 4. You can also add include_paths per project. 39 | 40 | 3. To use DUB features, you'll need to have [DUB](https://github.com/rejectedsoftware/dub#installation) installed and in your PATH environment variable. 41 | 42 | 4. You might want to add custom key bindings for `Goto Definition` and `Show Documentation` commands or other commands. You can do that by going to `Preferences -> Key Bindings - User` and having the following as an example: 43 | `[{ "keys": ["ctrl+shift+g"], "command": "dcd_goto_definition" }, 44 | { "keys": ["ctrl+\\"], "command": "dcd_show_documentation" }]` 45 | 46 | Available Commands 47 | ------------------ 48 | 49 | - DKit: Restart DCD Autocompletion Server 50 | - If you notice that the autocompletion stopped working, try running this command to resolve the issue. 51 | - DKit: Update Import Paths 52 | - If you have just added a new file to your project or created a Sublime project from a DUB package, run this command to update the imports. 53 | - DKit: List Installed DUB Packages 54 | - This command lists the DUB packages installed as reported by DUB. 55 | - DKit: Create DUB Package File 56 | - This command creates a dub package template file. 57 | - DKit: Create Project From DUB Package File 58 | - You can create a Sublime project from a DUB package file using this command. Open the DUB package file, then run this to create the project. 59 | - DKit: Goto Definition (requires at least DCD 0.3.0-beta) 60 | - Use goto definition by setting the cursor in the symbol that you want to goto, and then running the command. The default keybinding for this command is ctrl+shift+g. 61 | - DKit: Show Documentation (requires at least DCD 0.3.0-beta) 62 | - Use show documentation by setting the cursor in the symbol that you want to see documentation for, and then running the command. The default keybinding for this command is ctrl+\\. 63 | 64 | Troubleshooting 65 | --------------- 66 | 67 | - If you find that your includes are not being suggested, try running `DKit: Update Import Paths` command in Sublime. 68 | - If you notice that the autocompletion stopped working, try running `DKit: Restart DCD Autocompletion Server` command in Sublime. 69 | - For other problems, please use the issue tracker. 70 | 71 | Linting 72 | ------- 73 | 74 | There is a sublime plugin for linting D code that uses [dscanner](https://github.com/Hackerpilot/Dscanner) at the following [link](https://github.com/economicmodeling/SublimeLinter-dscanner). 75 | 76 | Questions 77 | --------- 78 | 79 | Please use GitHub's issue tracker for questions and problems. 80 | --------------------------------------------------------------------------------