├── .github ├── issue_template.md └── workflows │ └── ci.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── lib └── main.js └── package.json /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | A few things to know before you create an issue 2 | 3 | * This package requires [Atom 1.21](https://atom.io) 4 | * We launch a "language server" process on your machine that understands the language you are editing 5 | * What that server can do - syntax compatibility, whether it supports formatting or outlines etc - is outside of our control 6 | * You can see what the language server supports by checking out our README and following the link after "powered by" 7 | * "disconnected", "JSONRPC" or "Content-Header" errors indicate that server process died. We're working on better recovery right now 8 | 9 | If you have understood all this, please remove this text and start writing your issue :) 10 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push] 4 | 5 | env: 6 | CI: true 7 | 8 | jobs: 9 | Test: 10 | strategy: 11 | matrix: 12 | os: [ubuntu-latest, macos-latest, windows-latest] 13 | channel: [stable, beta] 14 | runs-on: ${{ matrix.os }} 15 | steps: 16 | - uses: actions/checkout@v1 17 | - uses: UziTech/action-setup-atom@v2 18 | with: 19 | version: ${{ matrix.channel }} 20 | - name: Install dependencies 21 | run: apm install 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | package-lock.json 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## v0.9.1 2 | 3 | - Generalize activation logic so that it works with all JS/TS language packages (@Aerijo) 4 | 5 | ## v0.9.0 6 | 7 | - Delay package activation until needed to improve Atom startup time (@laughedelic) 8 | - Prevent error after package deactivation 9 | 10 | ## v0.8.0 11 | 12 | The underlying language server has been changed to the [Theia IDE typescript-language-server](https://github.com/theia-ide/typescript-language-server). This resolves a number of important issues around module resolution, performance, version of TypeScript, tsconfig feature support etc. 13 | 14 | Thanks to the Theia IDE team for their language server and @mattlyons0 for helping figure out how to use it and push the necessary required changes upstream! 15 | 16 | - Changed language server to Theia IDE 17 | - Code Format now supported 18 | - No longer prints "Server failed to shutdown" on package deactivation 19 | - Many issues are likely resolved 20 | 21 | ## v0.7.9 22 | 23 | - Prevent initialization failure caused by spread operator on some versions of Atom 24 | 25 | ## v0.7.8 26 | 27 | - Prevent error when opening a file without first opening a directory/folder (project) 28 | 29 | Note: It is recommended that you always open the root of your project as a folder in order for the language server to correctly identify your projects tsconfig etc. 30 | 31 | ## v0.7.7 32 | 33 | - Update atom-languageclient to 0.9.7 to fix prompts, log messages, improve AutoComplete etc. 34 | - Setting to ignore flow projects 35 | - Setting to configure autocomplete suggestion priority 36 | 37 | ## v0.7.6 38 | 39 | - Compatibility with Atom's experimental tree-sitter parsing 40 | - Log messages from the language server are now shown in the Atom IDE UI Console window 41 | - Removed redundant fuzzaldrin-plus dependency 42 | 43 | ## v0.7.5 44 | 45 | - Update atom-languageclient to 0.9.1 to address missing trigger characters on some completions 46 | 47 | ## v0.7.4 48 | 49 | - Update atom-languageclient for much improved autocomplete 50 | 51 | ## v0.7.3 52 | 53 | - Update language server to make use of TypeScript 2.7.2 54 | - Update atom-languageclient for better language server stability 55 | 56 | ## v0.7.2 57 | 58 | - Update atom-language-client 59 | - Resolved autocomplete suggestions retain while filtering 60 | - Return type processing works again 61 | 62 | ## v0.7.1 63 | 64 | - Update language server 65 | - Update atom-language-client 66 | - Now resolves autocomplete suggestions 67 | - Force server shutdown after 2 seconds during quit 68 | - Add configuration switch to turn off diagnostics 69 | 70 | ## v0.7.0 71 | 72 | - Update language server 73 | - Update atom language client 74 | - Enable signature help 75 | 76 | ## v0.6.2 77 | 78 | - Update language server 79 | - Support document format if available 80 | - Dependency updates 81 | - README updates 82 | 83 | ## v0.6.1 84 | 85 | - Various README changes 86 | 87 | ## v0.6.0 88 | 89 | - Fix currentsuggestions errors #18 90 | - Add support for tsx/typescript react, fixes #15 91 | - Various README updates 92 | - Dependency updates 93 | 94 | ## v0.1.6 95 | 96 | - Do not throw when editing non-JS/TS files 97 | - Switch off streaming by upgrading to language server 2.2.1 98 | - Simplify English setting descriptions 99 | - Remove `=>` as a split condition for method sigs 100 | 101 | ## v0.1.5 102 | 103 | - Choice of left or right for return types in autocomplete 104 | 105 | ## v0.1.4 106 | 107 | - Ensure functions not messed up by skipping `(` 108 | 109 | ## v0.1.3 110 | 111 | - Split return and param signatures into left/right, fixes #7 112 | 113 | ## v0.1.2 114 | 115 | - Multi-project autocomplete filtering 116 | - Allow disabling of JavaScript support, fixes #5 117 | 118 | ## v0.1.1 119 | 120 | - Fix fuzzy-find autocomplete 121 | 122 | ## v0.1.0 123 | 124 | - First published release 125 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 GitHub Inc. 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 | ##### Atom and all repositories under Atom will be archived on December 15, 2022. Learn more in our [official announcement](https://github.blog/2022-06-08-sunsetting-atom/) 2 | # IDE-TypeScript package 3 | [![CI](https://github.com/atom/ide-typescript/actions/workflows/ci.yml/badge.svg)](https://github.com/atom/ide-typescript/actions/workflows/ci.yml) 4 | 5 | TypeScript and JavaScript language support for Atom-IDE, powered by the [Theia TypeScript Language Server](https://github.com/theia-ide/typescript-language-server). 6 | 7 | ![Screen shot of IDE-TypeScript](https://user-images.githubusercontent.com/118951/30306800-37e3c506-972f-11e7-805c-ba5a45a6bc3c.png) 8 | 9 | ## Early access 10 | 11 | This package is currently an early access release. You should also install the [atom-ide-ui](https://atom.io/packages/atom-ide-ui) package to expose the functionality within Atom. 12 | 13 | ## Features 14 | 15 | * Auto completion 16 | * Diagnostics (errors & warnings, with autofixes) 17 | * Document outline 18 | * Find references 19 | * Go to definition 20 | * Hover 21 | * Signature help 22 | * Format code 23 | 24 | ## Contributing 25 | Always feel free to help out! Whether it's [filing bugs and feature requests](https://github.com/atom/languageserver-typescript/issues/new) or working on some of the [open issues](https://github.com/atom/languageserver-typescript/issues), Atom's [contributing guide](https://github.com/atom/atom/blob/master/CONTRIBUTING.md) will help get you started while the [guide for contributing to packages](https://github.com/atom/atom/blob/master/docs/contributing-to-packages.md) has some extra information. 26 | 27 | ## License 28 | MIT License. See [the license](LICENSE.md) for more details. 29 | -------------------------------------------------------------------------------- /lib/main.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | const { AutoLanguageClient } = require('atom-languageclient') 4 | 5 | const jsScopes = ['source.js', 'source.js.jsx', 'javascript'] 6 | const tsScopes = ['source.ts', 'source.tsx', 'typescript'] 7 | const allScopes = tsScopes.concat(jsScopes) 8 | const tsExtensions = ['*.json', '.ts', '.tsx'] 9 | const jsExtensions = ['.js', '.jsx'] 10 | const allExtensions = tsExtensions.concat(jsExtensions) 11 | 12 | class TypeScriptLanguageClient extends AutoLanguageClient { 13 | getGrammarScopes() { 14 | return atom.config.get('ide-typescript.javascriptSupport') ? allScopes : tsScopes 15 | } 16 | getLanguageName() { return 'TypeScript' } 17 | getServerName() { return 'Theia' } 18 | 19 | startServerProcess(projectPath) { 20 | this.supportedExtensions = atom.config.get('ide-typescript.javascriptSupport') ? allExtensions : tsExtensions 21 | const serverPath = this.getServerPath(projectPath) 22 | console.info(`starting typescript server: ${serverPath}`) 23 | 24 | return super.spawnChildNode([ 25 | 'node_modules/typescript-language-server/lib/cli', 26 | '--stdio', 27 | '--tsserver-path', serverPath 28 | ], { cwd: path.join(__dirname, '..') }) 29 | } 30 | 31 | consumeLinterV2() { 32 | if (atom.config.get('ide-typescript.diagnosticsEnabled') === true) { 33 | super.consumeLinterV2.apply(this, arguments) 34 | } 35 | } 36 | 37 | deactivate() { 38 | let deactivate = super.deactivate(); 39 | let cancel = new Promise((resolve, _reject) => { 40 | deactivate.then((_result) => { 41 | resolve(); 42 | }) 43 | }); 44 | 45 | return Promise.race([deactivate, this.createTimeoutPromise(2000, cancel)]) 46 | } 47 | 48 | shouldStartForEditor(editor) { 49 | const projectPath = this.getProjectPath(editor.getURI() || ''); 50 | if (!projectPath) return false 51 | 52 | if (atom.config.get('ide-typescript.ignoreFlow') === true) { 53 | const flowConfigPath = path.join(projectPath, '.flowconfig') 54 | if (fs.existsSync(flowConfigPath)) return false 55 | } 56 | 57 | if (!this.validateTypeScriptServerPath(projectPath)) return false 58 | 59 | return super.shouldStartForEditor(editor); 60 | } 61 | 62 | validateTypeScriptServerPath(projectPath) { 63 | const tsPath = this.getServerPath(projectPath); 64 | 65 | if (fs.existsSync(tsPath)) return true 66 | 67 | atom.notifications.addError('ide-typescript could not locate the TypeScript server', { 68 | dismissable: true, 69 | buttons: [ 70 | { text: 'Set TypeScript server path', onDidClick: () => this.openPackageSettings() }, 71 | ], 72 | description: 73 | `No TypeScript server could be found at ${tsPath}` 74 | }) 75 | } 76 | 77 | openPackageSettings() { 78 | atom.workspace.open('atom://config/packages/ide-typescript') 79 | } 80 | 81 | getProjectPath(filePath) { 82 | const projectPath = atom.project.getDirectories().find(d => filePath.startsWith(d.path)) 83 | return projectPath != null ? projectPath.path : '' 84 | } 85 | 86 | getServerPath(projectPath) { 87 | const relativePathSpecifiedByUser = atom.config.get('ide-typescript.typeScriptServer.path') 88 | const relativePathDefault = 'node_modules/typescript/lib/tsserver.js' 89 | const absPathLocal = path.resolve(projectPath, relativePathSpecifiedByUser) 90 | const absPathGlobal = path.resolve(__dirname, '..', relativePathSpecifiedByUser) 91 | const absPathGlobalDefault = path.resolve(__dirname, '..', relativePathDefault) 92 | 93 | if (fs.existsSync(absPathLocal)) { 94 | return absPathLocal 95 | } 96 | if (fs.existsSync(absPathGlobal)) { 97 | return absPathGlobal 98 | } 99 | 100 | return absPathGlobalDefault 101 | } 102 | 103 | createTimeoutPromise(milliseconds, cancelPromise) { 104 | let cancel = false; 105 | cancelPromise.then((_result) => { 106 | cancel = true; 107 | }) 108 | 109 | return new Promise((resolve, reject) => { 110 | let timeout = setTimeout(() => { 111 | clearTimeout(timeout) 112 | 113 | if (cancel !== true) { 114 | this.logger.error(`Server failed to shutdown in ${milliseconds}ms, forcing termination`); 115 | resolve(); 116 | } else { 117 | reject(); 118 | } 119 | }, milliseconds) 120 | }) 121 | } 122 | 123 | provideAutocomplete() { 124 | const autocompleteResultsFirst = atom.config.get('ide-typescript.autocompleteResultsFirst') 125 | const provided = super.provideAutocomplete() 126 | provided.suggestionPriority = autocompleteResultsFirst ? 2 : 1 127 | return provided 128 | } 129 | 130 | onDidConvertAutocomplete(_completionItem, suggestion, _request) { 131 | TypeScriptLanguageClient.setLeftAndRightLabels(suggestion) 132 | 133 | // Theia language server sets snippets to '' leading to ambiguity between using that and text 134 | if (suggestion.snippet === '' && suggestion.text != null && suggestion.text !== '') { 135 | suggestion.snippet = undefined 136 | } 137 | } 138 | 139 | static setLeftAndRightLabels(suggestion) { 140 | if (suggestion.rightLabel == null || suggestion.displayText == null) return 141 | const nameIndex = suggestion.rightLabel.indexOf(suggestion.displayText) 142 | if (nameIndex >= 0) { 143 | const signature = suggestion.rightLabel.substr(nameIndex + suggestion.displayText.length).trim() 144 | let paramsStart = -1 145 | let paramsEnd = -1 146 | let returnStart = -1 147 | let bracesDepth = 0 148 | for (let i = 0; i < signature.length; i++) { 149 | switch (signature[i]) { 150 | case '(': { 151 | if (bracesDepth++ === 0 && paramsStart === -1) { 152 | paramsStart = i; 153 | } 154 | break; 155 | } 156 | case ')': { 157 | if (--bracesDepth === 0 && paramsEnd === -1) { 158 | paramsEnd = i; 159 | } 160 | break; 161 | } 162 | case ':': { 163 | if (returnStart === -1 && bracesDepth === 0) { 164 | returnStart = i; 165 | } 166 | break; 167 | } 168 | } 169 | } 170 | 171 | if (atom.config.get('ide-typescript.returnTypeInAutocomplete') === 'left') { 172 | if (paramsStart > -1) { 173 | suggestion.rightLabel = signature.substring(paramsStart, paramsEnd + 1).trim() 174 | } 175 | if (returnStart > -1) { 176 | suggestion.leftLabel = signature.substring(returnStart + 1).trim() 177 | } 178 | // We have a 'property' icon, we don't need to pollute the signature with '(property) ' 179 | const propertyPrefix = '(property) ' 180 | if (suggestion.rightLabel.startsWith(propertyPrefix)) { 181 | suggestion.rightLabel = suggestion.rightLabel.substring(propertyPrefix.length) 182 | } 183 | } else { 184 | suggestion.rightLabel = signature.substring(paramsStart).trim() 185 | suggestion.leftLabel = '' 186 | } 187 | } 188 | } 189 | 190 | filterChangeWatchedFiles(filePath) { 191 | return this.supportedExtensions.indexOf(path.extname(filePath).toLowerCase()) > -1; 192 | } 193 | } 194 | 195 | module.exports = new TypeScriptLanguageClient() 196 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ide-typescript", 3 | "main": "./lib/main", 4 | "version": "0.9.5", 5 | "description": "TypeScript and JavaScript language support for Atom-IDE.", 6 | "repository": "https://github.com/atom/ide-typescript", 7 | "license": "MIT", 8 | "engines": { 9 | "atom": ">=1.21.0" 10 | }, 11 | "configSchema": { 12 | "diagnosticsEnabled": { 13 | "type": "boolean", 14 | "default": true, 15 | "description": "Use the TypeScript language server to provide diagnostics and warnings." 16 | }, 17 | "typeScriptServer": { 18 | "title": "TypeScript server", 19 | "type": "object", 20 | "properties": { 21 | "path": { 22 | "title": "TypeScript server path", 23 | "type": "string", 24 | "default": "node_modules/typescript/lib/tsserver.js", 25 | "description": "Path to a TypeScript `tsserver.js` the language server will use to process TypeScript. Defaults to the bundled version." 26 | } 27 | } 28 | }, 29 | "javascriptSupport": { 30 | "type": "boolean", 31 | "title": "JavaScript support", 32 | "default": true, 33 | "description": "Use the TypeScript language server to also provide services for JavaScript." 34 | }, 35 | "ignoreFlow": { 36 | "type": "boolean", 37 | "title": "Ignore Flow project folders", 38 | "default": true, 39 | "description": "Ignore project folders that contain a Flow `.flowconfig` file." 40 | }, 41 | "autocompleteResultsFirst": { 42 | "type": "boolean", 43 | "title": "Show autocomplete results first", 44 | "default": true, 45 | "description": "Suggestions will be placed before the rest of autocomplete results (e.g. snippets etc). Requires restart to take effect." 46 | }, 47 | "returnTypeInAutocomplete": { 48 | "type": "string", 49 | "default": "left", 50 | "enum": [ 51 | { 52 | "value": "left", 53 | "description": "On their own to the left (like other languages)" 54 | }, 55 | { 56 | "value": "right", 57 | "description": "Following parameters to the right (like source code)" 58 | } 59 | ], 60 | "description": "Where return types are shown in AutoComplete." 61 | } 62 | }, 63 | "dependencies": { 64 | "atom-languageclient": "0.9.9", 65 | "typescript": "~3.6.4", 66 | "typescript-language-server": "~0.4.0" 67 | }, 68 | "activationHooks": [ 69 | "source.ts:root-scope-used", 70 | "source.tsx:root-scope-used", 71 | "source.js:root-scope-used", 72 | "source.js.jsx:root-scope-used" 73 | ], 74 | "enhancedScopes": [ 75 | "source.ts", 76 | "source.tsx", 77 | "source.js", 78 | "source.js.jsx" 79 | ], 80 | "consumedServices": { 81 | "console": { 82 | "versions": { 83 | "0.1.0": "consumeConsole" 84 | } 85 | }, 86 | "datatip": { 87 | "versions": { 88 | "0.1.0": "consumeDatatip" 89 | } 90 | }, 91 | "linter-indie": { 92 | "versions": { 93 | "2.0.0": "consumeLinterV2" 94 | } 95 | }, 96 | "signature-help": { 97 | "versions": { 98 | "0.1.0": "consumeSignatureHelp" 99 | } 100 | } 101 | }, 102 | "providedServices": { 103 | "autocomplete.provider": { 104 | "versions": { 105 | "2.0.0": "provideAutocomplete" 106 | } 107 | }, 108 | "code-actions": { 109 | "versions": { 110 | "0.1.0": "provideCodeActions" 111 | } 112 | }, 113 | "code-format.range": { 114 | "versions": { 115 | "0.1.0": "provideCodeFormat" 116 | } 117 | }, 118 | "definitions": { 119 | "versions": { 120 | "0.1.0": "provideDefinitions" 121 | } 122 | }, 123 | "find-references": { 124 | "versions": { 125 | "0.1.0": "provideFindReferences" 126 | } 127 | }, 128 | "outline-view": { 129 | "versions": { 130 | "0.1.0": "provideOutlines" 131 | } 132 | } 133 | } 134 | } 135 | --------------------------------------------------------------------------------