├── .gitignore ├── README.md ├── lib └── main.js ├── package.json └── script └── lint /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | npm-debug.log 3 | node_modules 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # C/C++ language support for Atom-IDE 2 | 3 | This project is still in its infancy, but the basic idea is to provide support 4 | for C++ and C to Atom-IDE by virtue of the highly experimental LLVM tool, 5 | [Clangd][clangd]. 6 | 7 | ## Development Status 8 | The current implementation should be considered **extremely** experimental, 9 | primarily because [Clangd][clangd] is still under heavy, initial development, 10 | and is therefore missing key functionality, contains bugs, etc. 11 | 12 | 13 | [clangd]: https://clang.llvm.org/extra/clangd.html 14 | -------------------------------------------------------------------------------- /lib/main.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | 4 | const { spawn } = require('child_process') 5 | const { AutoLanguageClient } = require('atom-languageclient') 6 | 7 | class CppLanguageClient extends AutoLanguageClient { 8 | getGrammarScopes () { return [ 'source.c', 'source.cpp' ] } 9 | getLanguageName () { return 'C++' } 10 | getServerName () { return 'Clangd' } 11 | 12 | startServerProcess (projectPath) { 13 | this.projectPath = projectPath 14 | 15 | const command = atom.config.get('ide-cpp.clangdPath') 16 | const args = [] 17 | const options = { 18 | // Clangd uses stderr for debug logging, so we have to ignore it. 19 | stdio: ['pipe', 'pipe', 'ignore'] 20 | } 21 | 22 | this.logger.debug(`Starting ${command} ${args.join(' ')}`) 23 | const serverProcess = spawn(command, args, options) 24 | 25 | serverProcess.on('error', error => { 26 | if (error.code === 'ENOENT') { 27 | atom.notifications.addError(`Unable to start ${this.getServerName()} language server`, { 28 | dismissable: true, 29 | description: '' + 30 | `Tried to spawn process using executable **${error.path}**, which does not exist. ` + 31 | `Ensure you have correctly configured the path to Clangd in the package settings.` 32 | }) 33 | } 34 | }) 35 | 36 | return Promise.resolve(serverProcess) 37 | } 38 | 39 | preInitialization (connection) { 40 | const didOpenTextDocument = connection.didOpenTextDocument.bind(connection) 41 | connection.didOpenTextDocument = (params) => { 42 | // TODO: Resolve the appropriate language ID (based on extension perhaps). 43 | params.textDocument.languageId = 'cpp' 44 | 45 | // Append non-standard 'metadata' to the 'textDocument/didOpen' payload, which currently 46 | // contains compilation flags needed by Clangd. 47 | params = this.appendClangMetadata(params) 48 | 49 | didOpenTextDocument(params) 50 | } 51 | } 52 | 53 | appendClangMetadata (params) { 54 | const flags = this.findCompilationFlags() 55 | if (flags) { 56 | // This non-standard addition is not documented anywhere (yet), but a test can be seen at 57 | // http://llvm.org/svn/llvm-project/clang-tools-extra/trunk/test/clangd/extra-flags.test 58 | params.metadata = { extraFlags: flags } 59 | } 60 | 61 | return params 62 | } 63 | 64 | findCompilationFlags () { 65 | // Additional details of the '.clang_complete' file can be found at 66 | // https://github.com/Rip-Rip/clang_complete 67 | const candidatePath = path.join(this.projectPath, '.clang_complete') 68 | if (fs.existsSync(candidatePath)) { 69 | return this.parseClangCompleteFile(candidatePath) 70 | } 71 | 72 | // TODO: Add support for additional file formats. 73 | 74 | return null 75 | } 76 | 77 | parseClangCompleteFile (filePath) { 78 | const rawData = fs.readFileSync(filePath) 79 | if (rawData) { 80 | const lines = rawData.toString().replace(/\r\n/, '\n').split('\n') 81 | return lines.filter(line => line.trim().length > 0) 82 | } 83 | } 84 | } 85 | 86 | module.exports = new CppLanguageClient() 87 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ide-cpp", 3 | "main": "./lib/main", 4 | "version": "0.1.0", 5 | "description": "C++ and C language support for Atom-IDE", 6 | "repository": "https://github.com/thomasjo/atom-ide-cpp", 7 | "license": "MIT", 8 | "keywords": [ 9 | "c++", 10 | "c", 11 | "ide", 12 | "clangd" 13 | ], 14 | "engines": { 15 | "atom": ">=1.21.0" 16 | }, 17 | "dependencies": { 18 | "atom-languageclient": "^0.6.3" 19 | }, 20 | "devDependencies": { 21 | "snazzy": "^7.0.0", 22 | "standard": "^10.0.3" 23 | }, 24 | "standard": { 25 | "globals": [ 26 | "afterEach", 27 | "atom", 28 | "beforeEach", 29 | "describe", 30 | "expect", 31 | "it", 32 | "jasmine", 33 | "runs", 34 | "spyOn", 35 | "waitsFor", 36 | "waitsForPromise" 37 | ] 38 | }, 39 | "enhancedScopes": [ 40 | "source.c", 41 | "source.cpp" 42 | ], 43 | "consumedServices": { 44 | "linter-indie": { 45 | "versions": { 46 | "2.0.0": "consumeLinterV2" 47 | } 48 | }, 49 | "datatip": { 50 | "versions": { 51 | "0.1.0": "consumeDatatip" 52 | } 53 | } 54 | }, 55 | "providedServices": { 56 | "autocomplete.provider": { 57 | "versions": { 58 | "2.0.0": "provideAutocomplete" 59 | } 60 | }, 61 | "code-actions": { 62 | "versions": { 63 | "0.1.0": "provideCodeActions" 64 | } 65 | }, 66 | "code-format.range": { 67 | "versions": { 68 | "0.1.0": "provideCodeFormat" 69 | } 70 | }, 71 | "code-highlight": { 72 | "versions": { 73 | "0.1.0": "provideCodeHighlight" 74 | } 75 | }, 76 | "definitions": { 77 | "versions": { 78 | "0.1.0": "provideDefinitions" 79 | } 80 | }, 81 | "find-references": { 82 | "versions": { 83 | "0.1.0": "provideFindReferences" 84 | } 85 | }, 86 | "signature-help": { 87 | "versions": { 88 | "0.1.0": "provideSignatureHelp" 89 | } 90 | }, 91 | "outline-view": { 92 | "versions": { 93 | "0.1.0": "provideOutlines" 94 | } 95 | } 96 | }, 97 | "configSchema": { 98 | "clangdPath": { 99 | "type": "string", 100 | "default": "/usr/bin/clangd" 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /script/lint: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Exit on failure, and treat expansion of unset variables as an error. 4 | set -eu 5 | 6 | lint() { 7 | local npm_bin=$( npm bin ) 8 | local paths="$1" 9 | 10 | "${npm_bin}/standard" --verbose "${paths}" | "${npm_bin}/snazzy" 11 | } 12 | 13 | main() { 14 | local script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd -P )" 15 | local source_dir=$( dirname "${script_dir}" ) 16 | 17 | local -i status=0 18 | echo "Linting package..." && lint "${source_dir}/lib/**/*.js" || status=$? 19 | echo "Linting package specs..." && lint "${source_dir}/spec/**/*.js" || status=$? 20 | return ${status} 21 | } 22 | 23 | main "$@" 24 | --------------------------------------------------------------------------------