@@ -35,7 +35,7 @@ export function activate(context: vscode.ExtensionContext) {
3535
3636 // Save file before running
3737 editor . document . save ( ) . then ( ( ) => {
38- let terminal = vscode . window . terminals . find ( t => t . name === 'ProXPL' ) ;
38+ let terminal = vscode . window . terminals . find ( t => t . name === 'ProXPL' ) ;
3939 if ( ! terminal ) {
4040 terminal = vscode . window . createTerminal ( 'ProXPL' ) ;
4141 }
@@ -66,121 +66,191 @@ export function activate(context: vscode.ExtensionContext) {
6666 });
6767 */
6868 } ) ;
69- } ) ;
7069 } ) ;
7170 } ) ;
72- context . subscriptions . push ( runCommand ) ;
73-
74- // 2. Watch Mode
75- let isWatchMode = false ;
76- let watchStatusBarItem = vscode . window . createStatusBarItem ( vscode . StatusBarAlignment . Left , 100 ) ;
77- watchStatusBarItem . command = 'proxpl.startWatchMode' ;
78- context . subscriptions . push ( watchStatusBarItem ) ;
79-
80- const updateStatusBar = ( ) => {
81- if ( isWatchMode ) {
82- watchStatusBarItem . text = '$(eye) ProXPL Watch: ON' ;
83- watchStatusBarItem . tooltip = 'Stop Watching ProXPL Files' ;
84- watchStatusBarItem . backgroundColor = new vscode . ThemeColor ( 'statusBarItem.warningBackground' ) ;
85- } else {
86- watchStatusBarItem . text = '$(eye-closed) ProXPL Watch: OFF' ;
87- watchStatusBarItem . tooltip = 'Start Watching ProXPL Files' ;
88- watchStatusBarItem . backgroundColor = undefined ;
89- }
90- watchStatusBarItem . show ( ) ;
91- } ;
92- updateStatusBar ( ) ;
71+ } ) ;
72+ context . subscriptions . push ( runCommand ) ;
9373
94- let watchCommand = vscode . commands . registerCommand ( 'proxpl.startWatchMode' , ( ) => {
95- isWatchMode = ! isWatchMode ;
96- updateStatusBar ( ) ;
97- vscode . window . showInformationMessage ( isWatchMode ? 'ProXPL Watch Mode Started' : 'ProXPL Watch Mode Stopped' ) ;
98- } ) ;
99- context . subscriptions . push ( watchCommand ) ;
74+ // 2. Watch Mode
75+ let isWatchMode = false ;
76+ let watchStatusBarItem = vscode . window . createStatusBarItem ( vscode . StatusBarAlignment . Left , 100 ) ;
77+ watchStatusBarItem . command = 'proxpl.startWatchMode' ;
78+ context . subscriptions . push ( watchStatusBarItem ) ;
10079
101- vscode . workspace . onDidSaveTextDocument ( ( document ) => {
102- if ( isWatchMode && ( document . fileName . endsWith ( '.prox' ) || document . fileName . endsWith ( '.pxpl' ) ) ) {
103- const terminalName = 'ProXPL Debugger' ;
104- let terminal = vscode . window . terminals . find ( t => t . name === terminalName ) ;
80+ const updateStatusBar = ( ) => {
81+ if ( isWatchMode ) {
82+ watchStatusBarItem . text = '$(eye) ProXPL Watch: ON' ;
83+ watchStatusBarItem . tooltip = 'Stop Watching ProXPL Files' ;
84+ watchStatusBarItem . backgroundColor = new vscode . ThemeColor ( 'statusBarItem.warningBackground' ) ;
85+ } else {
86+ watchStatusBarItem . text = '$(eye-closed) ProXPL Watch: OFF' ;
87+ watchStatusBarItem . tooltip = 'Start Watching ProXPL Files' ;
88+ watchStatusBarItem . backgroundColor = undefined ;
89+ }
90+ watchStatusBarItem . show ( ) ;
91+ } ;
92+ updateStatusBar ( ) ;
10593
106- if ( ! terminal ) {
107- terminal = vscode . window . createTerminal ( terminalName ) ;
108- }
94+ let watchCommand = vscode . commands . registerCommand ( 'proxpl.startWatchMode' , ( ) => {
95+ isWatchMode = ! isWatchMode ;
96+ updateStatusBar ( ) ;
97+ vscode . window . showInformationMessage ( isWatchMode ? 'ProXPL Watch Mode Started' : 'ProXPL Watch Mode Stopped' ) ;
98+ } ) ;
99+ context . subscriptions . push ( watchCommand ) ;
100+
101+ vscode . workspace . onDidSaveTextDocument ( ( document ) => {
102+ if ( isWatchMode && ( document . fileName . endsWith ( '.prox' ) || document . fileName . endsWith ( '.pxpl' ) ) ) {
103+ const terminalName = 'ProXPL Debugger' ;
104+ let terminal = vscode . window . terminals . find ( t => t . name === terminalName ) ;
109105
110- terminal . show ( ) ; // Focus the terminal as requested
111- // Clear previous output
112- vscode . commands . executeCommand ( 'workbench.action.terminal.clear' ) ;
113- terminal . sendText ( `proxpl "${ document . fileName } "` ) ;
106+ if ( ! terminal ) {
107+ terminal = vscode . window . createTerminal ( terminalName ) ;
114108 }
115- } ) ;
116109
117- // 3. Formatter Provider
118- const formattingProvider = vscode . languages . registerDocumentFormattingEditProvider ( 'proxpl' , {
119- provideDocumentFormattingEdits ( document : vscode . TextDocument ) : vscode . TextEdit [ ] {
120- const edits : vscode . TextEdit [ ] = [ ] ;
121- let lastLineWasEmpty = false ;
122-
123- for ( let i = 0 ; i < document . lineCount ; i ++ ) {
124- const line = document . lineAt ( i ) ;
125- const text = line . text ;
126-
127- // 1. Remove extra empty lines (consecutive empty lines)
128- if ( text . trim ( ) === '' ) {
129- if ( lastLineWasEmpty ) {
130- // Delete this extra empty line
131- edits . push ( vscode . TextEdit . delete ( line . rangeIncludingLineBreak ) ) ;
132- continue ;
133- }
134- lastLineWasEmpty = true ;
135- } else {
136- lastLineWasEmpty = false ;
137- }
110+ terminal . show ( ) ; // Focus the terminal as requested
111+ // Clear previous output
112+ vscode . commands . executeCommand ( 'workbench.action.terminal.clear' ) ;
113+ terminal . sendText ( `proxpl "${ document . fileName } "` ) ;
114+ }
115+ } ) ;
116+
117+ // 3. Formatter Provider
118+ const formattingProvider = vscode . languages . registerDocumentFormattingEditProvider ( 'proxpl' , {
119+ provideDocumentFormattingEdits ( document : vscode . TextDocument ) : vscode . TextEdit [ ] {
120+ const edits : vscode . TextEdit [ ] = [ ] ;
121+ let lastLineWasEmpty = false ;
122+
123+ for ( let i = 0 ; i < document . lineCount ; i ++ ) {
124+ const line = document . lineAt ( i ) ;
125+ const text = line . text ;
138126
139- // 2. Remove trailing whitespace
140- if ( text . endsWith ( ' ' ) || text . endsWith ( '\t' ) ) {
141- edits . push ( vscode . TextEdit . delete ( new vscode . Range ( i , text . trimEnd ( ) . length , i , text . length ) ) ) ;
127+ // 1. Remove extra empty lines (consecutive empty lines)
128+ if ( text . trim ( ) === '' ) {
129+ if ( lastLineWasEmpty ) {
130+ // Delete this extra empty line
131+ edits . push ( vscode . TextEdit . delete ( line . rangeIncludingLineBreak ) ) ;
132+ continue ;
142133 }
134+ lastLineWasEmpty = true ;
135+ } else {
136+ lastLineWasEmpty = false ;
137+ }
143138
144- // 3. Basic Indentation (Fix to 4 spaces)
145- const indentMatch = text . match ( / ^ ( \s + ) / ) ;
146- if ( indentMatch ) {
147- const oldIndent = indentMatch [ 1 ] ;
148- const newIndent = oldIndent . replace ( / \t / g, ' ' ) ;
149- if ( oldIndent !== newIndent ) {
150- edits . push ( vscode . TextEdit . replace ( new vscode . Range ( i , 0 , i , oldIndent . length ) , newIndent ) ) ;
151- }
139+ // 2. Remove trailing whitespace
140+ if ( text . endsWith ( ' ' ) || text . endsWith ( '\t' ) ) {
141+ edits . push ( vscode . TextEdit . delete ( new vscode . Range ( i , text . trimEnd ( ) . length , i , text . length ) ) ) ;
142+ }
143+
144+ // 3. Basic Indentation (Fix to 4 spaces)
145+ const indentMatch = text . match ( / ^ ( \s + ) / ) ;
146+ if ( indentMatch ) {
147+ const oldIndent = indentMatch [ 1 ] ;
148+ const newIndent = oldIndent . replace ( / \t / g, ' ' ) ;
149+ if ( oldIndent !== newIndent ) {
150+ edits . push ( vscode . TextEdit . replace ( new vscode . Range ( i , 0 , i , oldIndent . length ) , newIndent ) ) ;
152151 }
153152 }
154- return edits ;
155153 }
156- } ) ;
157- context . subscriptions . push ( formattingProvider ) ;
158-
159- // 4. Hover Support
160- const hoverProvider = vscode . languages . registerHoverProvider ( 'proxpl' , {
161- provideHover ( document : vscode . TextDocument , position : vscode . Position ) {
162- const range = document . getWordRangeAtPosition ( position ) ;
163- if ( ! range ) return null ;
164- const word = document . getText ( range ) ;
165-
166- const descriptions : { [ key : string ] : string } = {
167- 'func' : 'Defines a new function in ProXPL. Syntax: `func name(params) { ... }`' ,
168- 'var' : 'Declares a new variable. ProXPL is dynamically typed but variables must be declared.' ,
169- 'if' : 'Conditional statement. Executes a block if the condition is true.' ,
170- 'else' : 'Defines an alternative block for an `if` statement.' ,
171- 'while' : 'Loop that continues as long as a condition is true.' ,
172- 'return' : 'Exits a function and optionally returns a value.' ,
173- 'print' : 'Built-in function to output values to the terminal.' ,
174- 'import' : 'Incorporates external modules into the current script.'
175- } ;
176-
177- if ( descriptions [ word ] ) {
178- return new vscode . Hover ( new vscode . MarkdownString ( descriptions [ word ] ) ) ;
179- }
180- return null ;
154+ return edits ;
155+ }
156+ } ) ;
157+ context . subscriptions . push ( formattingProvider ) ;
158+
159+ // 4. Hover Support
160+ const hoverProvider = vscode . languages . registerHoverProvider ( 'proxpl' , {
161+ provideHover ( document : vscode . TextDocument , position : vscode . Position ) {
162+ const range = document . getWordRangeAtPosition ( position ) ;
163+ if ( ! range ) return null ;
164+ const word = document . getText ( range ) ;
165+
166+ const descriptions : { [ key : string ] : string } = {
167+ 'func' : 'Defines a new function in ProXPL. Syntax: `func name(params) { ... }`' ,
168+ 'var' : 'Declares a new variable. ProXPL is dynamically typed but variables must be declared (using `let` or `const` preferred).' ,
169+ 'let' : 'Declares a mutable variable.' ,
170+ 'const' : 'Declares an immutable constant.' ,
171+ 'if' : 'Conditional statement. Executes a block if the condition is true.' ,
172+ 'else' : 'Defines an alternative block for an `if` statement.' ,
173+ 'while' : 'Loop that continues as long as a condition is true.' ,
174+ 'for' : 'Loop with initializer, condition, and increment.' ,
175+ 'return' : 'Exits a function and optionally returns a value.' ,
176+ 'print' : 'Built-in statement/function to output values to the terminal.' ,
177+ 'import' : 'Incorporates external modules into the current script.' ,
178+ 'class' : 'Defines a new class with methods and properties.' ,
179+ 'this' : 'Refers to the current instance of the class.' ,
180+ 'super' : 'Refers to the superclass.' ,
181+ 'true' : 'Boolean true literal.' ,
182+ 'false' : 'Boolean false literal.' ,
183+ 'null' : 'Represents the absence of value.' ,
184+ 'len' : 'Built-in function. Returns the length of a string or list.' ,
185+ 'str' : 'Built-in function. Converts a value to a string.' ,
186+ 'clock' : 'Built-in function. Returns the current time in seconds.' ,
187+ 'input' : 'Built-in function. Reads a line of input from stdin.' ,
188+ 'type' : 'Built-in function. Returns the type of a value.' ,
189+ 'try' : 'Starts a block of code to test for errors.' ,
190+ 'catch' : 'Handles errors thrown in the try block.' ,
191+ 'throw' : 'Throws an error/exception.'
192+ } ;
193+
194+ if ( descriptions [ word ] ) {
195+ return new vscode . Hover ( new vscode . MarkdownString ( descriptions [ word ] ) ) ;
181196 }
182- } ) ;
183- context . subscriptions . push ( hoverProvider ) ;
197+ return null ;
198+ }
199+ } ) ;
200+ context . subscriptions . push ( hoverProvider ) ;
201+
202+ // 5. Definition Provider (Basic "Go to Definition")
203+ const definitionProvider = vscode . languages . registerDefinitionProvider ( 'proxpl' , {
204+ provideDefinition ( document : vscode . TextDocument , position : vscode . Position ) {
205+ const range = document . getWordRangeAtPosition ( position ) ;
206+ if ( ! range ) return null ;
207+ const word = document . getText ( range ) ;
208+
209+ const text = document . getText ( ) ;
210+ // Regex to find 'func word' or 'class word'
211+ const funcRegex = new RegExp ( `func\\s+${ word } \\s*\\(` , 'g' ) ;
212+ const classRegex = new RegExp ( `class\\s+${ word } \\s*\\{` , 'g' ) ;
213+
214+ const results : vscode . Location [ ] = [ ] ;
215+
216+ let match ;
217+ while ( ( match = funcRegex . exec ( text ) ) !== null ) {
218+ const pos = document . positionAt ( match . index ) ;
219+ results . push ( new vscode . Location ( document . uri , new vscode . Range ( pos , pos ) ) ) ;
220+ }
221+ while ( ( match = classRegex . exec ( text ) ) !== null ) {
222+ const pos = document . positionAt ( match . index ) ;
223+ results . push ( new vscode . Location ( document . uri , new vscode . Range ( pos , pos ) ) ) ;
224+ }
225+
226+ return results ;
227+ }
228+ } ) ;
229+ context . subscriptions . push ( definitionProvider ) ;
230+
231+ // 6. Completion Item Provider
232+ const completionProvider = vscode . languages . registerCompletionItemProvider ( 'proxpl' , {
233+ provideCompletionItems ( document : vscode . TextDocument , position : vscode . Position ) {
234+ const keywords = [
235+ 'func' , 'class' , 'if' , 'else' , 'while' , 'for' , 'return' , 'print' ,
236+ 'var' , 'let' , 'const' , 'true' , 'false' , 'null' , 'import' , 'export' ,
237+ 'from' , 'as' , 'try' , 'catch' , 'throw' , 'async' , 'await'
238+ ] ;
239+ const builtins = [ 'len' , 'str' , 'clock' , 'input' , 'type' ] ;
240+
241+ const items : vscode . CompletionItem [ ] = [ ] ;
242+
243+ keywords . forEach ( word => {
244+ items . push ( new vscode . CompletionItem ( word , vscode . CompletionItemKind . Keyword ) ) ;
245+ } ) ;
246+ builtins . forEach ( word => {
247+ items . push ( new vscode . CompletionItem ( word , vscode . CompletionItemKind . Function ) ) ;
248+ } ) ;
249+
250+ return items ;
251+ }
252+ } ) ;
253+ context . subscriptions . push ( completionProvider ) ;
184254}
185255
186256function mapLineNumber ( lineStr : string ) : number {
0 commit comments