diff --git a/.gitignore b/.gitignore index e4c48304..a3617d82 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,12 @@ -out -node_modules -.vscode-test/ -*.vsix +# Folders .idea/ -package-lock.json \ No newline at end of file +.vscode-test/ +node_modules +out + +# Files +.vscode/settings.json +package-lock.json + +# Both +*.vsix \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 31f94c75..442130aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ # CHANGELOG +## 2.6.0 - 2021-01-18 + +### Features + +- Add `Open Help` command +- Add `Run Selection` command +- Add foldable region comments + +### Fixes + +- Improve formatting for using `ExitApp` to end subroutines +- Fix function coloring for functions whose names were also keywords ([#11](https://github.com/mark-wiemer/vscode-autohotkey-plus-plus/issues/11)) +- Fix function coloring for calls with a space before the parentheses (e.g. `foo ()`) +- Fix detection of labels indented with a tab +- Remove confusing deprecation warning from `StrSplit` function +- Remove variables from outline + ## 2.5.12 - 2020-11-08 - Improve settings readability diff --git a/LICENSE b/LICENSE index 53d8954c..46ccf4f8 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ -Copyright 2020 weijan (https://github.com/cweijan), Mark Wiemer (https://github.com/mark-wiemer) +Copyright (c) 2020 [weijan](https://github.com/cweijan), [Mark Wiemer](https://github.com/mark-wiemer) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 28e82f29..90f405f6 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,22 @@ # AutoHotkey Plus Plus (AHK++) -AutoHotkey Plus Plus (AHK++) provides actively maintained, comprehensive AutoHotkey language support for VS Code. This includes IntelliSense and debug support, along with the standard code highlighting. AHK++ is a fork of the deprecated [AutoHotkey Plus by cweijan](https://github.com/cweijan/vscode-autohotkey#readme). +AutoHotkey Plus Plus (AHK++) provides actively maintained, comprehensive AutoHotkey language support for VS Code. This includes IntelliSense and debug support, along with the standard code highlighting. AHK++ is a fork of the [once-deprecated AutoHotkey Plus by cweijan](https://github.com/AutoHotkey-Plus/vscode-autohotkey/commit/e87f12774692d4c0d792650c099e6071dc17b069). > View this README on the [project site](https://github.com/mark-wiemer/vscode-autohotkey-plus-plus#readme) ## Contents - [Why AutoHotkey Plus Plus?](#why-autohotkey-plus-plus) -- [Coffee](#coffee) - [Install](#install) +- [Commands](#commands) - [Debug](#debug) - [Language Features](#language-features) - [IntelliSense](#intellisense) - [Function Symbol](#function-symbol) - - [Goto Definition](#goto-definition) - - [Find References](#find-references) - - [Code Symbol](#code-symbol) + - [Go To Definition](#go-to-definition) + - [Find References](#find-symbol-references) - [Hover Tip](#hover-tip) - [Code Format](#code-format) -- [Context Menu](#context-menu) - [Credits](#credits) ## Why AutoHotkey Plus Plus? @@ -26,30 +24,40 @@ AutoHotkey Plus Plus (AHK++) provides actively maintained, comprehensive AutoHot AutoHotkey Plus Plus is one of many extensions that offer VS Code language support. So why should you use this one? - **IntelliSense**: Smart code completion, syntax highlighting, code navigation, and more. - **Actively Maintained**: Any issues encountered while using this extension can be reported and fixed. With other extensions, anything that's broken will stay broken forever. You can report any issues (and view all issues) at [the issue tracker](https://github.com/mark-wiemer/vscode-autohotkey-plus-plus/issues) +- **Actively Maintained**: Any issues encountered while using this extension can be reported and fixed. With other extensions, anything that's broken will stay broken forever. You can report any issues with AHK++ (and view all issues) through the [issue tracker](https://github.com/mark-wiemer/vscode-autohotkey-plus-plus/issues). - **Debug Support**: Run and debug AHK scripts from VS Code. - **New Features**: Another benefit to active maintenance is that AHK++ can add new features as users request them. -## Coffee +## Install -If you like this extension, consider [buying the orignal author a coffee](https://www.buymeacoffee.com/cweijan). Thank you! +Install from VS Code or install from VS Code Marketplace: [Install AutoHotkey Plus Plus](https://marketplace.visualstudio.com/items?itemName=mark-wiemer.vscode-autohotkey-plus-plus). -## Install +## Commands -Install from VS Code Marketplace: [Install AutoHotkey Plus Plus](https://marketplace.visualstudio.com/items?itemName=mark-wiemer.vscode-autohotkey-plus-plus). +With AHK++, you can compile, debug, and run your scripts with keyboard shortcuts. You can also run a selection as a standalone script. Additionally, you can `Open Help` with `Ctrl + F1`. + +- Compile: `Ctrl + Shift + F9` +- Debug: `F9` +- Open Help: `Ctrl + F1` +- Run: `Ctrl + F9` +- Run Selection: `Ctrl + F8` ## Debug 1. Click Run or press F9. 2. Debugger supports breakpoints, stack tracing, and variable watching - ![Debug](image/debug.gif) + + ![Debug](image/debug.gif) ### Debug Features 1. **Output Message**: You can use `OutputDebug` command instead of `MsgBox` to log values. - ![Output](image/output.jpg) + + ![Output](image/output.jpg) + 2. **Evaluate**: Set and get variable values through the debug console. - ![Evaluate](image/evalute.jpg) + + ![Evaluate](image/evalute.jpg) This extension provides basic debugging functions. If you need more debugging functions (such as conditional breakpoints), you can add an additional extension: [Install vscode-autohotkey-debug](https://marketplace.visualstudio.com/items?itemName=zero-plusplus.vscode-autohotkey-debug). @@ -61,33 +69,26 @@ Supports IntelliSense for variables and functions. ### Function Symbol -1. Detach source function as symbol -2. You can add a comment to the function using a semicolon on the line above the function declaration - -![Function Symbol](image/functionSymbol.jpg) +1. You can add a comment to the function using a semicolon on the line above the function declaration -### Goto Definition +### Go to Definition -1. Support goto function and variable definition. +1. Supports navigation to symbol definition. 2. Usage: Ctrl-click on the symbol to navigate to its definition. ![Goto Definition](image/gotoDefinition.jpg) ### Find Symbol References -Usage: Move coordinates to symbol, then: +Select a symbol, then: -- Right-click on a symbol, then select `Find All References`. +- Right-click and select `Find All References`. - Or press `Shift + F12`. -### Code Symbol - -Usage: Add two semicolon to comment code block -![Code Symbol](image/codeSymbol.jpg) - ### Hover Tip -Usage: Move mouse to function call or command. +Usage: Hover over symbol to see IntelliSense documentation. + ![Hover](image/hover.png) ### Code Format @@ -98,19 +99,9 @@ Supports standard VS Code formatting. ![Code Format](image/codeFormat.jpg) -## Context Menu - -Run and compile code from the context menu. - -Right-click to open the context menu, then: - -- **Run**: Run script without debug (`Ctrl + F9`). -- **Compile**: Compile script in same directory (`Ctrl + Shift + F9`). - ![compile](image/compile.jpg) - ## Credits Previous extensions: -- [AutoHotkey Plus](https://github.com/cweijan/vscode-autohotkey) -- [AutoHotkey](https://github.com/stef-levesque/vscode-autohotkey) +- [AutoHotkey Plus by cweijan](https://github.com/cweijan/vscode-autohotkey) +- [AutoHotkey by stef-levesque](https://github.com/stef-levesque/vscode-autohotkey) diff --git a/ahk.configuration.json b/ahk.configuration.json index 8e12cadd..32b4354e 100644 --- a/ahk.configuration.json +++ b/ahk.configuration.json @@ -28,11 +28,10 @@ ["'", "'"], ["%", "%"] ], - // Folding regions marked by ";region" and ";endregion" comments. "folding": { "markers": { - "start": "^\\s*\\;\\s*region\\b", - "end": "^\\s*\\;\\s*endregion\\b" + "start": "^(\\/\\*)?\\s*\\;\\s*region\\b", + "end": "^(\\*\\/)?\\s*\\;\\s*endregion\\b" } } } diff --git a/demos/demo_for_ahk_v1.ahk b/demos/demo_for_ahk_v1.ahk index f786de4b..6335f3d3 100644 --- a/demos/demo_for_ahk_v1.ahk +++ b/demos/demo_for_ahk_v1.ahk @@ -4,54 +4,103 @@ function() return function() { - globalVar := "Local" - SuperGlobalVar := "Local" - bool := true - str := "string" - if (str == "str") { - MsgBox Overwrite primitive variable! - } - str_multiline := " - (LTrim - line 1 - line 2 - line 3 - )" - int := 123 - float := 123.456 - - emptyArray := [] - smallArray := [1, 2, { str: "string" }] + globalVar := "Local" + SuperGlobalVar := "Local" + bool := true + str := "string" + if (str == "str") { + MsgBox Overwrite primitive variable! + } + ; Known bug: `line ` lines should be indented one level more + ; https://github.com/mark-wiemer/vscode-autohotkey-plus-plus/issues/25 + str_multiline := " + (LTrim + line 1 + line 2 + line 3 + )" + int := 123 + float := 123.456 + + emptyArray := [] + smallArray := [1, 2, { str: "string" }] sparseArray := { 1: 1, 3: 3 } - arrayLike := { 1: 1, 2: 2, 3: 3, length: 3 } - bigArray := [] - Loop 150 { - bigArray.push(A_Index) - } - if (bigArray == "str") { - MsgBox Overwrite object variable! - } - - obj := { str: str, int: int, float: float } - objobj := { str: str, obj: obj } - objobjobj := { str: str, int: int, obj: { str: str, obj: obj } } - - circular := {} - circular.circular := circular - instance := new Cls() - - enum := obj._NewEnum() + arrayLike := { 1: 1, 2: 2, 3: 3, length: 3 } + bigArray := [] + Loop 150 { + bigArray.push(A_Index) + } + if (bigArray == "str") { + MsgBox Overwrite object variable! + } + + obj := { str: str, int: int, float: float } + objobj := { str: str, obj: obj } + ; Known bug: the entire body of a function should be indented + ; https://github.com/mark-wiemer/vscode-autohotkey-plus-plus/issues/26 +objobjobj := { str: str, int: int, obj: { str: str, obj: obj } } + +circular := {} +circular.circular := circular +instance := new Cls() + +enum := obj._NewEnum() } class Cls { - instanceVar := "instance" - static str := "string" - static num := 123 - static obj := { str: "string", int: 123, float: 123.456 } - property[] { - get { - } - } - method() { - } + instanceVar := "instance" + static str := "string" + static num := 123 + static obj := { str: "string", int: 123, float: 123.456 } + property[] { + get { + } + } + method() { + } +} + +; Block comments and nested regions +/* ;region +Collapse me! +{ + Collapse me too! } +*/ ;endregion + +; Hotkeys and Keywords + +<#Tab:: AltTab + +; FUNCTIONS + +; Method header comment accessible to IntelliSense +LAlt() { + ; do a thing +} + +; Function calls (with a space before parens) +foo() +bar () +baz () + +; Functions with keyword names +LAlt() +Pause() +AppsKey() +CapsLock() + +; SUBROUTINES + +; ExitApp indentation for subroutines +MySub: + foo() +ExitApp + +; RUN SELECTION + +; Select the following line and hit `Ctrl + F8` to run selection +f1:: MsgBox, You hit F1 + +; The F2 hotkey will not work because it was not part of the selection +f2:: MsgBox, You hit F2 \ No newline at end of file diff --git a/docs/Development.md b/docs/Development.md index 4b74191c..e5f62997 100644 --- a/docs/Development.md +++ b/docs/Development.md @@ -3,10 +3,14 @@ This document covers the development process, from writing code to publishing a new version. 1. Write the code on the `dev` branch, or offshoots of that branch. Merge the changes to the `dev` branch as they become stable. + - Test all added commands + - Perform the formatting tests + - Confirm README appears as intended + - Confirm links in README work 1. Once the `dev` branch has all the features for a new release, create a new release branch named `v-..` (e.g. `v-2.5.10`). + - Confirm the package version has been updated + - Confirm the changelog has been updated 1. Push the changes, open a PR, review the changes, and merge to `master`. - 1. Confirm the package version has been updated - 1. Confirm the changelog has been updated 1. Pull the new master branch 1. Package the new release using `vsce package`. 1. Publish the release through [Visual Studio Marketplace](https://marketplace.visualstudio.com/manage/publishers/mark-wiemer) diff --git a/image/codeSymbol.jpg b/image/codeSymbol.jpg deleted file mode 100644 index 04f6f91f..00000000 Binary files a/image/codeSymbol.jpg and /dev/null differ diff --git a/image/compile.jpg b/image/compile.jpg deleted file mode 100644 index cc8ee3be..00000000 Binary files a/image/compile.jpg and /dev/null differ diff --git a/image/functionSymbol.jpg b/image/functionSymbol.jpg deleted file mode 100644 index c492f19c..00000000 Binary files a/image/functionSymbol.jpg and /dev/null differ diff --git a/package.json b/package.json index 6f18efda..6bf49646 100644 --- a/package.json +++ b/package.json @@ -2,14 +2,14 @@ "name": "vscode-autohotkey-plus-plus", "displayName": "AutoHotkey Plus Plus", "description": "AutoHotkey IntelliSense, debug, and language support for VS Code, forked from AutoHotkey Plus by cweijan", - "version": "2.5.12", + "version": "2.6.0", "publisher": "mark-wiemer", "engines": { "vscode": "^1.30.0" }, "activationEvents": [ "onLanguage:ahk", - "onCommand:run.ahk", + "onCommand:ahk++.run", "onDebug" ], "main": "./out/extension", @@ -17,7 +17,7 @@ "Programming Languages", "Snippets" ], - "license": "See LICENSE file", + "license": "MIT", "icon": "icon.png", "bugs": { "url": "https://github.com/mark-wiemer/vscode-autohotkey-plus-plus/issues" @@ -141,42 +141,57 @@ ], "commands": [ { - "command": "compile.ahk", - "title": "Compile Script", - "category": "AHK", + "command": "ahk++.compile", + "title": "Compile AHK Script", + "category": "AHK++", "icon": "./image/build.svg" }, { - "command": "run.ahk", - "title": "Run AHK Script", - "category": "AHK" - }, - { - "command": "debug.ahk", + "command": "ahk++.debug", "title": "Debug AHK Script", - "category": "AHK", + "category": "AHK++", "icon": { "light": "./image/run.png", "dark": "./image/run-dark.png" } + }, + { + "command": "ahk++.openHelp", + "title": "Open AutoHotkey Help", + "category": "AHK++" + }, + { + "command": "ahk++.run", + "title": "Run AHK Script", + "category": "AHK++" + }, + { + "command": "ahk++.runSelection", + "title": "Run Selection", + "category": "AHK" } ], "menus": { "editor/context": [ { - "command": "run.ahk", + "command": "ahk++.compile", "when": "editorLangId == ahk", - "group": "navigation@0" + "group": "navigation@1" }, { - "command": "compile.ahk", + "command": "ahk++.run", "when": "editorLangId == ahk", - "group": "navigation@1" + "group": "navigation@0" + }, + { + "command": "ahk++.runSelection", + "when": "editorLangId == ahk && editorHasSelection", + "group": "navigation@-1" } ], "editor/title": [ { - "command": "debug.ahk", + "command": "ahk++.debug", "when": "editorLangId == ahk", "group": "navigation@1" } @@ -184,18 +199,28 @@ }, "keybindings": [ { - "command": "debug.ahk", + "command": "ahk++.compile", + "key": "ctrl+shift+f9", + "when": "editorLangId == ahk" + }, + { + "command": "ahk++.debug", "key": "f9", "when": "editorLangId == ahk" }, { - "command": "run.ahk", + "command": "ahk++.openHelp", + "key": "ctrl+f1", + "when": "editorLangId == ahk" + }, + { + "command": "ahk++.run", "key": "ctrl+f9", "when": "editorLangId == ahk" }, { - "command": "compile.ahk", - "key": "ctrl+shift+f9", + "command": "ahk++.runSelection", + "key": "ctrl+f8", "when": "editorLangId == ahk" } ], @@ -209,15 +234,20 @@ "type": "object", "title": "AutoHotkey Plus Plus", "properties": { - "ahk++.executePath": { - "type": "string", - "default": "C:/Program Files/AutoHotkey/AutoHotkeyU64.exe", - "description": "The execute path of AHK." - }, "ahk++.compilePath": { "type": "string", "default": "C:/Program Files/AutoHotkey/Compiler/Ahk2Exe.exe", - "description": "The compiler path of AHK." + "description": "Path to the AHK compiler." + }, + "ahk++.helpPath": { + "type": "string", + "default": "C:/Program Files/AutoHotkey/AutoHotkey.chm", + "description": "Path to the AHK Help document." + }, + "ahk++.executePath": { + "type": "string", + "default": "C:/Program Files/AutoHotkey/AutoHotkeyU64.exe", + "description": "Path to the AHK runner." }, "ahk++.intellisense": { "type": "boolean", diff --git a/snippets/ahk.json b/snippets/ahk.json index 633322b7..56aefb70 100644 --- a/snippets/ahk.json +++ b/snippets/ahk.json @@ -1671,7 +1671,7 @@ "StrSplit()": { "prefix": "StrSplit()", "body": "StrSplit(${1:String}, ${2:[Delimiters}, ${3:OmitChars]})", - "description": "Separates a string into an array of substrings using the specified delimiters.\nāš Deprecated: Use the StrSplit function instead." + "description": "Separates a string into an array of substrings using the specified delimiters." }, "StrReplace()": { "prefix": "StrReplace()", diff --git a/src/common/fileManager.ts b/src/common/fileManager.ts index 4753332f..75b67cde 100644 --- a/src/common/fileManager.ts +++ b/src/common/fileManager.ts @@ -1,14 +1,14 @@ import * as fs from 'fs'; import * as path from 'path'; import * as vscode from 'vscode'; -import { Detecter } from '../core/detect/detecter'; +import { Parser } from '../parser/parser'; export class FileManager { private static storagePath: string; public static init(context: vscode.ExtensionContext) { - this.storagePath = context.globalStoragePath; // TODO + this.storagePath = context.globalStoragePath; vscode.workspace.onDidSaveTextDocument((e) => { - Detecter.buildScript(e); + Parser.buildScript(e); }); } diff --git a/src/common/global.ts b/src/common/global.ts index 38245849..69146338 100644 --- a/src/common/global.ts +++ b/src/common/global.ts @@ -30,6 +30,7 @@ export class Global { export enum ConfigKey { compilePath = 'compilePath', + helpPath = 'helpPath', + enableIntelliSense = 'enableIntelliSense', executePath = 'executePath', - intellisense = 'intellisense', } diff --git a/src/common/out.ts b/src/common/out.ts index 01bbbf58..6ecc0504 100644 --- a/src/common/out.ts +++ b/src/common/out.ts @@ -1,4 +1,3 @@ -'user strict'; import * as vscode from 'vscode'; import { OutputChannel } from 'vscode'; diff --git a/src/debugger/debugDispatcher.ts b/src/debugger/debugDispatcher.ts index 4b6463e2..1990f0bb 100644 --- a/src/debugger/debugDispatcher.ts +++ b/src/debugger/debugDispatcher.ts @@ -1,7 +1,7 @@ import { EventEmitter } from 'events'; import { Scope, StackFrame, Variable } from 'vscode-debugadapter'; import { DebugProtocol } from 'vscode-debugprotocol'; -import { ScriptRunner } from '../core/ScriptRunner'; +import { RunnerService } from '../service/runnerService'; import { DebugServer } from './debugServer'; import { LaunchRequestArguments } from './debugSession'; import { BreakPointHandler } from './handler/breakpointHandler'; @@ -96,11 +96,11 @@ export class DebugDispatcher extends EventEmitter { } }); if (!args.program) { - args.program = await ScriptRunner.getPathByActive(); + args.program = await RunnerService.getPathByActive(); } if (!existsSync(runtime)) { - Out.log(`AutoHotkey Execute Bin Not Found: ${runtime}`); + Out.log(`AutoHotkey execute bin not found: ${runtime}`); this.end(); return; } @@ -119,7 +119,7 @@ export class DebugDispatcher extends EventEmitter { public async restart() { this.sendComand('stop'); this.end(); - ScriptRunner.startDebugger(this.startArgs.program); + RunnerService.startDebugger(this.startArgs.program); } /** diff --git a/src/debugger/debugSession.ts b/src/debugger/debugSession.ts index 05b5a7b2..193daf73 100644 --- a/src/debugger/debugSession.ts +++ b/src/debugger/debugSession.ts @@ -1,3 +1,4 @@ +import { commands } from 'vscode'; import { BreakpointEvent, InitializedEvent, @@ -61,6 +62,7 @@ export class DebugSession extends LoggingDebugSession { }) .on('output', (text) => { this.sendEvent(new OutputEvent(`${text}\n`)); + commands.executeCommand('workbench.debug.action.focusRepl'); }) .on('end', () => { this.sendEvent(new TerminatedEvent()); diff --git a/src/extension.ts b/src/extension.ts index 8258a93d..1c6d43fa 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,24 +1,25 @@ import * as vscode from 'vscode'; import { ProviderResult } from 'vscode'; -import { Detecter } from './core/detect/detecter'; -import { ScriptRunner } from './core/ScriptRunner'; +import { Parser } from './parser/parser'; +import { RunnerService } from './service/runnerService'; import { DebugSession } from './debugger/debugSession'; -import { DefProvider } from './provider/DefProvider'; -import { TemplateProvider } from './provider/templateProvider'; -import { FormatProvider } from './provider/formattingProvider'; -import { SymBolProvider } from './provider/SymbolProvider'; +import { DefProvider } from './providers/defProvider'; +import { TemplateService } from './service/templateService'; +import { FormatProvider } from './providers/formattingProvider'; +import { SymbolProvider } from './providers/symbolProvider'; import { FileManager } from './common/fileManager'; -import { AhkHoverProvider } from './provider/ahkHoverProvider'; -import { RefProvider } from './provider/RefProvider'; +import { AhkHoverProvider } from './providers/ahkHoverProvider'; +import { RefProvider } from './providers/refProvider'; import { Global, ConfigKey } from './common/global'; -import { AhkRenameProvider } from './provider/ahkRenameProvider'; -import { SignatureProvider } from './provider/signatureProvider'; -import { CompletionProvider } from './provider/CompletionProvider'; +import { AhkRenameProvider } from './providers/ahkRenameProvider'; +import { SignatureProvider } from './providers/signatureProvider'; +import { CompletionProvider } from './providers/completionProvider'; +import { HelpService } from './service/helpService'; export function activate(context: vscode.ExtensionContext) { (async () => { Global.updateStatusBarItems('Indexing AutoHotkey Workspace...'); - await Detecter.buildByPath(vscode.workspace.rootPath); + await Parser.buildByPath(vscode.workspace.rootPath); Global.updateStatusBarItems('Index Workspace Success!'); Global.hide(); })(); @@ -46,28 +47,34 @@ export function activate(context: vscode.ExtensionContext) { ), vscode.languages.registerDocumentSymbolProvider( language, - new SymBolProvider(), + new SymbolProvider(), ), vscode.languages.registerDocumentFormattingEditProvider( language, new FormatProvider(), ), vscode.languages.registerReferenceProvider(language, new RefProvider()), - TemplateProvider.createEditorListenr(), vscode.debug.registerDebugAdapterDescriptorFactory( 'ahk', new InlineDebugAdapterFactory(), ), - vscode.commands.registerCommand('run.ahk', () => ScriptRunner.run()), - vscode.commands.registerCommand('debug.ahk', () => - ScriptRunner.startDebugger(), + TemplateService.createEditorListener(), + vscode.commands.registerCommand('ahk++.compile', () => + RunnerService.compile(), ), - vscode.commands.registerCommand('compile.ahk', () => - ScriptRunner.compile(), + vscode.commands.registerCommand('ahk++.debug', () => + RunnerService.startDebugger(), + ), + vscode.commands.registerCommand('ahk++.openHelp', () => + HelpService.open(), + ), + vscode.commands.registerCommand('ahk++.run', () => RunnerService.run()), + vscode.commands.registerCommand('ahk++.runSelection', () => + RunnerService.runSelection(), ), ); - if (Global.getConfig(ConfigKey.intellisense)) { + if (Global.getConfig(ConfigKey.enableIntelliSense)) { context.subscriptions.push( vscode.languages.registerCompletionItemProvider( language, diff --git a/src/core/detect/model.ts b/src/parser/model.ts similarity index 100% rename from src/core/detect/model.ts rename to src/parser/model.ts diff --git a/src/core/detect/detecter.ts b/src/parser/parser.ts similarity index 93% rename from src/core/detect/detecter.ts rename to src/parser/parser.ts index dc1c8b9b..628f83b2 100644 --- a/src/core/detect/detecter.ts +++ b/src/parser/parser.ts @@ -1,10 +1,10 @@ import * as fs from 'fs'; import * as vscode from 'vscode'; -import { CodeUtil } from '../../common/codeUtil'; -import { Out } from '../../common/out'; +import { CodeUtil } from '../common/codeUtil'; +import { Out } from '../common/out'; import { Script, Method, Ref, Label, Block, Variable } from './model'; -export class Detecter { +export class Parser { private static documentCache = new Map(); /** @@ -66,7 +66,7 @@ export class Detecter { if (blockComment) { continue; } - const methodOrRef = Detecter.detechMethodByLine(document, line); + const methodOrRef = Parser.detechMethodByLine(document, line); if (methodOrRef) { if (methodOrRef instanceof Method) { methods.push(methodOrRef); @@ -85,12 +85,12 @@ export class Detecter { CodeUtil.join(refs, methodOrRef); } } - const label = Detecter.getLabelByLine(document, line); + const label = Parser.getLabelByLine(document, line); if (label) { labels.push(label); continue; } - const block = Detecter.getBlockByLine(document, line); + const block = Parser.getBlockByLine(document, line); if (block) { blocks.push(block); } @@ -103,7 +103,7 @@ export class Detecter { currentMethod.endLine = line; } } - const variable = Detecter.detechVariableByLine(document, line); + const variable = Parser.detechVariableByLine(document, line); if (variable) { if (deep == 0 || !currentMethod) { this.joinVars(variables, variable); @@ -198,7 +198,7 @@ export class Detecter { private static getLabelByLine(document: vscode.TextDocument, line: number) { const text = CodeUtil.purity(document.lineAt(line).text); - const label = /^ *([\u4e00-\u9fa5_a-zA-Z0-9]+) *:{1}(?!(:|=))/.exec( + const label = /^[ \t]*([\u4e00-\u9fa5_a-zA-Z0-9]+) *:{1}(?!(:|=))/.exec( text, ); if (label) { @@ -221,7 +221,7 @@ export class Detecter { ): Variable | Variable[] { const lineText = CodeUtil.purity(document.lineAt(line).text); - const defMatch = lineText.match(Detecter.varDefPattern); + const defMatch = lineText.match(Parser.varDefPattern); if (defMatch) { const varName = defMatch[1]; return { @@ -235,7 +235,7 @@ export class Detecter { } else { let vars = []; const commandMatchAll = CodeUtil.matchAll( - Detecter.varCommandPattern, + Parser.varCommandPattern, lineText.replace(/\(.+?\)/g, ''), ); for (let index = 0; index < commandMatchAll.length; index++) { @@ -298,7 +298,7 @@ export class Detecter { line, character, true, - Detecter.getRemarkByLine(document, line - 1), + Parser.getRemarkByLine(document, line - 1), ); } for (let i = line + 1; i < document.lineCount; i++) { @@ -314,7 +314,7 @@ export class Detecter { line, character, false, - Detecter.getRemarkByLine(document, line - 1), + Parser.getRemarkByLine(document, line - 1), ); } else { return new Ref(methodName, document, line, character); diff --git a/src/provider/SymbolProvider.ts b/src/providers/SymbolProvider.ts similarity index 71% rename from src/provider/SymbolProvider.ts rename to src/providers/SymbolProvider.ts index f1173eb8..207a5873 100644 --- a/src/provider/SymbolProvider.ts +++ b/src/providers/SymbolProvider.ts @@ -1,14 +1,14 @@ import * as vscode from 'vscode'; -import { Detecter } from '../core/detect/detecter'; +import { Parser } from '../parser/parser'; -export class SymBolProvider implements vscode.DocumentSymbolProvider { +export class SymbolProvider implements vscode.DocumentSymbolProvider { public async provideDocumentSymbols( document: vscode.TextDocument, token: vscode.CancellationToken, ): Promise { const result = []; - const script = await Detecter.buildScript(document, false); + const script = await Parser.buildScript(document, false); for (const method of script.methods) { result.push( @@ -52,20 +52,6 @@ export class SymBolProvider implements vscode.DocumentSymbolProvider { ); } - for (const variable of script.variables) { - result.push( - new vscode.SymbolInformation( - variable.name, - vscode.SymbolKind.Variable, - null, - new vscode.Location( - variable.document.uri, - new vscode.Position(variable.line, variable.character), - ), - ), - ); - } - return result; } } diff --git a/src/provider/ahkHoverProvider.ts b/src/providers/ahkHoverProvider.ts similarity index 95% rename from src/provider/ahkHoverProvider.ts rename to src/providers/ahkHoverProvider.ts index 8c64785c..b1edb59a 100644 --- a/src/provider/ahkHoverProvider.ts +++ b/src/providers/ahkHoverProvider.ts @@ -1,17 +1,16 @@ +import { readFileSync } from 'fs'; +import { join } from 'path'; import { - HoverProvider, - TextDocument, - Position, CancellationToken, ExtensionContext, - Range, Hover, + HoverProvider, MarkdownString, + Position, + Range, + TextDocument, } from 'vscode'; -import { join } from 'path'; -import { readFileSync } from 'fs'; -import { worker } from 'cluster'; -import { Detecter } from '../core/detect/detecter'; +import { Parser } from '../parser/parser'; interface Snippet { prefix: string; @@ -42,7 +41,7 @@ export class AhkHoverProvider implements HoverProvider { return snippetHover; } - const method = await Detecter.getMethodByName(document, context.word); + const method = await Parser.getMethodByName(document, context.word); if (method) { const markdonw = new MarkdownString('', true).appendCodeblock( method.full, diff --git a/src/provider/ahkRenameProvider.ts b/src/providers/ahkRenameProvider.ts similarity index 88% rename from src/provider/ahkRenameProvider.ts rename to src/providers/ahkRenameProvider.ts index 0be969ea..8010a720 100644 --- a/src/provider/ahkRenameProvider.ts +++ b/src/providers/ahkRenameProvider.ts @@ -1,5 +1,6 @@ import * as vscode from 'vscode'; -import { Detecter } from '../core/detect/detecter'; +import { Parser } from '../parser/parser'; +import { Out } from '../common/out'; export class AhkRenameProvider implements vscode.RenameProvider { async provideRenameEdits( @@ -9,13 +10,13 @@ export class AhkRenameProvider implements vscode.RenameProvider { token: vscode.CancellationToken, ): Promise { for (const doc of vscode.workspace.textDocuments) { - Detecter.buildScript(doc); + Parser.buildScript(doc); } const word = document.getText( document.getWordRangeAtPosition(position), ); - const refs = Detecter.getAllRefByName(word); + const refs = Parser.getAllRefByName(word); const workEdit = new vscode.WorkspaceEdit(); for (const ref of refs) { if (ref.document.uri.scheme != 'file') { @@ -50,7 +51,7 @@ export class AhkRenameProvider implements vscode.RenameProvider { const wordRange = document.getWordRangeAtPosition(position); const word = document.getText(wordRange); - const method = await Detecter.getMethodByName(document, word); + const method = await Parser.getMethodByName(document, word); if (method != null) { return wordRange; } diff --git a/src/provider/CompletionProvider.ts b/src/providers/completionProvider.ts similarity index 94% rename from src/provider/CompletionProvider.ts rename to src/providers/completionProvider.ts index f6b5609a..fcae2fd3 100644 --- a/src/provider/CompletionProvider.ts +++ b/src/providers/completionProvider.ts @@ -1,5 +1,5 @@ import * as vscode from 'vscode'; -import { Detecter } from '../core/detect/detecter'; +import { Parser } from '../parser/parser'; import { SnippetString } from 'vscode'; export class CompletionProvider implements vscode.CompletionItemProvider { @@ -27,7 +27,7 @@ export class CompletionProvider implements vscode.CompletionItemProvider { const result: vscode.CompletionItem[] = []; - (await Detecter.getAllMethod()).forEach((method) => { + (await Parser.getAllMethod()).forEach((method) => { const completionItem = new vscode.CompletionItem( method.params.length == 0 ? method.name : method.full, vscode.CompletionItemKind.Method, @@ -65,7 +65,7 @@ export class CompletionProvider implements vscode.CompletionItemProvider { } }); - const script = await Detecter.buildScript(document, true); + const script = await Parser.buildScript(document, true); script.variables.forEach((variable) => { const completionItem = new vscode.CompletionItem( variable.name, diff --git a/src/provider/DefProvider.ts b/src/providers/defProvider.ts similarity index 93% rename from src/provider/DefProvider.ts rename to src/providers/defProvider.ts index 1db15d51..fd14921b 100644 --- a/src/provider/DefProvider.ts +++ b/src/providers/defProvider.ts @@ -1,5 +1,5 @@ import * as vscode from 'vscode'; -import { Detecter } from '../core/detect/detecter'; +import { Parser } from '../parser/parser'; import { existsSync } from 'fs'; export class DefProvider implements vscode.DefinitionProvider { @@ -23,7 +23,7 @@ export class DefProvider implements vscode.DefinitionProvider { document.lineAt(position.line).text, ) ) { - const method = await Detecter.getMethodByName(document, word); + const method = await Parser.getMethodByName(document, word); if (method) { const methodDoc = method.document; return new vscode.Location( @@ -34,7 +34,7 @@ export class DefProvider implements vscode.DefinitionProvider { } // getlabel - const label = await Detecter.getLabelByName(document, word); + const label = await Parser.getLabelByName(document, word); if (label) { const tempDocument = label.document; return new vscode.Location( @@ -43,7 +43,7 @@ export class DefProvider implements vscode.DefinitionProvider { ); } - const script = await Detecter.buildScript(document, true); + const script = await Parser.buildScript(document, true); for (const method of script.methods) { if ( diff --git a/src/provider/formattingProvider.ts b/src/providers/formattingProvider.ts similarity index 98% rename from src/provider/formattingProvider.ts rename to src/providers/formattingProvider.ts index 6198fede..dbc0aedf 100644 --- a/src/provider/formattingProvider.ts +++ b/src/providers/formattingProvider.ts @@ -70,7 +70,7 @@ export class FormatProvider implements vscode.DocumentFormattingEditProvider { notDeep = false; } - if (purityText.match(/\b(return)\b/i) && tagDeep === deep) { + if (purityText.match(/\b(return|ExitApp)\b/i) && tagDeep === deep) { tagDeep == 0; deep--; notDeep = false; diff --git a/src/provider/RefProvider.ts b/src/providers/refProvider.ts similarity index 87% rename from src/provider/RefProvider.ts rename to src/providers/refProvider.ts index ba2b213f..2046b5fc 100644 --- a/src/provider/RefProvider.ts +++ b/src/providers/refProvider.ts @@ -1,5 +1,5 @@ import * as vscode from 'vscode'; -import { Detecter } from '../core/detect/detecter'; +import { Parser } from '../parser/parser'; export class RefProvider implements vscode.ReferenceProvider { public provideReferences( @@ -13,7 +13,7 @@ export class RefProvider implements vscode.ReferenceProvider { ); const vscodeRefs = []; - const refs = Detecter.getAllRefByName(word); + const refs = Parser.getAllRefByName(word); for (const ref of refs) { vscodeRefs.push( new vscode.Location( diff --git a/src/provider/signatureProvider.ts b/src/providers/signatureProvider.ts similarity index 91% rename from src/provider/signatureProvider.ts rename to src/providers/signatureProvider.ts index fbe6be11..92198bca 100644 --- a/src/provider/signatureProvider.ts +++ b/src/providers/signatureProvider.ts @@ -1,5 +1,5 @@ import * as vscode from 'vscode'; -import { Detecter } from '../core/detect/detecter'; +import { Parser } from '../parser/parser'; export class SignatureProvider implements vscode.SignatureHelpProvider { public async provideSignatureHelp( @@ -25,7 +25,7 @@ export class SignatureProvider implements vscode.SignatureHelpProvider { document.getWordRangeAtPosition(methodPosition), ); - const method = await Detecter.getMethodByName(document, word); + const method = await Parser.getMethodByName(document, word); if (method) { return { activeSignature: 0, diff --git a/src/service/helpService.ts b/src/service/helpService.ts new file mode 100644 index 00000000..5be07e97 --- /dev/null +++ b/src/service/helpService.ts @@ -0,0 +1,9 @@ +import { ConfigKey, Global } from '../common/global'; +import { Process } from '../common/processWrapper'; + +export class HelpService { + public static open(): void { + const helpPath = Global.getConfig(ConfigKey.helpPath); + Process.exec(`C:/Windows/hh.exe ${helpPath}`); + } +} diff --git a/src/core/ScriptRunner.ts b/src/service/runnerService.ts similarity index 63% rename from src/core/ScriptRunner.ts rename to src/service/runnerService.ts index 228bd4e5..e8daa171 100644 --- a/src/core/ScriptRunner.ts +++ b/src/service/runnerService.ts @@ -4,10 +4,20 @@ import { FileManager, FileModel } from '../common/fileManager'; import { ConfigKey, Global } from '../common/global'; import { Process } from '../common/processWrapper'; -export class ScriptRunner { - /** - * start debuggin session - */ +export class RunnerService { + /** Runs the editor selection as a standalone script. */ + public static async runSelection(): Promise { + const editor = vscode.window.activeTextEditor; + if (!editor) { + vscode.window.showErrorMessage('No active editor found!'); + return; + } + + var selection = editor.selection; + var text = editor.document.getText(selection); + this.run(await this.createTemplate(text)); + } + public static async startDebugger(script?: string) { const cwd = script ? vscode.Uri.file(script) @@ -27,61 +37,35 @@ export class ScriptRunner { } /** - * run/debug script - * @param executePath runtime path - * @param path execute script path - * @param debug enable debug model? - * @param debugPort debug proxy port + * Runs the script at the specified path */ - public static async run( - executePath = null, - path: string = null, - debug: boolean = false, - debugPort = 9000, - ): Promise { - executePath = Global.getConfig(ConfigKey.executePath); - if (!vscode.window.activeTextEditor.document.isUntitled) { - vscode.commands.executeCommand('workbench.action.files.save'); - } - if (executePath) { - if (!path) { - path = await this.getPathByActive(); - } - try { - await Process.exec( - `\"${executePath}\"${ - debug - ? ' /ErrorStdOut /debug=localhost:' + debugPort - : '' - } \"${path}\"`, - { cwd: `${res(path, '..')}` }, - ); - return true; - } catch (error) { - return false; - } - } else { - return false; + public static async run(path?: string): Promise { + const executePath = Global.getConfig(ConfigKey.executePath); + this.checkAndSaveActive(); + if (!path) { + path = await this.getPathByActive(); } + Process.exec(`\"${executePath}\" \"${path}\"`, { + cwd: `${res(path, '..')}`, + }); } /** - * compile current script + * Compiles current script */ public static async compile() { const currentPath = vscode.window.activeTextEditor.document.uri.fsPath; - if (!vscode.window.activeTextEditor.document.isUntitled) { - vscode.commands.executeCommand('workbench.action.files.save'); - } if (!currentPath) { + vscode.window.showErrorMessage('Cannot compile never-saved files.'); return; } + this.checkAndSaveActive(); const pos = currentPath.lastIndexOf('.'); const compilePath = currentPath.substr(0, pos < 0 ? currentPath.length : pos) + '.exe'; if ( await Process.exec( - `"${Global.getConfig( + `"${Global.getConfig( ConfigKey.compilePath, )}" /in "${currentPath}" /out "${compilePath}"`, { cwd: `${res(currentPath, '..')}` }, @@ -94,17 +78,22 @@ export class ScriptRunner { public static async getPathByActive(): Promise { const document = vscode.window.activeTextEditor.document; if (document.isUntitled) { - const path = `temp-${this.getNowDate()}.ahk`; - const fullPath = await FileManager.record( - path, - document.getText(), - FileModel.WRITE, - ); - return fullPath; + return await this.createTemplate(document.getText()); } return document.fileName; } + public static async createTemplate(content: string) { + const path = `temp-${this.getNowDate()}.ahk`; + return await FileManager.record(path, content, FileModel.WRITE); + } + + private static checkAndSaveActive(): void { + if (!vscode.window.activeTextEditor.document.isUntitled) { + vscode.commands.executeCommand('workbench.action.files.save'); + } + } + private static getNowDate(): string { const date = new Date(); let month: string | number = date.getMonth() + 1; @@ -133,7 +122,7 @@ export class ScriptRunner { ); } - public static pad(n: any, width: number, z?: any): number { + private static pad(n: any, width: number, z?: any): number { z = z || '0'; n = n + ''; return n.length >= width diff --git a/src/provider/templateProvider.ts b/src/service/templateService.ts similarity index 82% rename from src/provider/templateProvider.ts rename to src/service/templateService.ts index 563b7adb..17048a3a 100644 --- a/src/provider/templateProvider.ts +++ b/src/service/templateService.ts @@ -1,7 +1,7 @@ import * as vscode from 'vscode'; -export class TemplateProvider { - public static createEditorListenr(): vscode.Disposable { +export class TemplateService { + public static createEditorListener(): vscode.Disposable { return vscode.window.onDidChangeActiveTextEditor((e) => { if ( e && diff --git a/syntaxes/ahk.tmLanguage.json b/syntaxes/ahk.tmLanguage.json index 16d58390..8dff9820 100644 --- a/syntaxes/ahk.tmLanguage.json +++ b/syntaxes/ahk.tmLanguage.json @@ -184,7 +184,7 @@ "example": "this" }, { - "match": "\\b(?!MsgBox)(?i:new|__New|__Delete|shift|lshift|rshift|alt|lalt|ralt|control|lcontrol|rcontrol|ctrl|lctrl|rctrl|lwin|rwin|appskey|altdown|altup|shiftdown|shiftup|ctrldown|ctrlup|lwindown|lwinup|rwindown|rwinup|lbutton|rbutton|mbutton|wheelup|wheelleft|wheelright|wheeldown|xbutton1|xbutton2|joy1|joy2|joy3|joy4|joy5|joy6|joy7|joy8|joy9|joy10|joy11|joy12|joy13|joy14|joy15|joy16|joy17|joy18|joy19|joy20|joy21|joy22|joy23|joy24|joy25|joy26|joy27|joy28|joy29|joy30|joy31|joy32|joyx|joyy|joyz|joyr|joyu|joyv|joypov|joyname|joybuttons|joyaxes|joyinfo|space|tab|enter|escape|esc|backspace|bs|delete|del|insert|ins|pgup|pgdn|home|end|up|down|left|right|printscreen|ctrlbreak|pause|scrolllock|capslock|numlock|numpad0|numpad1|numpad2|numpad3|numpad4|numpad5|numpad6|numpad7|numpad8|numpad9|numpadmult|numpadadd|numpadsub|numpaddiv|numpaddot|numpaddel|numpadins|numpadclear|numpadup|numpaddown|numpadleft|numpadright|numpadhome|numpadend|numpadpgup|numpadpgdn|numpadenter|f1|f2|f3|f4|f5|f6|f7|f8|f9|f10|f11|f12|f13|f14|f15|f16|f17|f18|f19|f20|f21|f22|f23|f24|browser_back|browser_forward|browser_refresh|browser_stop|browser_search|browser_favorites|browser_home|volume_mute|volume_down|volume_up|media_next|media_prev|media_stop|media_play_pause|launch_mail|launch_media|launch_app1|launch_app2|vk\\d+|sc\\d+)\\b", + "match": "\\b(?!MsgBox)(?i:new|__New|__Delete|shift|lshift|rshift|alt|lalt|ralt|control|lcontrol|rcontrol|ctrl|lctrl|rctrl|lwin|rwin|appskey|altdown|altup|shiftdown|shiftup|ctrldown|ctrlup|lwindown|lwinup|rwindown|rwinup|lbutton|rbutton|mbutton|wheelup|wheelleft|wheelright|wheeldown|xbutton1|xbutton2|joy1|joy2|joy3|joy4|joy5|joy6|joy7|joy8|joy9|joy10|joy11|joy12|joy13|joy14|joy15|joy16|joy17|joy18|joy19|joy20|joy21|joy22|joy23|joy24|joy25|joy26|joy27|joy28|joy29|joy30|joy31|joy32|joyx|joyy|joyz|joyr|joyu|joyv|joypov|joyname|joybuttons|joyaxes|joyinfo|space|tab|enter|escape|esc|backspace|bs|delete|del|insert|ins|pgup|pgdn|home|end|up|down|left|right|printscreen|ctrlbreak|pause|scrolllock|capslock|numlock|numpad0|numpad1|numpad2|numpad3|numpad4|numpad5|numpad6|numpad7|numpad8|numpad9|numpadmult|numpadadd|numpadsub|numpaddiv|numpaddot|numpaddel|numpadins|numpadclear|numpadup|numpaddown|numpadleft|numpadright|numpadhome|numpadend|numpadpgup|numpadpgdn|numpadenter|f1|f2|f3|f4|f5|f6|f7|f8|f9|f10|f11|f12|f13|f14|f15|f16|f17|f18|f19|f20|f21|f22|f23|f24|browser_back|browser_forward|browser_refresh|browser_stop|browser_search|browser_favorites|browser_home|volume_mute|volume_down|volume_up|media_next|media_prev|media_stop|media_play_pause|launch_mail|launch_media|launch_app1|launch_app2|vk\\d+|sc\\d+)\\b(?!\\s*\\()\\b", "name": "keyword.keys.ahk", "example": "LButton" }, @@ -279,7 +279,7 @@ "name": "support.function.ahk" }, { - "match": "\\b(?!MsgBox)([\\w]+)(?=\\()", + "match": "\\b(?!MsgBox)[\\w]+\\s*(?=\\()", "name": "entity.name.function.ahk", "example": "fun(" },