diff --git a/.vscode/settings.json b/.vscode/settings.json index d057c896..ccdf8c37 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { "editor.tabSize": 4, "editor.insertSpaces": false, - "editor.detectIndentation": false +"editor.detectIndentation": false, +"godotTools.editorPath.godot4": "/home/oier/Descargas/Godot_v4.4.1-stable_linux.x86_64" } diff --git a/install-extension.sh b/install-extension.sh new file mode 100755 index 00000000..8fa23908 --- /dev/null +++ b/install-extension.sh @@ -0,0 +1,171 @@ +#!/bin/bash + +# ASHES Language Support Extension Installer +# This script installs the ASHES language support extension for VSCode/VSCodium + +set -e # Exit on any error + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Function to print colored output +print_info() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +print_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +print_warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +print_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# Function to detect VSCode/VSCodium extensions directory +detect_extensions_dir() { + local extensions_dir="" + + # Check for VSCodium first (more common on Linux) + if command -v codium &> /dev/null; then + extensions_dir="$HOME/.vscode-oss/extensions" + elif command -v code &> /dev/null; then + extensions_dir="$HOME/.vscode/extensions" + else + print_warning "Neither VSCode nor VSCodium found in PATH" + extensions_dir="$HOME/.vscode/extensions" + fi + + echo "$extensions_dir" +} + +# Function to check if directory exists and is writable +check_directory() { + local dir="$1" + + if [ ! -d "$dir" ]; then + print_info "Creating directory: $dir" + mkdir -p "$dir" || { + print_error "Failed to create directory: $dir" + return 1 + } + fi + + if [ ! -w "$dir" ]; then + print_error "Directory is not writable: $dir" + return 1 + fi + + return 0 +} + +# Main installation function +main() { + print_info "ASHES Language Support Extension Installer" + print_info "==========================================" + echo + + # Get the script directory (where the vscode-extension folder is located) + SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + EXTENSION_SOURCE="$SCRIPT_DIR/vscode-extension-ashes" + + # Check if source extension directory exists + if [ ! -d "$EXTENSION_SOURCE" ]; then + print_error "Extension source directory not found: $EXTENSION_SOURCE" + print_error "Please run this script from the project root directory." + exit 1 + fi + + # Detect default extensions directory + DEFAULT_EXTENSIONS_DIR=$(detect_extensions_dir) + + print_info "Default VSCode/VSCodium extensions directory:" + print_info " $DEFAULT_EXTENSIONS_DIR" + echo + + # Ask for confirmation or custom path + read -p "Use default path? (y/n) [y]: " use_default + use_default=${use_default:-y} + + if [[ $use_default =~ ^[Yy]$ ]]; then + EXTENSIONS_DIR="$DEFAULT_EXTENSIONS_DIR" + else + echo + read -p "Enter custom extensions directory path: " custom_path + EXTENSIONS_DIR="$custom_path" + fi + + # Validate the extensions directory + if ! check_directory "$EXTENSIONS_DIR"; then + exit 1 + fi + + # Set the destination path for the symlink + EXTENSION_DEST="$EXTENSIONS_DIR/vscode-extension-ashes" + + echo + print_info "Installation Summary:" + print_info "====================" + print_info "Source: $EXTENSION_SOURCE" + print_info "Destination: $EXTENSION_DEST" + print_info "Link name: vscode-extension-ashes" + echo + + # Final confirmation + read -p "Proceed with installation? (y/n) [y]: " confirm + confirm=${confirm:-y} + + if [[ ! $confirm =~ ^[Yy]$ ]]; then + print_info "Installation cancelled." + exit 0 + fi + + echo + print_info "Installing extension..." + + # Check if destination already exists + if [ -e "$EXTENSION_DEST" ]; then + print_warning "Destination already exists: $EXTENSION_DEST" + read -p "Remove existing installation? (y/n) [y]: " remove_existing + remove_existing=${remove_existing:-y} + + if [[ $remove_existing =~ ^[Yy]$ ]]; then + print_info "Removing existing installation..." + rm -rf "$EXTENSION_DEST" || { + print_error "Failed to remove existing installation" + exit 1 + } + else + print_info "Installation cancelled." + exit 0 + fi + fi + + # Create the symlink + print_info "Creating symlink..." + if ln -s "$EXTENSION_SOURCE" "$EXTENSION_DEST"; then + print_success "Extension installed successfully!" + echo + print_info "Next steps:" + print_info "1. Restart VSCode/VSCodium" + print_info "2. Open any .esc file to test the extension" + print_info "3. The language should be automatically detected as 'ASHES'" + echo + print_info "To uninstall, simply remove the symlink:" + print_info " rm '$EXTENSION_DEST'" + else + print_error "Failed to create symlink" + print_error "Make sure you have write permissions to: $EXTENSIONS_DIR" + exit 1 + fi +} + +# Run main function +main "$@" diff --git a/vscode-extension-ashes/.gitignore b/vscode-extension-ashes/.gitignore new file mode 100644 index 00000000..154499d3 --- /dev/null +++ b/vscode-extension-ashes/.gitignore @@ -0,0 +1,4 @@ +out +node_modules +*.vsix +.DS_Store diff --git a/vscode-extension-ashes/.vscodeignore b/vscode-extension-ashes/.vscodeignore new file mode 100644 index 00000000..38999676 --- /dev/null +++ b/vscode-extension-ashes/.vscodeignore @@ -0,0 +1,10 @@ +.vscode/** +.vscode-test/** +src/** +.gitignore +.yarnrc +vsc-extension-quickstart.md +**/tsconfig.json +**/.eslintrc.json +**/*.map +**/*.ts diff --git a/vscode-extension-ashes/INSTALL.md b/vscode-extension-ashes/INSTALL.md new file mode 100644 index 00000000..34389c14 --- /dev/null +++ b/vscode-extension-ashes/INSTALL.md @@ -0,0 +1,84 @@ +# Installation Guide for ASHES Language Support Extension + +## Quick Installation + +1. **Copy the extension folder** to your VS Code extensions directory: + - **Linux**: `~/.vscode/extensions/` + - **macOS**: `~/.vscode/extensions/` + - **Windows**: `%USERPROFILE%\.vscode\extensions\` + +2. **Rename the folder** to `ashes-language-support-0.1.0` (or similar) + +3. **Reload VS Code** or restart the application + +4. **Test the extension** by opening any `.esc` file + +## Alternative Installation (Development Mode) + +1. **Open VS Code** in the extension directory: + ```bash + cd vscode-extension + code . + ``` + +2. **Press F5** to run the extension in a new Extension Development Host window + +3. **Open a .esc file** in the new window to test the extension + +## Building from Source + +1. **Install dependencies**: + ```bash + npm install + ``` + +2. **Compile TypeScript**: + ```bash + npm run compile + ``` + +3. **Package the extension** (optional): + ```bash + npx vsce package + ``` + +## Testing the Extension + +1. **Open the sample file**: `sample.esc` in the extension directory +2. **Check syntax highlighting**: Events, commands, and variables should be colorized +3. **Test auto-completion**: Type `say(` and press `Ctrl+Space` +4. **Test snippets**: Type `event` and press `Tab` +5. **Test hover**: Hover over commands like `say` or `set_global` +6. **Test command reference**: Press `Ctrl+Shift+P` and type "ASHES: Show Command Reference" + +## Features to Test + +- ✅ Syntax highlighting for events (`:event_name`) +- ✅ Syntax highlighting for commands (`say`, `set_global`, etc.) +- ✅ Syntax highlighting for variables (`var`, `global`) +- ✅ Syntax highlighting for dialog blocks (`?!`) +- ✅ Auto-completion for commands +- ✅ Auto-completion for built-in variables +- ✅ Code snippets for common patterns +- ✅ Hover information for commands +- ✅ Smart indentation +- ✅ Code folding for events + +## Troubleshooting + +### Extension not loading +- Check that the folder is in the correct extensions directory +- Restart VS Code completely +- Check the Developer Console for errors (`Help > Toggle Developer Tools`) + +### Syntax highlighting not working +- Make sure the file has a `.esc` extension +- Check that the language is set to "ASHES" in the bottom-right corner of VS Code + +### Auto-completion not working +- Press `Ctrl+Space` to manually trigger completion +- Check that the extension is activated (should show in the Extensions panel) + +## Uninstalling + +Simply delete the extension folder from your VS Code extensions directory and restart VS Code. diff --git a/vscode-extension-ashes/README.md b/vscode-extension-ashes/README.md new file mode 100644 index 00000000..33fe6a7a --- /dev/null +++ b/vscode-extension-ashes/README.md @@ -0,0 +1,93 @@ +# ASHES Language Support + +A Visual Studio Code extension that provides syntax highlighting and IntelliSense for the ASHES (Adventure Scripting Helping Escoria) language used in Escoria adventure game framework. + +## Features + +- **Syntax Highlighting**: Full syntax highlighting for ASHES language files (.esc) +- **Auto-completion**: IntelliSense for ASHES commands, built-in variables, and keywords +- **Hover Information**: Detailed information about commands and variables on hover +- **Code Snippets**: Pre-built snippets for common ASHES patterns +- **Command Reference**: Built-in command reference panel +- **Smart Indentation**: Proper indentation rules for ASHES code structure + +## ASHES Language Features Supported + +### Events +- Event definitions with `:event_name` +- Event flags with `| FLAG_NAME` + +### Commands +- All standard Escoria commands (say, set_global, change_scene, etc.) +- Custom commands +- Command parameter hints + +### Variables +- Local variables with `var` +- Global variables with `global` +- Built-in variables (CURRENT_PLAYER, ESC_LAST_SCENE, etc.) +- Global IDs with `$` prefix + +### Control Flow +- If/elif/else statements +- While loops +- Break and done keywords + +### Dialog System +- Dialog blocks with `?!` +- Dialog choices with `-` +- Conditional dialog choices with `[condition]` + +### Comments +- Line comments with `#` + +## Installation + +1. Copy this extension folder to your VS Code extensions directory +2. Reload VS Code +3. Open any `.esc` file to see syntax highlighting + +## Usage + +### Auto-completion +- Type any ASHES command and press `Ctrl+Space` for suggestions +- Use `$` prefix for global ID suggestions +- Built-in variables are automatically suggested + +### Snippets +- Type snippet prefixes and press `Tab` to expand: + - `event` - Create new event + - `say` - Say command + - `dialog` - Dialog block + - `if` - If statement + - And many more... + +### Command Reference +- Press `Ctrl+Shift+P` and type "ASHES: Show Command Reference" +- View all available commands with descriptions and parameters + +## Language Features + +### Syntax Highlighting +- Events are highlighted in blue +- Commands are highlighted in green +- Variables are highlighted in orange +- Strings are highlighted in yellow +- Comments are highlighted in gray +- Dialog blocks have special highlighting + +### Smart Indentation +- Automatic indentation for events, control flow, and dialog blocks +- Proper outdenting for `break`, `done`, `else`, etc. + +### Folding +- Events can be folded for better code organization +- Dialog blocks can be folded + +## Contributing + +This extension is designed specifically for the Escoria framework and ASHES language. If you find issues or want to add features, please contribute to the project. + +## License + +This extension is part of the Gymkhana project and follows the same license terms. diff --git a/vscode-extension-ashes/language-configuration.json b/vscode-extension-ashes/language-configuration.json new file mode 100644 index 00000000..09cf6aca --- /dev/null +++ b/vscode-extension-ashes/language-configuration.json @@ -0,0 +1,35 @@ +{ + "comments": { + "lineComment": "#" + }, + "brackets": [ + ["{", "}"], + ["[", "]"], + ["(", ")"] + ], + "autoClosingPairs": [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ["\"", "\""], + ["'", "'"] + ], + "surroundingPairs": [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ["\"", "\""], + ["'", "'"] + ], + "folding": { + "markers": { + "start": "^\\s*:", + "end": "^\\s*$" + } + }, + "wordPattern": "(-?\\d*\\.\\d\\w*)|([^\\`\\~\\!\\@\\#\\%\\^\\&\\*\\(\\)\\-\\=\\+\\[\\{\\]\\}\\\\\\|\\;\\:\\'\\\"\\,\\.\\<\\>\\/\\?\\s]+)", + "indentationRules": { + "increaseIndentPattern": "^\\s*(?!\\s*#).*:\\s*$|^\\s*if\\s+.*:|^\\s*elif\\s+.*:|^\\s*else\\s*:|^\\s*while\\s+.*:|^\\s*-\\s+.*:|^\\s*\\?!\\s*$", + "decreaseIndentPattern": "^\\s*(elif|else|done|break|stop)\\b" + } +} diff --git a/vscode-extension-ashes/package-lock.json b/vscode-extension-ashes/package-lock.json new file mode 100644 index 00000000..c19417d1 --- /dev/null +++ b/vscode-extension-ashes/package-lock.json @@ -0,0 +1,49 @@ +{ + "name": "ashes-language-support", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "ashes-language-support", + "version": "0.1.0", + "license": "ISC", + "devDependencies": { + "@types/node": "16.x", + "@types/vscode": "^1.74.0", + "typescript": "^4.9.4" + }, + "engines": { + "vscode": "^1.74.0" + } + }, + "node_modules/@types/node": { + "version": "16.18.126", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.126.tgz", + "integrity": "sha512-OTcgaiwfGFBKacvfwuHzzn1KLxH/er8mluiy8/uM3sGXHaRe73RrSIj01jow9t4kJEW633Ov+cOexXeiApTyAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/vscode": { + "version": "1.103.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.103.0.tgz", + "integrity": "sha512-o4hanZAQdNfsKecexq9L3eHICd0AAvdbLk6hA60UzGXbGH/q8b/9xv2RgR7vV3ZcHuyKVq7b37IGd/+gM4Tu+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + } + } +} diff --git a/vscode-extension-ashes/package.json b/vscode-extension-ashes/package.json new file mode 100644 index 00000000..0699ecd9 --- /dev/null +++ b/vscode-extension-ashes/package.json @@ -0,0 +1,79 @@ +{ + "name": "ashes-language-support", + "displayName": "ASHES Language Support", + "description": "Syntax highlighting and IntelliSense for ASHES (Adventure Scripting Helping Escoria) language", + "version": "0.1.0", + "publisher": "gymkhana-dev", + "engines": { + "vscode": "^1.74.0" + }, + "categories": [ + "Programming Languages", + "Snippets" + ], + "keywords": [ + "ashes", + "escoria", + "adventure", + "game", + "scripting" + ], + "activationEvents": [ + "onLanguage:ashes" + ], + "main": "./out/extension.js", + "contributes": { + "languages": [ + { + "id": "ashes", + "aliases": [ + "ASHES", + "ashes" + ], + "extensions": [ + ".esc" + ], + "configuration": "./language-configuration.json" + } + ], + "grammars": [ + { + "language": "ashes", + "scopeName": "source.ashes", + "path": "./syntaxes/ashes.tmLanguage.json" + } + ], + "snippets": [ + { + "language": "ashes", + "path": "./snippets/ashes.json" + } + ], + "commands": [ + { + "command": "ashes.showCommandReference", + "title": "Show ASHES Command Reference", + "category": "ASHES" + } + ], + "menus": { + "commandPalette": [ + { + "command": "ashes.showCommandReference" + } + ] + } + }, + "scripts": { + "vscode:prepublish": "npm run compile", + "compile": "tsc -p ./", + "watch": "tsc -watch -p ./" + }, + "devDependencies": { + "@types/node": "16.x", + "@types/vscode": "^1.74.0", + "typescript": "^4.9.4" + }, + "author": "", + "license": "ISC" +} diff --git a/vscode-extension-ashes/sample.esc b/vscode-extension-ashes/sample.esc new file mode 100644 index 00000000..8c79b670 --- /dev/null +++ b/vscode-extension-ashes/sample.esc @@ -0,0 +1,102 @@ +# Sample ASHES script for testing the VS Code extension +# This file demonstrates various ASHES language features + +:setup + # Global variable declarations + global game_started = false + global player_name = "Player" + global score = 0 + + # Local variables + var tutorial_completed = false + var current_room = "intro" + + # Set initial state + set_global("game_started", true) + set_active($player, true) + teleport($player, $start_position) + +:ready + # Check if this is the first time + if !game_started: + say($player, "Welcome to the game!", "welcome_message") + game_started = true + else: + say($player, "Welcome back!", "welcome_back") + +:action1 + # Simple interaction + say($player, "You examine the object carefully.", "examine_object") + + # Check inventory + if $magic_key in inventory: + say($player, "You have the magic key!", "has_key") + else: + say($player, "You need a key to proceed.", "needs_key") + +:action2 + # Dialog system example + say($player, "You approach the mysterious character.", "approach_character") + + ?! + - "Hello, who are you?" [!name_known] + say_last_dialog_option() + say($npc, "I am the guardian of this place.", "guardian_intro") + global name_known = true + - "Can you help me?" [name_known] + say_last_dialog_option() + say($npc, "I can guide you, but you must prove yourself.", "guardian_help") + ?! + - "How can I prove myself?" + say_last_dialog_option() + say($npc, "Find the three ancient artifacts.", "guardian_task") + - "I'm not interested." + say_last_dialog_option() + say($npc, "Very well, good luck on your own.", "guardian_dismiss") + done + - "Goodbye" + say_last_dialog_option() + say($npc, "Farewell, traveler.", "guardian_farewell") + done + +:action3 + # Complex logic example + var artifacts_found = 0 + + # Check for artifacts + if $artifact1_collected: + artifacts_found += 1 + if $artifact2_collected: + artifacts_found += 1 + if $artifact3_collected: + artifacts_found += 1 + + # Conditional responses + if artifacts_found == 3: + say($player, "I have found all three artifacts!", "all_artifacts") + change_scene("res://rooms/victory_room.tscn") + elif artifacts_found > 0: + say($player, "I have found " + str(artifacts_found) + " artifacts so far.", "some_artifacts") + else: + say($player, "I haven't found any artifacts yet.", "no_artifacts") + +:use | TK + # Use with item + if $magic_key in inventory: + say($player, "You use the magic key.", "use_key") + set_active($locked_door, false) + play_snd("res://sounds/door_open.ogg", _sfx) + else: + say($player, "You can't use this without the right item.", "cant_use") + +:look + # Look action + say($player, "You look around carefully.", "look_around") + + # Conditional descriptions + if $secret_door is active: + say($player, "You notice a hidden passage.", "hidden_passage") + else: + say($player, "Nothing unusual catches your eye.", "nothing_unusual") + +# End of sample script diff --git a/vscode-extension-ashes/snippets/ashes.json b/vscode-extension-ashes/snippets/ashes.json new file mode 100644 index 00000000..1cdb2767 --- /dev/null +++ b/vscode-extension-ashes/snippets/ashes.json @@ -0,0 +1,176 @@ +{ + "Event": { + "prefix": "event", + "body": [ + ":${1:event_name}", + "\t${2:// Event code here}" + ], + "description": "Create a new ASHES event" + }, + "Event with target": { + "prefix": "eventtarget", + "body": [ + ":${1:event_name} \"${2:target_id}\"", + "\t${3:// Event code here}" + ], + "description": "Create a new ASHES event with target" + }, + "Event with flags": { + "prefix": "eventflags", + "body": [ + ":${1:event_name} | ${2:NO_UI} | ${3:NO_TT}", + "\t${4:// Event code here}" + ], + "description": "Create a new ASHES event with flags" + }, + "Event with flags and target": { + "prefix": "eventflagstarget", + "body": [ + ":${1:event_name} | ${2:NO_UI} | ${3:NO_TT} \"${4:target_id}\"", + "\t${5:// Event code here}" + ], + "description": "Create a new ASHES event with flags and target" + }, + "Say command": { + "prefix": "say", + "body": [ + "say(${1:player}, \"${2:text}\"${3:, \"${4:translation_key}\"})" + ], + "description": "Say command for dialog" + }, + "Set global variable": { + "prefix": "setglobal", + "body": [ + "set_global(\"${1:variable_name}\", ${2:value})" + ], + "description": "Set a global variable" + }, + "Global variable declaration": { + "prefix": "global", + "body": [ + "global ${1:variable_name}${2: = ${3:value}}" + ], + "description": "Declare a global variable" + }, + "Local variable declaration": { + "prefix": "var", + "body": [ + "var ${1:variable_name}${2: = ${3:value}}" + ], + "description": "Declare a local variable" + }, + "If statement": { + "prefix": "if", + "body": [ + "if ${1:condition}:", + "\t${2:// Code here}" + ], + "description": "If statement" + }, + "If-else statement": { + "prefix": "ifelse", + "body": [ + "if ${1:condition}:", + "\t${2:// Code here}", + "else:", + "\t${3:// Code here}" + ], + "description": "If-else statement" + }, + "While loop": { + "prefix": "while", + "body": [ + "while ${1:condition}:", + "\t${2:// Code here}" + ], + "description": "While loop" + }, + "Dialog block": { + "prefix": "dialog", + "body": [ + "?!", + "\t- \"${1:choice_text}\"", + "\t\t${2:// Code here}" + ], + "description": "Dialog block with choice" + }, + "Dialog choice with condition": { + "prefix": "dialogif", + "body": [ + "?!", + "\t- \"${1:choice_text}\" [${2:condition}]", + "\t\t${3:// Code here}" + ], + "description": "Dialog choice with condition" + }, + "Change scene": { + "prefix": "changescene", + "body": [ + "change_scene(\"${1:scene_path}\"${2:, ${3:true}})" + ], + "description": "Change to a different scene" + }, + "Set active": { + "prefix": "setactive", + "body": [ + "set_active($${1:object_id}, ${2:true})" + ], + "description": "Set object active/inactive" + }, + "Teleport": { + "prefix": "teleport", + "body": [ + "teleport($${1:object_id}, $${2:target_id})" + ], + "description": "Teleport object to target" + }, + "Walk to": { + "prefix": "walk", + "body": [ + "walk($${1:object_id}, $${2:target_id})" + ], + "description": "Walk object to target" + }, + "Play sound": { + "prefix": "playsnd", + "body": [ + "play_snd(\"${1:sound_path}\"${2:, ${3:_music}})" + ], + "description": "Play a sound file" + }, + "Play video": { + "prefix": "playvideo", + "body": [ + "play_video(\"${1:video_path}\")" + ], + "description": "Play a video file" + }, + "Inventory add": { + "prefix": "inventoryadd", + "body": [ + "inventory_add($${1:item_id})" + ], + "description": "Add item to inventory" + }, + "Inventory remove": { + "prefix": "inventoryremove", + "body": [ + "inventory_remove($${1:item_id})" + ], + "description": "Remove item from inventory" + }, + "Comment": { + "prefix": "comment", + "body": [ + "# ${1:comment}" + ], + "description": "Add a comment" + }, + "Print": { + "prefix": "print", + "body": [ + "print(\"${1:message}\")" + ], + "description": "Print debug message" + } +} diff --git a/vscode-extension-ashes/src/extension.ts b/vscode-extension-ashes/src/extension.ts new file mode 100644 index 00000000..3e15de7b --- /dev/null +++ b/vscode-extension-ashes/src/extension.ts @@ -0,0 +1,495 @@ +import * as vscode from 'vscode'; + +// ASHES commands with their descriptions and parameters +const ASHES_COMMANDS = [ + { + name: 'accept_input', + description: 'Accept specific input types', + parameters: ['input_type'] + }, + { + name: 'anim', + description: 'Play animation on object', + parameters: ['object_id', 'animation_name'] + }, + { + name: 'anim_block', + description: 'Play animation and wait for completion', + parameters: ['object_id', 'animation_name'] + }, + { + name: 'block_say', + description: 'Start a block of say commands', + parameters: [] + }, + { + name: 'camera_push', + description: 'Push camera to new position', + parameters: ['x', 'y'] + }, + { + name: 'camera_push_block', + description: 'Push camera and wait for completion', + parameters: ['x', 'y'] + }, + { + name: 'camera_set_limits', + description: 'Set camera movement limits', + parameters: ['left', 'top', 'right', 'bottom'] + }, + { + name: 'camera_set_pos', + description: 'Set camera position', + parameters: ['x', 'y'] + }, + { + name: 'camera_set_pos_block', + description: 'Set camera position and wait', + parameters: ['x', 'y'] + }, + { + name: 'camera_set_target', + description: 'Set camera target', + parameters: ['object_id'] + }, + { + name: 'camera_set_target_block', + description: 'Set camera target and wait', + parameters: ['object_id'] + }, + { + name: 'camera_set_zoom', + description: 'Set camera zoom level', + parameters: ['zoom_level'] + }, + { + name: 'camera_set_zoom_block', + description: 'Set camera zoom and wait', + parameters: ['zoom_level'] + }, + { + name: 'camera_set_zoom_height', + description: 'Set camera zoom height', + parameters: ['height'] + }, + { + name: 'camera_set_zoom_height_block', + description: 'Set camera zoom height and wait', + parameters: ['height'] + }, + { + name: 'camera_shift', + description: 'Shift camera position', + parameters: ['x', 'y'] + }, + { + name: 'camera_shift_block', + description: 'Shift camera and wait', + parameters: ['x', 'y'] + }, + { + name: 'change_scene', + description: 'Change to a different scene', + parameters: ['scene_path', 'enable_transition', 'run_events'] + }, + { + name: 'custom', + description: 'Execute custom command', + parameters: ['command_name', '...args'] + }, + { + name: 'dec_global', + description: 'Decrement global variable', + parameters: ['variable_name'] + }, + { + name: 'enable_terrain', + description: 'Enable/disable terrain', + parameters: ['terrain_name', 'enabled'] + }, + { + name: 'end_block_say', + description: 'End a block of say commands', + parameters: [] + }, + { + name: 'hide_menu', + description: 'Hide menu', + parameters: ['menu_name'] + }, + { + name: 'inc_global', + description: 'Increment global variable', + parameters: ['variable_name'] + }, + { + name: 'inventory_add', + description: 'Add item to inventory', + parameters: ['item_id'] + }, + { + name: 'inventory_remove', + description: 'Remove item from inventory', + parameters: ['item_id'] + }, + { + name: 'item_count_add', + description: 'Add to item count', + parameters: ['item_id', 'count'] + }, + { + name: 'play_lib_snd', + description: 'Play library sound', + parameters: ['filename', 'namespace'] + }, + { + name: 'play_snd', + description: 'Play sound file', + parameters: ['sound_path', 'type'] + }, + { + name: 'play_video', + description: 'Play video file', + parameters: ['video_path'] + }, + { + name: 'print', + description: 'Print debug message', + parameters: ['message'] + }, + { + name: 'print_internal', + description: 'Print internal message', + parameters: ['message'] + }, + { + name: 'queue_event', + description: 'Queue event for later execution', + parameters: ['object_id', 'event_name'] + }, + { + name: 'queue_resource', + description: 'Queue resource for loading', + parameters: ['resource_path'] + }, + { + name: 'rand_global', + description: 'Set random value to global', + parameters: ['variable_name', 'min', 'max'] + }, + { + name: 'repeat', + description: 'Repeat command', + parameters: ['count', 'command'] + }, + { + name: 'save_game', + description: 'Save game state', + parameters: ['save_name'] + }, + { + name: 'say', + description: 'Display dialog text', + parameters: ['speaker', 'text', 'translation_key', 'type'] + }, + { + name: 'say_last_dialog_option', + description: 'Say the last dialog option', + parameters: [] + }, + { + name: 'say_random', + description: 'Say random text from list', + parameters: ['speaker', 'list_id', 'length'] + }, + { + name: 'say_sequence', + description: 'Say text sequence', + parameters: ['speaker', 'list_id', 'length', 'loop'] + }, + { + name: 'sched_event', + description: 'Schedule event for later', + parameters: ['delay', 'object_id', 'event_name'] + }, + { + name: 'set_active', + description: 'Set object active/inactive', + parameters: ['object_id', 'active'] + }, + { + name: 'set_active_if_exists', + description: 'Set object active if it exists', + parameters: ['object_id', 'active'] + }, + { + name: 'set_angle', + description: 'Set object angle', + parameters: ['object_id', 'angle'] + }, + { + name: 'set_animations', + description: 'Set object animations', + parameters: ['object_id', 'animations'] + }, + { + name: 'set_direction', + description: 'Set object direction', + parameters: ['object_id', 'direction'] + }, + { + name: 'set_global', + description: 'Set global variable', + parameters: ['variable_name', 'value', 'force'] + }, + { + name: 'set_globals', + description: 'Set multiple global variables', + parameters: ['variables_dict'] + }, + { + name: 'set_gui_visible', + description: 'Set GUI visibility', + parameters: ['visible'] + }, + { + name: 'set_interactive', + description: 'Set object interactive state', + parameters: ['object_id', 'interactive'] + }, + { + name: 'set_item_custom_data', + description: 'Set item custom data', + parameters: ['item_id', 'key', 'value'] + }, + { + name: 'set_speed', + description: 'Set object speed', + parameters: ['object_id', 'speed'] + }, + { + name: 'set_state', + description: 'Set object state', + parameters: ['object_id', 'state'] + }, + { + name: 'set_tooltip', + description: 'Set object tooltip', + parameters: ['object_id', 'action', 'text'] + }, + { + name: 'show_menu', + description: 'Show menu', + parameters: ['menu_name'] + }, + { + name: 'slide', + description: 'Slide object to position', + parameters: ['object_id', 'x', 'y', 'duration'] + }, + { + name: 'slide_block', + description: 'Slide object and wait', + parameters: ['object_id', 'x', 'y', 'duration'] + }, + { + name: 'spawn', + description: 'Spawn object', + parameters: ['object_id', 'x', 'y'] + }, + { + name: 'stop', + description: 'Stop current event', + parameters: [] + }, + { + name: 'stop_snd', + description: 'Stop sound', + parameters: ['sound_type'] + }, + { + name: 'teleport', + description: 'Teleport object to target', + parameters: ['object_id', 'target_id'] + }, + { + name: 'teleport_pos', + description: 'Teleport object to position', + parameters: ['object_id', 'x', 'y'] + }, + { + name: 'transition', + description: 'Play transition effect', + parameters: ['transition_type', 'duration'] + }, + { + name: 'turn_to', + description: 'Turn object to face target', + parameters: ['object_id', 'target_id'] + }, + { + name: 'wait', + description: 'Wait for specified time', + parameters: ['duration'] + }, + { + name: 'walk', + description: 'Walk object to target', + parameters: ['object_id', 'target_id'] + }, + { + name: 'walk_block', + description: 'Walk object and wait', + parameters: ['object_id', 'target_id'] + }, + { + name: 'walk_to_pos', + description: 'Walk object to position', + parameters: ['object_id', 'x', 'y'] + }, + { + name: 'walk_to_pos_block', + description: 'Walk object to position and wait', + parameters: ['object_id', 'x', 'y'] + } +]; + +// Built-in variables +const BUILTIN_VARIABLES = [ + 'CURRENT_PLAYER', + 'ESC_LAST_SCENE', + 'ESC_CURRENT_SCENE', + 'FORCE_LAST_SCENE_NULL', + 'ANIMATION_RESOURCES' +]; + +// Keywords +const KEYWORDS = [ + 'var', 'global', 'if', 'elif', 'else', 'while', 'break', 'done', 'stop', 'pass', + 'true', 'false', 'nil', 'and', 'or', 'not', 'in', 'is', 'active' +]; + +export function activate(context: vscode.ExtensionContext) { + console.log('ASHES Language Support extension is now active!'); + + // Register completion provider + const completionProvider = vscode.languages.registerCompletionItemProvider( + 'ashes', + { + provideCompletionItems(document: vscode.TextDocument, position: vscode.Position) { + const completions: vscode.CompletionItem[] = []; + + // Add command completions + ASHES_COMMANDS.forEach(command => { + const completion = new vscode.CompletionItem(command.name, vscode.CompletionItemKind.Function); + completion.detail = command.description; + completion.documentation = new vscode.MarkdownString( + `**${command.name}**\n\n${command.description}\n\n**Parameters:** ${command.parameters.join(', ')}` + ); + completion.insertText = new vscode.SnippetString(`${command.name}($1)`); + completions.push(completion); + }); + + // Add built-in variable completions + BUILTIN_VARIABLES.forEach(variable => { + const completion = new vscode.CompletionItem(variable, vscode.CompletionItemKind.Variable); + completion.detail = 'Built-in variable'; + completion.documentation = new vscode.MarkdownString(`Built-in ASHES variable: **${variable}**`); + completions.push(completion); + }); + + // Add keyword completions + KEYWORDS.forEach(keyword => { + const completion = new vscode.CompletionItem(keyword, vscode.CompletionItemKind.Keyword); + completion.detail = 'ASHES keyword'; + completions.push(completion); + }); + + return completions; + } + }, + ' ', '(', '$' // Trigger characters + ); + + // Register hover provider for commands + const hoverProvider = vscode.languages.registerHoverProvider( + 'ashes', + { + provideHover(document: vscode.TextDocument, position: vscode.Position) { + const word = document.getText(document.getWordRangeAtPosition(position)); + const command = ASHES_COMMANDS.find(cmd => cmd.name === word); + + if (command) { + const hover = new vscode.Hover( + new vscode.MarkdownString( + `**${command.name}**\n\n${command.description}\n\n**Parameters:** ${command.parameters.join(', ')}` + ) + ); + return hover; + } + + const builtinVar = BUILTIN_VARIABLES.find(variable => variable === word); + if (builtinVar) { + const hover = new vscode.Hover( + new vscode.MarkdownString(`Built-in ASHES variable: **${builtinVar}**`) + ); + return hover; + } + + return null; + } + } + ); + + // Register command for showing command reference + const showCommandReference = vscode.commands.registerCommand('ashes.showCommandReference', () => { + const panel = vscode.window.createWebviewPanel( + 'ashesCommandReference', + 'ASHES Command Reference', + vscode.ViewColumn.One, + {} + ); + + const commandsHtml = ASHES_COMMANDS.map(command => + ` + ${command.name} + ${command.description} + ${command.parameters.join(', ')} + ` + ).join(''); + + panel.webview.html = ` + + + + + + +

ASHES Command Reference

+ + + + + + + + + + ${commandsHtml} + +
CommandDescriptionParameters
+ + + `; + }); + + context.subscriptions.push(completionProvider, hoverProvider, showCommandReference); +} + +export function deactivate() {} diff --git a/vscode-extension-ashes/syntaxes/ashes.tmLanguage.json b/vscode-extension-ashes/syntaxes/ashes.tmLanguage.json new file mode 100644 index 00000000..174e2498 --- /dev/null +++ b/vscode-extension-ashes/syntaxes/ashes.tmLanguage.json @@ -0,0 +1,390 @@ +{ + "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", + "name": "ASHES", + "scopeName": "source.ashes", + "patterns": [ + { + "include": "#comments" + }, + { + "include": "#events" + }, + { + "include": "#dialog-blocks" + }, + { + "include": "#dialog-choices" + }, + { + "include": "#commands" + }, + { + "include": "#variables" + }, + { + "include": "#control-flow" + }, + { + "include": "#strings" + }, + { + "include": "#numbers" + }, + { + "include": "#operators" + }, + { + "include": "#keywords" + }, + { + "include": "#builtin-variables" + }, + { + "include": "#global-ids" + } + ], + "repository": { + "comments": { + "patterns": [ + { + "name": "comment.line.number-sign.ashes", + "begin": "#", + "end": "$", + "patterns": [ + { + "name": "comment.line.number-sign.ashes", + "match": "." + } + ] + } + ] + }, + "events": { + "patterns": [ + { + "name": "entity.name.function.event.ashes", + "match": "^\\s*(:)([a-zA-Z_][a-zA-Z0-9_]*)(\\s*\\|\\s*[A-Z_]+)*", + "captures": { + "1": { + "name": "punctuation.definition.event.ashes" + }, + "2": { + "name": "entity.name.function.event.ashes" + }, + "3": { + "name": "entity.name.tag.event-flags.ashes" + } + } + }, + { + "name": "entity.name.function.event-with-target.ashes", + "match": "^\\s*(:)([a-zA-Z_][a-zA-Z0-9_]*)(\\s*\\|\\s*[A-Z_]+)*\\s+(\"[^\"]*\")", + "captures": { + "1": { + "name": "punctuation.definition.event.ashes" + }, + "2": { + "name": "entity.name.function.event.ashes" + }, + "3": { + "name": "entity.name.tag.event-flags.ashes" + }, + "4": { + "name": "string.quoted.double.event-target.ashes" + } + } + } + ] + }, + "dialog-blocks": { + "patterns": [ + { + "name": "meta.dialog-block.ashes", + "begin": "^(\\s*)(\\?\\!)", + "beginCaptures": { + "1": { + "name": "punctuation.whitespace.indent.ashes" + }, + "2": { + "name": "keyword.control.dialog.ashes" + } + }, + "end": "^(?=\\1[^\\s]|\\s*$)", + "patterns": [ + { + "include": "#dialog-choices" + }, + { + "include": "#commands" + }, + { + "include": "#variables" + }, + { + "include": "#control-flow" + }, + { + "include": "#strings" + }, + { + "include": "#comments" + } + ] + } + ] + }, + "dialog-choices": { + "patterns": [ + { + "name": "meta.dialog-choice.ashes", + "begin": "^(\\s*)(-)\\s*(\"[^\"]*\")\\s*(\\[.*?\\])?", + "beginCaptures": { + "1": { + "name": "punctuation.whitespace.indent.ashes" + }, + "2": { + "name": "punctuation.definition.dialog-choice.ashes" + }, + "3": { + "name": "string.quoted.double.dialog-choice.ashes" + }, + "4": { + "name": "meta.condition.dialog-choice.ashes" + } + }, + "end": "^(?=\\1[^\\s-]|\\s*$)", + "patterns": [ + { + "include": "#commands" + }, + { + "include": "#variables" + }, + { + "include": "#control-flow" + }, + { + "include": "#strings" + }, + { + "include": "#comments" + } + ] + } + ] + }, + "commands": { + "patterns": [ + { + "name": "support.function.command.ashes", + "match": "\\b(accept_input|anim|anim_block|block_say|camera_push|camera_push_block|camera_set_limits|camera_set_pos|camera_set_pos_block|camera_set_target|camera_set_target_block|camera_set_zoom|camera_set_zoom_block|camera_set_zoom_height|camera_set_zoom_height_block|camera_shift|camera_shift_block|change_scene|custom|dec_global|enable_terrain|end_block_say|hide_menu|inc_global|inventory_add|inventory_remove|item_count_add|play_lib_snd|play_snd|play_video|print|print_internal|queue_event|queue_resource|rand_global|repeat|save_game|say|say_last_dialog_option|say_random|say_sequence|sched_event|set_active|set_active_if_exists|set_angle|set_animations|set_direction|set_global|set_globals|set_gui_visible|set_interactive|set_item_custom_data|set_speed|set_state|set_tooltip|show_menu|slide|slide_block|spawn|stop|stop_snd|teleport|teleport_pos|transition|turn_to|wait|walk|walk_block|walk_to_pos|walk_to_pos_block)\\b", + "captures": { + "1": { + "name": "support.function.command.ashes" + } + } + }, + { + "name": "meta.function-call.ashes", + "begin": "\\b([a-zA-Z_][a-zA-Z0-9_]*)\\s*(\\()", + "beginCaptures": { + "1": { + "name": "support.function.command.ashes" + }, + "2": { + "name": "punctuation.definition.parameters.begin.ashes" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.definition.parameters.end.ashes" + } + }, + "patterns": [ + { + "include": "#strings" + }, + { + "include": "#numbers" + }, + { + "include": "#global-ids" + }, + { + "include": "#builtin-variables" + }, + { + "include": "#operators" + } + ] + } + ] + }, + "variables": { + "patterns": [ + { + "name": "storage.type.variable.ashes", + "match": "\\b(var|global)\\b", + "captures": { + "1": { + "name": "storage.type.variable.ashes" + } + } + }, + { + "name": "variable.other.ashes", + "match": "\\b([a-zA-Z_][a-zA-Z0-9_]*)\\s*(=)", + "captures": { + "1": { + "name": "variable.other.ashes" + }, + "2": { + "name": "keyword.operator.assignment.ashes" + } + } + } + ] + }, + "control-flow": { + "patterns": [ + { + "name": "keyword.control.ashes", + "match": "\\b(if|elif|else|while|break|done|stop|pass)\\b", + "captures": { + "1": { + "name": "keyword.control.ashes" + } + } + }, + { + "name": "meta.control-flow.ashes", + "begin": "\\b(if|elif|while)\\s+", + "beginCaptures": { + "1": { + "name": "keyword.control.ashes" + } + }, + "end": ":", + "endCaptures": { + "0": { + "name": "punctuation.separator.condition.ashes" + } + }, + "patterns": [ + { + "include": "#operators" + }, + { + "include": "#builtin-variables" + }, + { + "include": "#global-ids" + }, + { + "include": "#strings" + }, + { + "include": "#numbers" + } + ] + } + ] + }, + "strings": { + "patterns": [ + { + "name": "string.quoted.double.ashes", + "begin": "\"", + "end": "\"", + "patterns": [ + { + "name": "constant.character.escape.ashes", + "match": "\\\\." + }, + { + "name": "variable.other.global.ashes", + "match": "\\{[^}]+\\}" + } + ] + }, + { + "name": "string.quoted.single.ashes", + "begin": "'", + "end": "'", + "patterns": [ + { + "name": "constant.character.escape.ashes", + "match": "\\\\." + } + ] + } + ] + }, + "numbers": { + "patterns": [ + { + "name": "constant.numeric.integer.ashes", + "match": "\\b\\d+\\b" + }, + { + "name": "constant.numeric.float.ashes", + "match": "\\b\\d+\\.\\d+\\b" + } + ] + }, + "operators": { + "patterns": [ + { + "name": "keyword.operator.arithmetic.ashes", + "match": "\\+|-|\\*|/" + }, + { + "name": "keyword.operator.comparison.ashes", + "match": "==|!=|<=|>=|<|>" + }, + { + "name": "keyword.operator.logical.ashes", + "match": "\\b(and|or|not)\\b|!" + }, + { + "name": "keyword.operator.assignment.ashes", + "match": "=" + } + ] + }, + "keywords": { + "patterns": [ + { + "name": "constant.language.boolean.ashes", + "match": "\\b(true|false|nil)\\b" + }, + { + "name": "keyword.other.ashes", + "match": "\\b(in|is|active)\\b" + } + ] + }, + "builtin-variables": { + "patterns": [ + { + "name": "variable.language.builtin.ashes", + "match": "\\b(CURRENT_PLAYER|ESC_LAST_SCENE|ESC_CURRENT_SCENE|FORCE_LAST_SCENE_NULL|ANIMATION_RESOURCES)\\b" + } + ] + }, + "global-ids": { + "patterns": [ + { + "name": "variable.other.global-id.ashes", + "match": "\\$([a-zA-Z_][a-zA-Z0-9_]*)", + "captures": { + "1": { + "name": "variable.other.global-id.ashes" + } + } + } + ] + } + } +} diff --git a/vscode-extension-ashes/tsconfig.json b/vscode-extension-ashes/tsconfig.json new file mode 100644 index 00000000..3d2f8f67 --- /dev/null +++ b/vscode-extension-ashes/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "ES2020", + "outDir": "out", + "lib": [ + "ES2020" + ], + "sourceMap": true, + "rootDir": "src", + "strict": true + }, + "exclude": [ + "node_modules", + ".vscode-test" + ] +} diff --git a/vscode-extension-ashes/vs-escoria4-ashes b/vscode-extension-ashes/vs-escoria4-ashes new file mode 120000 index 00000000..11941b4a --- /dev/null +++ b/vscode-extension-ashes/vs-escoria4-ashes @@ -0,0 +1 @@ +/home/oier/.vscode-oss/extensions/vs-escoria4-ashes \ No newline at end of file