Eric Streit il y a 3 semaines
commit
75f5e8e12b

+ 5 - 0
README.md

@@ -0,0 +1,5 @@
+# ADW Modula-2 editor plugin
+
+This plugin originally was meant to add syntax highlighting.
+
+I added snippets and auto-upcasing.

+ 2 - 0
[Content_Types].xml

@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension=".bnf" ContentType="application/octet-stream"/><Default Extension=".cmd" ContentType="application/octet-stream"/><Default Extension=".js" ContentType="application/javascript"/><Default Extension=".json" ContentType="application/json"/><Default Extension=".md" ContentType="text/markdown"/><Default Extension=".png" ContentType="image/png"/><Default Extension=".sh" ContentType="application/x-sh"/><Default Extension=".txt" ContentType="text/plain"/><Default Extension=".vsixmanifest" ContentType="text/xml"/><Default Extension=".yml" ContentType="text/yaml"/></Types>

+ 47 - 0
extension.vsixmanifest

@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+	<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
+		<Metadata>
+			<Identity Language="en-US" Id="adw-modula2-lsp" Version="0.1.0" Publisher="catink123" />
+			<DisplayName>ADW Modula-2</DisplayName>
+			<Description xml:space="preserve">Language Server and Syntax Support for ADW Modula-2</Description>
+			<Tags>modula2,Modula-2,__ext_mod,__ext_def</Tags>
+			<Categories>Programming Languages</Categories>
+			<GalleryFlags>Public</GalleryFlags>
+			
+			<Properties>
+				<Property Id="Microsoft.VisualStudio.Code.Engine" Value="^1.107.0" />
+				<Property Id="Microsoft.VisualStudio.Code.ExtensionDependencies" Value="" />
+				<Property Id="Microsoft.VisualStudio.Code.ExtensionPack" Value="" />
+				<Property Id="Microsoft.VisualStudio.Code.ExtensionKind" Value="workspace" />
+				<Property Id="Microsoft.VisualStudio.Code.LocalizedLanguages" Value="" />
+				<Property Id="Microsoft.VisualStudio.Code.EnabledApiProposals" Value="" />
+				
+				<Property Id="Microsoft.VisualStudio.Code.ExecutesCode" Value="true" />
+				
+				<Property Id="Microsoft.VisualStudio.Services.Links.Source" Value="https://github.com/catink123/adw-modula2-lsp.git" />
+				<Property Id="Microsoft.VisualStudio.Services.Links.Getstarted" Value="https://github.com/catink123/adw-modula2-lsp.git" />
+				<Property Id="Microsoft.VisualStudio.Services.Links.GitHub" Value="https://github.com/catink123/adw-modula2-lsp.git" />
+				<Property Id="Microsoft.VisualStudio.Services.Links.Support" Value="https://github.com/catink123/adw-modula2-lsp/issues" />
+				<Property Id="Microsoft.VisualStudio.Services.Links.Learn" Value="https://github.com/catink123/adw-modula2-lsp#readme" />
+				
+				
+				<Property Id="Microsoft.VisualStudio.Services.GitHubFlavoredMarkdown" Value="true" />
+				<Property Id="Microsoft.VisualStudio.Services.Content.Pricing" Value="Free"/>
+
+				
+				
+			</Properties>
+			<License>extension/LICENSE.txt</License>
+			<Icon>extension/logo.png</Icon>
+		</Metadata>
+		<Installation>
+			<InstallationTarget Id="Microsoft.VisualStudio.Code"/>
+		</Installation>
+		<Dependencies/>
+		<Assets>
+			<Asset Type="Microsoft.VisualStudio.Code.Manifest" Path="extension/package.json" Addressable="true" />
+			<Asset Type="Microsoft.VisualStudio.Services.Content.Details" Path="extension/readme.md" Addressable="true" />
+<Asset Type="Microsoft.VisualStudio.Services.Content.License" Path="extension/LICENSE.txt" Addressable="true" />
+<Asset Type="Microsoft.VisualStudio.Services.Icons.Default" Path="extension/logo.png" Addressable="true" />
+		</Assets>
+	</PackageManifest>

+ 30 - 0
extension/.gitignore

@@ -0,0 +1,30 @@
+# --- Dependency Folders ---
+node_modules/
+client/node_modules/
+server/node_modules/
+
+# --- Compilation Output ---
+# We ignore the 'out' folders because they contain generated JS.
+# Users should run 'npm run compile' to generate these.
+out/
+client/out/
+server/out/
+
+# --- VS Code Metadata ---
+.vscode-test/
+*.vsix
+
+# --- TypeScript Artifacts ---
+*.tsbuildinfo
+
+# --- OS / IDE Junk ---
+.DS_Store
+Thumbs.db
+.history/
+.ionide/
+
+# --- Log Files ---
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*

+ 7 - 0
extension/AUTO_UPPERCASE.md

@@ -0,0 +1,7 @@
+# Automatic Modula-2 keyword uppercasing
+
+Version 0.1.1 adds client-side keyword uppercasing. It inspects only the token(s) touched by the latest edit and uppercases recognized keywords while leaving nested `(* ... *)` comments, `<* ... *>` pragmas, strings, character literals, and identifiers such as `beginValue` unchanged.
+
+Setting: `adwModula2.autoUppercaseKeywords` (default `true`)
+
+Command: `Modula-2: Uppercase Keywords`

+ 21 - 0
extension/LICENSE.txt

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2026 Matsvei Radzevich
+
+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 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.

+ 58 - 0
extension/language-configuration.json

@@ -0,0 +1,58 @@
+{
+  "comments": {
+    "blockComment": [
+      "(*",
+      "*)"
+    ]
+  },
+  "brackets": [
+    [
+      "(",
+      ")"
+    ],
+    [
+      "[",
+      "]"
+    ]
+  ],
+  "autoClosingPairs": [
+    {
+      "open": "(",
+      "close": ")"
+    },
+    {
+      "open": "[",
+      "close": "]"
+    },
+    {
+      "open": "\"",
+      "close": "\""
+    },
+    {
+      "open": "'",
+      "close": "'"
+    }
+  ],
+  "surroundingPairs": [
+    [
+      "(",
+      ")"
+    ],
+    [
+      "[",
+      "]"
+    ],
+    [
+      "\"",
+      "\""
+    ],
+    [
+      "'",
+      "'"
+    ]
+  ],
+  "indentationRules": {
+    "increaseIndentPattern": "^\\s*(BEGIN|CASE|CLASS|FOR|IF|LOOP|RECORD|REPEAT|WHILE|WITH|TRY|EXCEPT|FINALLY)\\b",
+    "decreaseIndentPattern": "^\\s*(END|ELSE|ELSIF|EXCEPT|FINALLY)\\b"
+  }
+}

BIN
extension/logo.png


+ 80 - 0
extension/package.json

@@ -0,0 +1,80 @@
+{
+  "name": "adw-modula2-lsp",
+  "displayName": "ADW Modula-2 (Auto Uppercase Keywords + Snippets)",
+  "description": "Language Server and Syntax Support for ADW Modula-2",
+  "publisher": "catink123",
+  "repository": "https://github.com/catink123/adw-modula2-lsp",
+  "version": "0.1.3",
+  "icon": "logo.png",
+  "engines": {
+    "vscode": "^1.107.0"
+  },
+  "categories": [
+    "Programming Languages"
+  ],
+  "activationEvents": [
+    "onLanguage:modula2"
+  ],
+  "main": "./out/client/src/extension.js",
+  "contributes": {
+    "languages": [
+      {
+        "id": "modula2",
+        "aliases": [
+          "Modula-2",
+          "modula2"
+        ],
+        "extensions": [
+          ".mod",
+          ".def"
+        ],
+        "configuration": "./language-configuration.json"
+      }
+    ],
+    "grammars": [
+      {
+        "language": "modula2",
+        "scopeName": "source.modula2",
+        "path": "./syntaxes/modula2.tmLanguage.json"
+      }
+    ],
+    "configuration": [
+      {
+        "title": "ADW Modula-2",
+        "properties": {
+          "adwModula2.autoUppercaseKeywords": {
+            "type": "boolean",
+            "default": true,
+            "description": "Automatically uppercase Modula-2 keywords while editing."
+          }
+        }
+      }
+    ],
+    "commands": [
+      {
+        "command": "adwModula2.uppercaseKeywords",
+        "title": "Modula-2: Uppercase Keywords"
+      }
+    ],
+    "snippets": [
+      {
+        "language": "modula2",
+        "path": "./snippets/modula2.json"
+      }
+    ]
+  },
+  "scripts": {
+    "compile": "tsc",
+    "watch": "tsc -w"
+  },
+  "dependencies": {
+    "vscode-languageclient": "^9.0.1",
+    "vscode-languageserver": "^9.0.1",
+    "vscode-languageserver-textdocument": "^1.0.12"
+  },
+  "devDependencies": {
+    "@types/node": "^25.5.2",
+    "@types/vscode": "^1.107.0",
+    "typescript": "^6.0.2"
+  }
+}

+ 63 - 0
extension/readme.md

@@ -0,0 +1,63 @@
+# ADW Modula-2 Language Server (LSP)
+
+A high-performance implementation of the Language Server Protocol for the ADW Modula-2 dialect, featuring full support for Object-Oriented extensions including CLASS, INHERIT, and OVERRIDE.
+
+## Core Functionality
+
+### 1. Semantic Analysis
+* **Contextual Highlighting**: Distinguished rendering for Types, Constants, Variables, and Procedures.
+* **Comment Handling**: Robust parsing of nested comments `(* (* ... *) *)` and complex strings to prevent highlighting bleed.
+
+### 2. Navigation Logic
+* **Go to Definition**: Resolution of local symbols and cross-file navigation to `.def` headers.
+* **Go to Implementation**: Direct mapping from procedure signatures in `.def` files to the corresponding bodies in `.mod` files.
+* **Symbol Lookup**: Comprehensive workspace-wide symbol indexing and search.
+
+### 3. ADW OOP Support
+* **Inheritance Mapping**: Resolution of `INHERIT` chains to provide accurate member completion for base classes.
+* **Override Identification**: Explicit marking of `OVERRIDE` methods within the Document Outline.
+
+### 4. Technical Architecture
+* **Indentation-Aware Scoping**: Deterministic scope resolution for nested procedures and modules based on block indentation.
+* **Asynchronous Indexing**: Background processing of all workspace `.def` and `.mod` files for non-blocking cross-reference resolution.
+
+## Installation
+
+### VSIX Package
+1. Obtain the `adw-modula2-lsp.vsix` file.
+2. In VS Code, navigate to the Extensions view (`Ctrl+Shift+X`).
+3. Select **Install from VSIX...** from the local menu and select the file.
+
+### Source Build
+1. Clone the repository.
+2. Execute `npm install` in the root, `/client`, and `/server` directories.
+3. Execute `npm run compile`.
+4. Launch the Extension Development Host via `F5`.
+
+
+
+## Project Structure
+
+* `client/`: VS Code extension client-side activation and lifecycle management.
+* `server/`: LSP Server implementation containing the Modula-2 parser and symbol engine.
+* `logo.png`: Extension identifier icon.
+
+## License
+
+This project is licensed under the MIT License. See the `LICENSE` file for full text.
+
+## Automatic keyword uppercasing
+
+Version 0.1.1 automatically uppercases Modula-2 keywords as you edit `.mod` and `.def` files. Comments, pragmas, strings, character literals and longer identifiers are protected.
+
+The feature can be disabled with `adwModula2.autoUppercaseKeywords`, or invoked manually with **Modula-2: Uppercase Keywords**.
+
+## Snippets
+
+The extension provides Modula-2 snippets for modules, definition/implementation
+modules, procedures, control flow, declarations, imports, records, pointers,
+and sets. Snippets are available for both `.mod` and `.def` files.
+
+Common prefixes include `module`, `definition`, `implementation`, `proc`,
+`if`, `ifelse`, `for`, `while`, `repeat`, `loop`, `case`, `record`, `type`,
+`var`, `const`, `from`, and `import`.

+ 250 - 0
extension/snippets/modula2.json

@@ -0,0 +1,250 @@
+{
+  "Module": {
+    "prefix": [
+      "module",
+      "MODULE"
+    ],
+    "body": [
+      "MODULE ${1:ModuleName};",
+      "",
+      "FROM ${2:Module} IMPORT ${3:Item};",
+      "",
+      "VAR",
+      "    ${4:variable} : ${5:INTEGER};",
+      "",
+      "BEGIN",
+      "    ${0}",
+      "END ${1:ModuleName}."
+    ],
+    "description": "Create a complete Modula-2 module"
+  },
+  "Definition Module": {
+    "prefix": [
+      "definition",
+      "defmodule",
+      "DEFINITION"
+    ],
+    "body": [
+      "DEFINITION MODULE ${1:ModuleName};",
+      "",
+      "EXPORTS",
+      "    ${0}",
+      "",
+      "END ${1:ModuleName}."
+    ],
+    "description": "Create a definition module"
+  },
+  "Implementation Module": {
+    "prefix": [
+      "implementation",
+      "implmodule",
+      "IMPLEMENTATION"
+    ],
+    "body": [
+      "IMPLEMENTATION MODULE ${1:ModuleName};",
+      "",
+      "FROM ${2:Module} IMPORT ${3:Item};",
+      "",
+      "BEGIN",
+      "    ${0}",
+      "END ${1:ModuleName}."
+    ],
+    "description": "Create an implementation module"
+  },
+  "Procedure": {
+    "prefix": [
+      "proc",
+      "procedure",
+      "PROCEDURE"
+    ],
+    "body": [
+      "PROCEDURE ${1:ProcedureName}(${2:parameters}) : ${3:RETURN_TYPE};",
+      "BEGIN",
+      "    ${0}",
+      "END ${1:ProcedureName};"
+    ],
+    "description": "Create a procedure"
+  },
+  "If": {
+    "prefix": [
+      "if",
+      "IF"
+    ],
+    "body": [
+      "IF ${1:condition} THEN",
+      "    ${0}",
+      "END;"
+    ],
+    "description": "Create an IF statement"
+  },
+  "If Else": {
+    "prefix": [
+      "ifelse",
+      "IFELSE"
+    ],
+    "body": [
+      "IF ${1:condition} THEN",
+      "    ${2}",
+      "ELSE",
+      "    ${0}",
+      "END;"
+    ],
+    "description": "Create an IF/ELSE statement"
+  },
+  "For": {
+    "prefix": [
+      "for",
+      "FOR"
+    ],
+    "body": [
+      "FOR ${1:i} := ${2:1} TO ${3:10} DO",
+      "    ${0}",
+      "END;"
+    ],
+    "description": "Create a FOR loop"
+  },
+  "While": {
+    "prefix": [
+      "while",
+      "WHILE"
+    ],
+    "body": [
+      "WHILE ${1:condition} DO",
+      "    ${0}",
+      "END;"
+    ],
+    "description": "Create a WHILE loop"
+  },
+  "Repeat": {
+    "prefix": [
+      "repeat",
+      "REPEAT"
+    ],
+    "body": [
+      "REPEAT",
+      "    ${0}",
+      "UNTIL ${1:condition};"
+    ],
+    "description": "Create a REPEAT/UNTIL loop"
+  },
+  "Loop": {
+    "prefix": [
+      "loop",
+      "LOOP"
+    ],
+    "body": [
+      "LOOP",
+      "    ${0}",
+      "END;"
+    ],
+    "description": "Create an unconditional LOOP"
+  },
+  "Case": {
+    "prefix": [
+      "case",
+      "CASE"
+    ],
+    "body": [
+      "CASE ${1:expression} OF",
+      "    ${2:value}: ${3}",
+      "ELSE",
+      "    ${0}",
+      "END;"
+    ],
+    "description": "Create a CASE statement"
+  },
+  "Record": {
+    "prefix": [
+      "record",
+      "RECORD"
+    ],
+    "body": [
+      "${1:RecordName} = RECORD",
+      "    ${2:field} : ${3:INTEGER};",
+      "    ${0}",
+      "END;"
+    ],
+    "description": "Create a RECORD type"
+  },
+  "Type": {
+    "prefix": [
+      "type",
+      "TYPE"
+    ],
+    "body": [
+      "TYPE",
+      "    ${1:TypeName} = ${0};"
+    ],
+    "description": "Create a TYPE declaration"
+  },
+  "Variable": {
+    "prefix": [
+      "var",
+      "variable",
+      "VAR"
+    ],
+    "body": [
+      "VAR",
+      "    ${1:name} : ${2:INTEGER};"
+    ],
+    "description": "Create a VAR declaration"
+  },
+  "Constant": {
+    "prefix": [
+      "const",
+      "constant",
+      "CONST"
+    ],
+    "body": [
+      "CONST",
+      "    ${1:Name} = ${0};"
+    ],
+    "description": "Create a CONST declaration"
+  },
+  "From Import": {
+    "prefix": [
+      "from",
+      "import",
+      "fromimport",
+      "FROM"
+    ],
+    "body": [
+      "FROM ${1:Module} IMPORT ${0};"
+    ],
+    "description": "Create a FROM/IMPORT statement"
+  },
+  "Import": {
+    "prefix": [
+      "import",
+      "IMPORT"
+    ],
+    "body": [
+      "IMPORT ${0};"
+    ],
+    "description": "Create an IMPORT statement"
+  },
+  "Pointer Type": {
+    "prefix": [
+      "pointer",
+      "ptr",
+      "POINTER"
+    ],
+    "body": [
+      "TYPE",
+      "    ${1:TypeName} = POINTER TO ${0};"
+    ],
+    "description": "Create a pointer type"
+  },
+  "Set Type": {
+    "prefix": [
+      "set",
+      "settype",
+      "SET"
+    ],
+    "body": [
+      "TYPE",
+      "    ${1:SetName} = SET OF ${0};"
+    ],
+    "description": "Create a SET type"
+  }
+}

+ 58 - 0
extension/syntaxes/modula2.tmLanguage.json

@@ -0,0 +1,58 @@
+{
+  "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
+  "name": "ADW Modula-2",
+  "patterns": [
+    {
+      "name": "comment.block.modula2",
+      "begin": "\\(\\*",
+      "end": "\\*\\)"
+    },
+    {
+      "name": "string.quoted.single.modula2",
+      "begin": "'",
+      "end": "'"
+    },
+    {
+      "name": "string.quoted.double.modula2",
+      "begin": "\"",
+      "end": "\"(A|U)?"
+    },
+    {
+      "name": "keyword.control.modula2",
+      "match": "\\b(AND|ARRAY|BEGIN|BY|CASE|CONST|DEFINITION|DIV|DO|ELSE|ELSIF|END|EXCEPT|EXIT|EXPORT|FINALLY|FOR|FORWARD|FROM|IF|IMPLEMENTATION|IMPORT|IN|LOOP|MOD|MODULE|NOT|OF|OR|PACKEDSET|POINTER|PROCEDURE|QUALIFIED|RECORD|REPEAT|REM|RETRY|RETURN|SET|THEN|TO|TYPE|UNTIL|VAR|WHILE|WITH|GENERIC)\\b"
+    },
+    {
+      "name": "keyword.other.oo.modula2",
+      "match": "\\b(AS|ABSTRACT|CLASS|GUARD|INHERIT|OVERRIDE|READONLY|REVEAL|TRACED|UNSAFEGUARDED)\\b"
+    },
+    {
+      "name": "keyword.operator.extended.modula2",
+      "match": "\\b(BAND|BOR|BXOR|BNOT|SHL|SHR|SAR|ROL|ROR|BREAK|CONTINUE|ASM|ASSEMBLER|PUREASM|EXPORTS|MACRO|INOUT|OUT|VALUE|NOHIGH|NEAR|FAR)\\b"
+    },
+    {
+      "name": "support.type.modula2",
+      "match": "\\b(INTEGER|CARDINAL|REAL|LONGREAL|COMPLEX|LONGCOMPLEX|BOOLEAN|CHAR|BITSET|SHORTINT|INTEGER8|SHORTCARD|CARDINAL8|INTEGER16|CARDINAL16|INTEGER32|CARDINAL32|LONGINT|INTEGER64|LONGCARD|CARDINAL64|ACHAR|UCHAR|BYTEBOOL|BOOL8|WORDBOOL|BOOL16|DWORDBOOL|BOOL32|BITSET16|BITSET32|LOC|BYTE|WORD|DWORD|MACHINEWORD|ADDRESS|ADRCARD|ADRINT)\\b"
+    },
+    {
+      "name": "keyword.control.directive.modula2",
+      "match": "(%IF|%THEN|%ELSIF|%ELSE|%END|%AND|%OR|%NOT)"
+    },
+    {
+      "name": "constant.numeric.hex.modula2",
+      "match": "\\b[0-9][0-9A-Fa-f]*H\\b"
+    },
+    {
+      "name": "constant.numeric.octal.modula2",
+      "match": "\\b[0-7]+(B|C)\\b"
+    },
+    {
+      "name": "constant.numeric.float.modula2",
+      "match": "\\b[0-9]+\\.[0-9]*(E[+-]?[0-9]+)?\\b"
+    },
+    {
+      "name": "constant.numeric.decimal.modula2",
+      "match": "\\b[0-9]+\\b"
+    }
+  ],
+  "scopeName": "source.modula2"
+}

+ 12 - 0
extension/test-auto-uppercase.js

@@ -0,0 +1,12 @@
+const assert = require('assert');
+const fs = require('fs');
+const src = fs.readFileSync(__dirname + '/out/client/src/extension.js', 'utf8');
+assert(src.includes('MODULA2_KEYWORDS'));
+assert(src.includes("get('autoUppercaseKeywords', true)"));
+assert(src.includes("registerCommand('adwModula2.uppercaseKeywords'"));
+assert(src.includes("languageId !== 'modula2'"));
+console.log('auto-uppercase integration checks passed');
+
+assert(src.includes('event.contentChanges.map'));
+assert(src.includes('change.rangeOffset'));
+console.log('incremental auto-uppercase checks passed');