├── .gitignore ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── data ├── classes.json └── defines.json ├── images ├── autocomplete.gif ├── icon.png └── tooltips.gif ├── package-lock.json ├── package.json ├── src ├── ApiData.ts ├── Autocomplete.ts ├── Hover.ts ├── extension.ts ├── thenable.d.ts ├── types.d.ts └── utils.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | node_modules 3 | Untitled-1.lua 4 | data/classes.old.json 5 | *.vsix -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | { 3 | "version": "0.1.0", 4 | "configurations": [ 5 | 6 | { 7 | "name": "Launch Extension", 8 | "type": "extensionHost", 9 | "request": "launch", 10 | "runtimeExecutable": "${execPath}", 11 | "args": ["--extensionDevelopmentPath=${workspaceRoot}" ], 12 | "stopOnEntry": false, 13 | "sourceMaps": true, 14 | "outFiles": [ "${workspaceRoot}/out/src/**/*.js" ], 15 | "preLaunchTask": "npm" 16 | }, 17 | { 18 | "name": "Current TS File", 19 | "type": "node", 20 | "request": "launch", 21 | "program": "${workspaceRoot}/node_modules/ts-node/dist/_bin.js", 22 | "args": ["${relativeFile}"], 23 | "cwd": "${workspaceRoot}", 24 | "protocol": "inspector" 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false // set this to true to hide the "out" folder with the compiled JS files 5 | }, 6 | "search.exclude": { 7 | "out": true // set this to false to include "out" folder in search results 8 | } 9 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // Available variables which can be used inside of strings. 2 | // ${workspaceRoot}: the root folder of the team 3 | // ${file}: the current opened file 4 | // ${fileBasename}: the current opened file's basename 5 | // ${fileDirname}: the current opened file's dirname 6 | // ${fileExtname}: the current opened file's extension 7 | // ${cwd}: the current working directory of the spawned process 8 | 9 | // A task runner that calls a custom npm script that compiles the extension. 10 | { 11 | "version": "0.1.0", 12 | 13 | // we want to run npm 14 | "command": "npm", 15 | 16 | // the command is a shell script 17 | "isShellCommand": true, 18 | 19 | // show the output window only if unrecognized errors occur. 20 | "showOutput": "silent", 21 | 22 | // we run the custom script "compile" as defined in package.json 23 | "args": ["run", "compile", "--loglevel", "silent"], 24 | 25 | // The tsc compiler is started in watching mode 26 | "isBackground": true, 27 | 28 | // use the standard tsc in watch mode problem matcher to find compile problems in the output. 29 | "problemMatcher": "$tsc-watch" 30 | } -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MCJack123/vscode-computercraft/340bf693760fee100e2001ca75391b1c055e65be/.vscodeignore -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 1.1.1 - 2022-03-28 4 | - Updated for CC: Tweaked 1.100.0 5 | - Fixed an exception when hovering 6 | 7 | ## 1.1.0 - 2020-11-10 8 | - Updated for CC: Tweaked 1.94.0 9 | 10 | ## 1.0.0 - 2019-10-01 11 | - Added turtle API (Superslammer) 12 | 13 | ## 0.9.0 - 2018-12-07 14 | - Added rest of APIs except turtle 15 | 16 | ## 0.0.1 - 2018-12-04 17 | - Initial release (incomplete) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Michel Jansson 4 | 5 | Original work Copyright (c) 2017 Simon Vizzini 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ComputerCraft API autocomplete extension 2 | This extension provides autocomplete for ComputerCraft/CC: Tweaked 1.100.0. 3 | 4 | ## Features 5 | 6 | Note: these screenshots are of the original base extension for Factorio; they do not represent the ComputerCraft updated fork. 7 | 8 | - Autocomplete of Lua classes and globals 9 | 10 | ![autocomplete](images/autocomplete.gif) 11 | 12 | - Mouse hover tooltips 13 | 14 | ![tooltips](images/tooltips.gif) 15 | 16 | 17 | ## Todo 18 | 19 | #### Features 20 | - Better support for functions that take tables as argument 21 | - Function signature hints (**registerSignatureHelpProvider**) 22 | 23 | #### Technical tasks 24 | - Instead of storing inherited properties in the data file, they should maybe get looked up during runtime 25 | - Unit tests 26 | 27 | ## Acknowledgments 28 | This extension is based on [Simon Vizzini](https://github.com/simonvizzini) extension [vscode-factorio-lua-api-autocomplete](https://github.com/simonvizzini/vscode-factorio-lua-api-autocomplete) 29 | -------------------------------------------------------------------------------- /data/defines.json: -------------------------------------------------------------------------------- 1 | { 2 | } -------------------------------------------------------------------------------- /images/autocomplete.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MCJack123/vscode-computercraft/340bf693760fee100e2001ca75391b1c055e65be/images/autocomplete.gif -------------------------------------------------------------------------------- /images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MCJack123/vscode-computercraft/340bf693760fee100e2001ca75391b1c055e65be/images/icon.png -------------------------------------------------------------------------------- /images/tooltips.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MCJack123/vscode-computercraft/340bf693760fee100e2001ca75391b1c055e65be/images/tooltips.gif -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vscode-computercraft", 3 | "displayName": "ComputerCraft", 4 | "description": "Autocompletion for the ComputerCraft/CC: Tweaked APIs", 5 | "version": "1.1.1", 6 | "publisher": "JackMacWindows", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/MCJack123/vscode-computercraft.git" 10 | }, 11 | "private": true, 12 | "license": "MIT", 13 | "engines": { 14 | "vscode": "^1.20.0" 15 | }, 16 | "categories": [ 17 | "Other", 18 | "Programming Languages" 19 | ], 20 | "activationEvents": [ 21 | "onLanguage:lua" 22 | ], 23 | "main": "./out/src/extension", 24 | "contributes": { 25 | "languages": [ 26 | { 27 | "id": "lua", 28 | "extensions": [ 29 | ".lua" 30 | ], 31 | "aliases": [ 32 | "lua" 33 | ] 34 | } 35 | ] 36 | }, 37 | "icon": "images/icon.png", 38 | "scripts": { 39 | "vscode:prepublish": "npm run compile", 40 | "compile": "tsc -p ./", 41 | "watch": "tsc -watch -p ./", 42 | "postinstall": "node ./node_modules/vscode/bin/install" 43 | }, 44 | "devDependencies": { 45 | "@types/node": "^6.0.40", 46 | "fs": "0.0.1-security", 47 | "typescript": "^2.0.3", 48 | "typings": "^2.1.1", 49 | "vscode": "^1.1.22" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/ApiData.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "fs" 2 | 3 | const brackets = /\[.*\]/g 4 | 5 | const additionalTriggers = { 6 | // trigger: "Key", 7 | } 8 | 9 | export default class L4RCApiData { 10 | 11 | private classes: L4RCTypeMap 12 | private defines: L4RCTypeMap 13 | 14 | constructor(private dataPath: string) { 15 | this.loadData(dataPath) 16 | } 17 | 18 | public findType(words: string[]): L4RCType { 19 | if (words.length === 0) { 20 | return { properties: this.classes } 21 | } 22 | 23 | // Clean up path by removing array/dict access brackets (players[0] => players) 24 | words = words.map(p => p.replace(brackets, "")) 25 | 26 | let type = this.classes[words.shift()] 27 | 28 | if (!type) { 29 | return null 30 | } 31 | 32 | if (!type.properties || words.length === 0) { 33 | return type 34 | } 35 | 36 | let props = type.properties 37 | 38 | for (let i = 0; i < words.length; i++) { 39 | type = props[words[i]] 40 | 41 | // Not found 42 | if (!type) return null 43 | 44 | // First try traverse it's own properties 45 | if (type.properties) { 46 | props = type.properties 47 | continue 48 | } 49 | 50 | // Then the complete type list 51 | let parentType = type.type 52 | 53 | // Special handling for defines 54 | if (/defines/.test(parentType)) { 55 | let [__, defineName] = parentType.split(".") 56 | //let define = this.defines[defineName] 57 | //return _.get(this.defines, [defineName, "properties"]) 58 | if (defineName && this.defines[defineName]) 59 | return this.defines[defineName] 60 | else 61 | return null 62 | } 63 | 64 | type = this.classes[parentType] 65 | 66 | if (type && type.properties) { 67 | props = type.properties 68 | continue 69 | } 70 | } 71 | 72 | return type 73 | } 74 | 75 | private loadData(dataPath: string) { 76 | const classes = this.loadDataFile(dataPath + "/classes.json") 77 | const defines = this.loadDataFile(dataPath + "/defines.json") 78 | 79 | // Add some additional autocomplete triggers (when typing on blank line or pressing ctrl-space) 80 | Object.keys(additionalTriggers).forEach(trigger => { 81 | let luaType = additionalTriggers[trigger] 82 | if (luaType in classes) { 83 | classes[trigger] = classes[luaType] 84 | } 85 | }) 86 | 87 | this.classes = classes 88 | this.defines = defines 89 | // todo: revisit this 90 | this.classes.defines = { 91 | type: "define", 92 | properties: defines 93 | } 94 | } 95 | 96 | private loadDataFile(fileName: string): L4RCTypeMap { 97 | const jsonStr = fs.readFileSync(fileName, "utf8") 98 | const data = JSON.parse(jsonStr) 99 | return data 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/Autocomplete.ts: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | import * as vscode from 'vscode' 4 | import L4RCApiData from "./ApiData" 5 | import { getLastMatch, keys, assign } from "./utils" 6 | 7 | const wordsRegex = /([\w\[\]]+\.[\w\[\]\.]*)/g 8 | 9 | export class L4RCAutocomplete implements vscode.CompletionItemProvider { 10 | constructor(private apiData: L4RCApiData) { } 11 | 12 | public provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Thenable { 13 | return new Promise((resolve, reject) => { 14 | let lineText = document.lineAt(position.line).text 15 | let lineTillCurrentPosition = lineText.substr(0, position.character) 16 | 17 | let match = getLastMatch(wordsRegex, lineTillCurrentPosition) 18 | let line = match ? match[1] : "" 19 | 20 | let words = line.split(".") 21 | words.pop() 22 | 23 | let type = this.apiData.findType(words) 24 | 25 | if (!type || !type.properties) { 26 | return reject() 27 | } 28 | 29 | let suggestions = this.toCompletionItems(type.properties) 30 | return resolve(suggestions) 31 | }) 32 | } 33 | 34 | private toCompletionItems(types: L4RCTypeMap): vscode.CompletionItem[] { 35 | return keys(types).map(key => this.toCompletionItem(types[key], key)) 36 | } 37 | 38 | private toCompletionItem(type: L4RCType, key: string): vscode.CompletionItem { 39 | const { doc, name, mode } = type 40 | 41 | let completionItem = assign(new vscode.CompletionItem(key), { 42 | detail: "(property) " + key + ": " + type.type, 43 | documentation: new vscode.MarkdownString(["**"+name+"**", doc, mode].filter(Boolean).join("\n\n")), 44 | kind: vscode.CompletionItemKind.Property, 45 | filterText: key + " " + name 46 | }) 47 | 48 | if (type.type === "function") { 49 | assign(completionItem, { 50 | detail: "(function) " + name + "("+ Object.keys(type.args).join(", ") + "): " + type.returns, 51 | kind: vscode.CompletionItemKind.Function, 52 | documentation: new vscode.MarkdownString([doc, mode].filter(Boolean).join("\n\n")), 53 | }) 54 | } 55 | else if (type.type === "field") { 56 | assign(completionItem, { 57 | detail: "(field) " + name, 58 | kind: vscode.CompletionItemKind.Field, 59 | }) 60 | } 61 | else if (type.type === "class") { 62 | assign(completionItem, { 63 | detail: "(class) " + name, 64 | kind: vscode.CompletionItemKind.Class, 65 | }) 66 | } 67 | else if (type.type === "define") { 68 | assign(completionItem, { 69 | detail: "(constant) " + type.type, 70 | kind: vscode.CompletionItemKind.Constant 71 | }) 72 | } 73 | 74 | return completionItem 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Hover.ts: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | import * as vscode from 'vscode' 4 | import L4RCApiData from "./ApiData" 5 | import { getLastMatch } from "./utils" 6 | 7 | const { isArray } = Array 8 | const { assign, keys } = Object 9 | 10 | const wordsRegex = /([\w\[\]]+\.*[\w\[\]\.]*)/g 11 | 12 | export class L4RCHover implements vscode.HoverProvider { 13 | constructor(private apiData: L4RCApiData) { } 14 | 15 | provideHover(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): vscode.ProviderResult { 16 | return new Promise((resolve, reject) => { 17 | let lineText = document.lineAt(position.line).text 18 | let wordRange = document.getWordRangeAtPosition(position) 19 | 20 | if (!wordRange) return null 21 | 22 | let lineTillCurrentWord = lineText.substr(0, wordRange.end.character) 23 | let match = getLastMatch(wordsRegex, lineTillCurrentWord) 24 | let wordsStr = match ? match[1] : null 25 | 26 | if (!wordsStr) return null 27 | 28 | let words = wordsStr.split(".") 29 | let word = words.pop() 30 | let type = this.apiData.findType(words) 31 | 32 | if (!type) return null 33 | 34 | let target 35 | 36 | if (type.properties && type.properties[word]) { 37 | target = type.properties[word] 38 | } else if (type[word]) { 39 | target = type[word] 40 | } else if (!target || (!target.type && !target.name)) { 41 | return null 42 | } 43 | 44 | let content = "```javascript\n(property) " + word + ": " + target.type + "\n```" 45 | 46 | if (target.type === "function") { 47 | content = "```javascript\n(function) " + target.name + "(" + Object.keys(target.args).join(", ") + "): " + target.returns + "\n```" 48 | } 49 | else if (target.type === "field") { 50 | content = "```javascript\n(field) " + target.name + "\n```" 51 | } 52 | else if (target.type === "class") { 53 | content = "```javascript\n(class) " + target.name + "\n```" 54 | } 55 | else if (target.type === "define") { 56 | content = "```javascript\n(define) " + word + "\n```" 57 | } 58 | 59 | 60 | if (target.name && target.name !== word) { 61 | content = content + "\n\n" + `**${target.name}**` 62 | } 63 | 64 | if (target.doc) { 65 | content += "\n\n" + target.doc 66 | } 67 | 68 | resolve(new vscode.Hover(content, wordRange)) 69 | }) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | import * as vscode from 'vscode' 4 | import L4RCApiData from "./ApiData" 5 | import { L4RCAutocomplete } from "./Autocomplete" 6 | import { L4RCHover } from "./Hover" 7 | 8 | const LUA_MODE = { language: "lua", scheme: "file" } 9 | 10 | export function activate(context: vscode.ExtensionContext) { 11 | let dataPath = context.asAbsolutePath("./data") 12 | const l4RCApiData = new L4RCApiData(dataPath) 13 | 14 | context.subscriptions.push( 15 | vscode.languages.registerCompletionItemProvider( 16 | LUA_MODE, 17 | new L4RCAutocomplete(l4RCApiData), 18 | '.' 19 | ) 20 | ) 21 | 22 | context.subscriptions.push( 23 | vscode.languages.registerHoverProvider( 24 | LUA_MODE, 25 | new L4RCHover(l4RCApiData) 26 | ) 27 | ) 28 | } 29 | 30 | // this method is called when your extension is deactivated 31 | export function deactivate() { 32 | } -------------------------------------------------------------------------------- /src/thenable.d.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | interface Thenable extends PromiseLike {} -------------------------------------------------------------------------------- /src/types.d.ts: -------------------------------------------------------------------------------- 1 | interface L4RCTypeMap { 2 | [prop: string]: L4RCType 3 | } 4 | 5 | interface L4RCType { 6 | type?: string 7 | name?: string 8 | doc?: string 9 | mode?: string 10 | properties?: L4RCTypeMap 11 | args?: L4RCTypeMap 12 | returns?: string 13 | inherits?: string[] 14 | } 15 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | export const { isArray } = Array 2 | export const { assign, keys } = Object 3 | 4 | export const getMatches = (regex: RegExp, str: string) : string[] => { 5 | let match = null, matches = [] 6 | while ((match = regex.exec(str)) !== null) { 7 | matches.push(match) 8 | } 9 | return matches 10 | } 11 | 12 | export const getLastMatch = (regex: RegExp, str: string) : string => { 13 | return getMatches(regex, str).slice(-1).pop() 14 | } 15 | 16 | export const each = (data: any[] | {}, callback: (value: any, key: string | number) => any) =>{ 17 | isArray(data) ? 18 | data.forEach(callback) : 19 | keys(data).forEach(k => callback(data[k], k)) 20 | } 21 | 22 | export const reduce = (data: any[] | {}, callback: (prev: any, curr: any, key: string | number) => any, initial?: any): any => { 23 | return isArray(data) ? 24 | data.reduce(callback, initial) : 25 | keys(data).reduce((prev, key) => callback(prev, data[key], key), initial) 26 | } 27 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "outDir": "out", 6 | "lib": [ 7 | "es6" 8 | ], 9 | "sourceMap": true, 10 | "rootDir": ".", 11 | "allowJs": false 12 | }, 13 | "exclude": [ 14 | "node_modules", 15 | ".vscode-test" 16 | ] 17 | } --------------------------------------------------------------------------------