├── README.md ├── client ├── .gitignore ├── harbourIcon.png ├── .vscodeignore ├── .vscode │ ├── tasks.json │ ├── settings.json │ └── launch.json ├── copy.js ├── webpack.config.js ├── syntaxes │ └── language-configuration.json ├── harbourIcon.svg ├── LICENSE.txt ├── harbour.code-snippets ├── src │ ├── utils.js │ ├── docCreator.js │ ├── decorator.js │ ├── myLocalize.js │ ├── debugProvider.js │ ├── validation.js │ ├── extension.js │ ├── formatEditor.js │ ├── taskProvider.js │ └── debugger.js ├── formatter-settings │ ├── style.css │ └── code.js ├── debugger.md ├── README.md ├── package.nls.json ├── package.nls.es.json ├── package.nls.it.json ├── CHANGELOG.md └── package.json ├── CHANGELOG.md ├── server ├── .gitignore ├── .vscodeignore ├── copy.sh ├── copy.bat ├── .vscode │ ├── tasks.json │ ├── settings.json │ └── launch.json ├── webpack.config.js ├── package.json ├── test_parse.js └── parseHBDoc.js ├── images ├── 0_9_6.png ├── 0_9_6.xcf ├── command.png ├── dates.png ├── forDec.png ├── ifdec.png ├── outline.png ├── strings.png ├── breadcump.png ├── comments.PNG ├── condBreak.png ├── inlineDoc.png ├── newConf1.png ├── newConf2.png ├── symbolDir.png ├── symbolDoc.png ├── validating.prg ├── whileDec.png ├── attachSample.mp4 ├── debuggerAdd.png ├── debuggerAdd2.png ├── debuggerCode.png ├── parenthesis.png ├── validating.png ├── copyExpression.png ├── gotodefinition.png ├── valdatingSetup.png ├── conditionalBreaks.gif ├── copyExpression2.png ├── automaticHarbourDoc.gif ├── attachSample.html ├── dates.prg ├── parenthesis.prg └── strings.prg ├── test ├── minimal.prg ├── err.prg ├── errors.prg ├── .vscode │ ├── settings.json │ ├── launch.json │ └── tasks.json ├── setup32.bat ├── mini_debugger.js ├── mybuild.bat ├── db1.prg ├── build_libX.bat ├── myBuildX.bat ├── debugger.md ├── dbg_ex.prg ├── class_c.prg ├── dbg_test.prg └── builds.prg └── .gitignore /README.md: -------------------------------------------------------------------------------- 1 | client/README.md -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | server 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | client/CHANGELOG.md -------------------------------------------------------------------------------- /server/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /images/0_9_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/APerricone/harbourCodeExtension/HEAD/images/0_9_6.png -------------------------------------------------------------------------------- /images/0_9_6.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/APerricone/harbourCodeExtension/HEAD/images/0_9_6.xcf -------------------------------------------------------------------------------- /images/command.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/APerricone/harbourCodeExtension/HEAD/images/command.png -------------------------------------------------------------------------------- /images/dates.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/APerricone/harbourCodeExtension/HEAD/images/dates.png -------------------------------------------------------------------------------- /images/forDec.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/APerricone/harbourCodeExtension/HEAD/images/forDec.png -------------------------------------------------------------------------------- /images/ifdec.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/APerricone/harbourCodeExtension/HEAD/images/ifdec.png -------------------------------------------------------------------------------- /images/outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/APerricone/harbourCodeExtension/HEAD/images/outline.png -------------------------------------------------------------------------------- /images/strings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/APerricone/harbourCodeExtension/HEAD/images/strings.png -------------------------------------------------------------------------------- /test/minimal.prg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/APerricone/harbourCodeExtension/HEAD/test/minimal.prg -------------------------------------------------------------------------------- /images/breadcump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/APerricone/harbourCodeExtension/HEAD/images/breadcump.png -------------------------------------------------------------------------------- /images/comments.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/APerricone/harbourCodeExtension/HEAD/images/comments.PNG -------------------------------------------------------------------------------- /images/condBreak.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/APerricone/harbourCodeExtension/HEAD/images/condBreak.png -------------------------------------------------------------------------------- /images/inlineDoc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/APerricone/harbourCodeExtension/HEAD/images/inlineDoc.png -------------------------------------------------------------------------------- /images/newConf1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/APerricone/harbourCodeExtension/HEAD/images/newConf1.png -------------------------------------------------------------------------------- /images/newConf2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/APerricone/harbourCodeExtension/HEAD/images/newConf2.png -------------------------------------------------------------------------------- /images/symbolDir.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/APerricone/harbourCodeExtension/HEAD/images/symbolDir.png -------------------------------------------------------------------------------- /images/symbolDoc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/APerricone/harbourCodeExtension/HEAD/images/symbolDoc.png -------------------------------------------------------------------------------- /images/validating.prg: -------------------------------------------------------------------------------- 1 | proc main() 2 | local unused 3 | unknown := 5 4 | error := ((4+5) 5 | 6 | -------------------------------------------------------------------------------- /images/whileDec.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/APerricone/harbourCodeExtension/HEAD/images/whileDec.png -------------------------------------------------------------------------------- /client/harbourIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/APerricone/harbourCodeExtension/HEAD/client/harbourIcon.png -------------------------------------------------------------------------------- /images/attachSample.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/APerricone/harbourCodeExtension/HEAD/images/attachSample.mp4 -------------------------------------------------------------------------------- /images/debuggerAdd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/APerricone/harbourCodeExtension/HEAD/images/debuggerAdd.png -------------------------------------------------------------------------------- /images/debuggerAdd2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/APerricone/harbourCodeExtension/HEAD/images/debuggerAdd2.png -------------------------------------------------------------------------------- /images/debuggerCode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/APerricone/harbourCodeExtension/HEAD/images/debuggerCode.png -------------------------------------------------------------------------------- /images/parenthesis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/APerricone/harbourCodeExtension/HEAD/images/parenthesis.png -------------------------------------------------------------------------------- /images/validating.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/APerricone/harbourCodeExtension/HEAD/images/validating.png -------------------------------------------------------------------------------- /images/copyExpression.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/APerricone/harbourCodeExtension/HEAD/images/copyExpression.png -------------------------------------------------------------------------------- /images/gotodefinition.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/APerricone/harbourCodeExtension/HEAD/images/gotodefinition.png -------------------------------------------------------------------------------- /images/valdatingSetup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/APerricone/harbourCodeExtension/HEAD/images/valdatingSetup.png -------------------------------------------------------------------------------- /images/conditionalBreaks.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/APerricone/harbourCodeExtension/HEAD/images/conditionalBreaks.gif -------------------------------------------------------------------------------- /images/copyExpression2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/APerricone/harbourCodeExtension/HEAD/images/copyExpression2.png -------------------------------------------------------------------------------- /images/automaticHarbourDoc.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/APerricone/harbourCodeExtension/HEAD/images/automaticHarbourDoc.gif -------------------------------------------------------------------------------- /server/.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | test/** 4 | .gitignore 5 | jsconfig.json 6 | vsc-extension-quickstart.md 7 | .eslintrc.json 8 | -------------------------------------------------------------------------------- /images/attachSample.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /test/err.prg: -------------------------------------------------------------------------------- 1 | proc altroFile() 2 | ? a 3 | return 4 | 5 | /* 6 | * without -ge1 7 | err.prg(2) Warning W0001 Ambiguous reference 'A' 8 | 9 | * with -ge1 10 | err.prg:2: Warning W0001 Ambiguous reference 'A' 11 | 12 | */ -------------------------------------------------------------------------------- /server/copy.sh: -------------------------------------------------------------------------------- 1 | npx webpack --mode production 2 | mkdir -p ../client/server/dist 3 | cp -Rvf dist/ ../client/server 4 | cp -vf package.json ../client/server 5 | mkdir -p ../client/extra 6 | # cp -vf ../test/dbg_lib.prg ../client/extra 7 | cd ../client 8 | npx webpack --mode production 9 | -------------------------------------------------------------------------------- /test/errors.prg: -------------------------------------------------------------------------------- 1 | // errors.prg 2 | 3 | static lNoComment 4 | 5 | /********************************** 6 | main func 7 | It is important 8 | **********************************/ 9 | proc main(a,b) 10 | /* test declaration */ 11 | local testDecl:=::soo 12 | return 13 | 14 | main() -------------------------------------------------------------------------------- /images/dates.prg: -------------------------------------------------------------------------------- 1 | proc main() 2 | local dHb := d"2018-11-26" 3 | local tHb := t"2018-11-26 13:25:34.234" 4 | LOCAL tfp := {^ 2018-11-26 13:25:34.234 } 5 | local errorDate := d"2999-13-32" 6 | 7 | #pragma begindump 8 | void prova() 9 | { 10 | // C Code 11 | } 12 | #pragma enddump 13 | 14 | -------------------------------------------------------------------------------- /test/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Inserire le impostazioni in questo file per sovrascrivere quelle utente e predefinite. 2 | { 3 | "files.exclude": { 4 | "**/.git": true, 5 | "**/.svn": true, 6 | "**/.hg": true, 7 | "**/CVS": true, 8 | "**/.DS_Store": true, 9 | "**/.hbmk": true 10 | }, 11 | "harbour.warningLevel": 3 12 | } -------------------------------------------------------------------------------- /server/copy.bat: -------------------------------------------------------------------------------- 1 | call npx webpack --mode production 2 | mkdir ..\client\server 3 | mkdir ..\client\server\dist 4 | copy dist\*.* ..\client\server\dist /Y 5 | copy package.json ..\client\server /Y 6 | mkdir ..\client\extra\ 7 | ::copy ..\test\dbg_lib.prg ..\client\extra\ /Y 8 | cd ..\client 9 | call npx webpack --mode production 10 | -------------------------------------------------------------------------------- /client/.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | debugger.md 3 | *.svg 4 | copy.* 5 | .gitignore 6 | node_modules 7 | !node_modules/@vscode/codicons/dist/codicon.css 8 | !node_modules/@vscode/codicons/dist/codicon.ttf 9 | !node_modules/@yagisumi/win-output-debug-string/build/Release/win_output_debug_string.node 10 | src 11 | webpack.config.js 12 | appunti_formatter 13 | formatter-settings/jquery-3.6.0.slim.js 14 | -------------------------------------------------------------------------------- /images/parenthesis.prg: -------------------------------------------------------------------------------- 1 | proc main() 2 | 3 | 4 | local a := (((5+4)*(2+7))+(7*3)) 5 | 6 | do while a 7 | // ... some stuff with a 8 | // other code 9 | enddo 10 | 11 | if .T. 12 | // true code 13 | else 14 | // false code 15 | endif 16 | 17 | for a:=1 to 10 18 | if .F. 19 | exit 20 | endif 21 | if .T. 22 | loop 23 | endif 24 | // other code 25 | next 26 | -------------------------------------------------------------------------------- /client/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "prelanch", 6 | "type": "npm", 7 | "script": "prelanch", 8 | "problemMatcher": [], 9 | "detail": "webpack --mode development", 10 | "group": { 11 | "kind": "build", 12 | "isDefault": true 13 | } 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /images/strings.prg: -------------------------------------------------------------------------------- 1 | #include 2 | proc main() 3 | LOCAL cString1 := "string1" 4 | LOCAL cString2 := 'string2' 5 | LOCAL cString3 := [string3] 6 | LOCAL cLongString, cEscaped := e"escaped\cmultiline\r\ntext" 7 | #pragma __text | cLongString+="%s" | | cLongString:="" 8 | Other string 9 | multiline 10 | all this text will be put inside cLongString 11 | #pragma __endtext 12 | TEXT INTO cLongString 13 | this mode is xHarbour style 14 | ENDTEXT -------------------------------------------------------------------------------- /server/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // Vedere https://go.microsoft.com/fwlink/?LinkId=733558 3 | // per la documentazione relativa al formato tasks.json 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "prelanch", 8 | "type": "npm", 9 | "script": "prelanch", 10 | "group": { 11 | "kind": "build", 12 | "isDefault": true 13 | }, 14 | "problemMatcher": [] 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /test/setup32.bat: -------------------------------------------------------------------------------- 1 | SET HB_INSTALL_PREFIX=C:\harbour 2 | SET HB_COMPILER_PREFIX=C:\harbour\bin\win\msvc 3 | SET FWH_INSTALL=C:\FWH 4 | SET TLPOSWIN_SRC=%CD% 5 | CALL "c:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars32.bat" 6 | if %ERRORLEVEL%==1 CALL "c:\Program Files\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars32.bat" 7 | if %ERRORLEVEL%==1 CALL "c:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars32.bat" 8 | SET PATH=%PATH%;%HB_COMPILER_PREFIX% 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .hbmk 2 | *.exe 3 | *.lib 4 | *.a 5 | *.pdb 6 | *.7z 7 | *-lock.json 8 | *.c 9 | *.cpp 10 | *.log 11 | *.txt 12 | *.i 13 | *.ppo 14 | client/server 15 | client/extra 16 | client/test 17 | node_modules 18 | bin 19 | *.obj 20 | obj 21 | *.ilk 22 | settings.json 23 | *.vsix 24 | *.bak 25 | buildLib*.bat 26 | test/.vscode/c_cpp_properties.json 27 | test/operators.prg 28 | test/test30.inc 29 | test/test30.prg 30 | **/dist 31 | **/.VSCodeCounter 32 | testVari 33 | 34 | *.exp 35 | *.ppt 36 | trash 37 | **/.clinic 38 | *.cpuprofile 39 | 40 | appunti_formatter -------------------------------------------------------------------------------- /server/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "terminal.integrated.defaultProfile.windows": "Command Prompt", 3 | "cSpell.words": [ 4 | "aperricone", 5 | "harbourdoc", 6 | "hbmk", 7 | "BEGINDUMP", 8 | "ENDDUMP", 9 | "ONELINER", 10 | "cstream", 11 | "endcase", 12 | "endclass", 13 | "endtext", 14 | "fiel", 15 | "hbdocs", 16 | "memv", 17 | "memvar", 18 | "preproc" 19 | ], 20 | "files.trimFinalNewlines": true, 21 | "files.trimTrailingWhitespace": true 22 | } -------------------------------------------------------------------------------- /test/mini_debugger.js: -------------------------------------------------------------------------------- 1 | var net = require("net"); 2 | 3 | var port = 6110; //temp 4 | var server = net.createServer(socket => { 5 | //connected! 6 | server.close(); 7 | socket.on("data", data=> { 8 | console.log(data.toString()); 9 | socket.write("STEP\r\n"); 10 | }); 11 | 12 | socket.on("close", () =>{ 13 | console.log("end.") 14 | socket.end(); 15 | }); 16 | //while(!socket.destroyed) 17 | //{ 18 | // var r = socket.read(); 19 | // while(r!=null) 20 | // { 21 | // console.log(r); 22 | // r = socket.read(); 23 | // } 24 | // 25 | //}; 26 | }).listen(port,"127.0.0.1"); 27 | -------------------------------------------------------------------------------- /test/mybuild.bat: -------------------------------------------------------------------------------- 1 | :start 2 | hbmk2 %1 %2 %3 %4 %5 %6 %7 %8 %9 3 | IF %ERRORLEVEL% LSS 1000 goto :eof 4 | set HB_INSTALL_PREFIX=C:\harbour 5 | SET CD_SRC=%CD% 6 | CALL "c:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars32.bat" 7 | if %ERRORLEVEL%==1 CALL "c:\Program Files\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars32.bat" 8 | if %ERRORLEVEL%==1 CALL "c:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars32.bat" 9 | echo on 10 | cd %CD_SRC% 11 | set path=%PATH%;%HB_INSTALL_PREFIX%\bin;%HB_INSTALL_PREFIX%\bin\win\msvc 12 | goto start 13 | -------------------------------------------------------------------------------- /client/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Inserire le impostazioni in questo file per sovrascrivere quelle utente e predefinite. 2 | { 3 | "terminal.integrated.defaultProfile.windows": "Command Prompt", 4 | "svg.preview.background": "black", 5 | "cSpell.words": [ 6 | "aperricone", 7 | "harbourdoc", 8 | "hbmk", 9 | "BEGINDUMP", 10 | "ENDDUMP", 11 | "ONELINER", 12 | "cstream", 13 | "endcase", 14 | "endclass", 15 | "endtext", 16 | "fiel", 17 | "hbdocs", 18 | "memv", 19 | "memvar", 20 | "preproc" 21 | ] 22 | } -------------------------------------------------------------------------------- /client/copy.js: -------------------------------------------------------------------------------- 1 | const spawn = require("child_process").spawn 2 | const platform = require("os").platform() 3 | const cmd = /^win/.test(platform) 4 | ? `..\\server\\copy.bat` 5 | : `../server/copy.sh` 6 | 7 | spawn(cmd, [], { stdio: "inherit", cwd:"../server/" }).on("exit", code => { 8 | const pjson = require('./package.json'); 9 | //console.log(pjson.version); 10 | const fs = require('fs'); 11 | let rs = fs.createReadStream('../test/dbg_lib.prg'); 12 | let ws = fs.createWriteStream('./extra/dbg_lib.prg'); 13 | ws.write(`// For Harbour extension version v.${pjson.version}\r\n\r\n`) 14 | rs.pipe(ws).on("finish",()=>{ 15 | process.exit(code) 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /test/db1.prg: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | function Main() 4 | 5 | local aMatch := hb_regexAll( '[a-z0-9_]+', "function Main()",.F./*CASE*/,/*line*/,/*nMat*/,/*nGet*/,.F./*onlyName*/) 6 | local n 7 | ErrorBlock({|e| MyErrorBlock(e) }) 8 | 9 | #ifndef __XHARBOUR__ 10 | BEGIN SEQUENCE WITH {|oErr| BREAK( oErr ) } 11 | #else 12 | try 13 | #endif 14 | n:=1+{2} 15 | ? Len( aMatch ) 16 | 17 | for n := 1 to Len( aMatch ) 18 | ? "1: ",aMatch[ n,1,1 ], " from ", aMatch[ n,1,2 ],"to ",aMatch[ n,1,3 ] 19 | next 20 | #ifndef __XHARBOUR__ 21 | recover 22 | #else 23 | catch 24 | #endif 25 | ? "recover" 26 | end 27 | 28 | return nil 29 | 30 | PROC MyErrorBlock(e) 31 | ? "ERRORISSIMO ",e:Description 32 | 33 | -------------------------------------------------------------------------------- /test/build_libX.bat: -------------------------------------------------------------------------------- 1 | goto start 2 | :setup 3 | set HB_INSTALL_PREFIX=C:\xharbour 4 | SET CD_SRC=%CD% 5 | :: I use visual studio 6 | CALL "c:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars32.bat" 7 | if %ERRORLEVEL%==1 CALL "c:\Program Files\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars32.bat" 8 | if %ERRORLEVEL%==1 CALL "c:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars32.bat" 9 | echo on 10 | cd %CD_SRC% 11 | set path=%PATH%;%HB_INSTALL_PREFIX%\bin;%HB_INSTALL_PREFIX%\bin\win\msvc 12 | 13 | :start 14 | harbour dbg_lib.prg -oobj\dbg_lib.c -I%HB_INSTALL_PREFIX%\include -gc0 15 | IF %ERRORLEVEL% GEQ 1000 goto setup 16 | cl obj\dbg_lib.c -I%HB_INSTALL_PREFIX%\include /c /Foobj/dbg_lib.obj 17 | lib obj/dbg_lib.obj /out:code_dbgX.lib 18 | -------------------------------------------------------------------------------- /server/webpack.config.js: -------------------------------------------------------------------------------- 1 | //@ts-check 2 | 3 | 'use strict'; 4 | 5 | const path = require('path'); 6 | const CopyPlugin = require('copy-webpack-plugin'); 7 | 8 | /**@type {import('webpack').Configuration}*/ 9 | const config = { 10 | target: 'node', 11 | entry: './src/main.js', 12 | output: { 13 | path: path.resolve(__dirname, 'dist'), 14 | filename: 'hb_server.js' 15 | }, 16 | devtool: 'source-map', 17 | resolve: { 18 | extensions: ['.ts', '.js'] 19 | }, 20 | module: { 21 | rules: [ 22 | { 23 | test: /\.js$/, 24 | exclude: /node_modules/ 25 | } 26 | ] 27 | }, 28 | node: { 29 | __dirname: false 30 | }, 31 | plugins: [ 32 | new CopyPlugin({patterns:[ 33 | {from: "src/hbdocs.*",to:"[name][ext]"} 34 | ]}) 35 | ] 36 | }; 37 | module.exports = config; -------------------------------------------------------------------------------- /client/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that launches the extension inside a new window 2 | { 3 | "version": "0.1.0", 4 | "configurations": [ 5 | { 6 | "name": "Extension", 7 | "type": "extensionHost", 8 | "request": "launch", 9 | "runtimeExecutable": "${execPath}", 10 | "args": ["--extensionDevelopmentPath=${workspaceRoot}" ], 11 | "preLaunchTask": "prelanch" 12 | }, 13 | { 14 | "type": "node", 15 | "request": "launch", 16 | "name": "Debugger", 17 | "cwd": "${workspaceRoot}", 18 | "program": "${workspaceRoot}/src/debugger.js", 19 | "args": [ "--server=4711" ], 20 | "preLaunchTask": "prelanch" 21 | }], 22 | "compounds": [ 23 | { 24 | "name": "Extension + Debugger", 25 | "configurations": [ "Extension", "Debugger" ] 26 | } 27 | ] 28 | } -------------------------------------------------------------------------------- /test/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "harbour-dbg", 6 | "request": "attach", 7 | "name": "Scegli un processo e collegati", 8 | "process": "${command:pickProcess}", 9 | "sourcePaths": ["${workspaceFolder}"] 10 | }, 11 | { 12 | "type": "harbour-dbg", 13 | "request": "launch", 14 | "name": "Launch currentFile", 15 | "program": "${workspaceRoot}/${fileBasenameNoExtension}", 16 | "sourcePaths": ["${workspaceRoot}"], 17 | "workingDir": "${workspaceRoot}/", 18 | "stopOnEntry": true, 19 | //"preLaunchTask": "build", 20 | "terminalType": "integrated" 21 | }, 22 | { 23 | "type": "harbour-dbg", 24 | "request": "attach", 25 | "name": "Attach currentFile", 26 | "program": "${workspaceRoot}/${fileBasenameNoExtension}", 27 | "sourcePaths": ["${workspaceRoot}"], 28 | "debugServer": 4711 29 | } 30 | ] 31 | } -------------------------------------------------------------------------------- /test/myBuildX.bat: -------------------------------------------------------------------------------- 1 | :start 2 | harbour %1.prg -oobj\%1.c -I%HB_INSTALL_PREFIX%\include -gc2 -b 3 | IF %ERRORLEVEL% GEQ 1000 goto setup 4 | cl obj\%1.c -I%HB_INSTALL_PREFIX%\include /c /Foobj/%1.obj 5 | link obj/%1.obj /libpath:%HB_INSTALL_PREFIX%\lib /subsystem:console ^ 6 | code_dbgX.lib vm.lib rtl.lib common.lib rdd.lib pcrepos.lib ^ 7 | macro.lib dbfntx.lib dbffpt.lib hbsix.lib lang.lib gtwin.lib 8 | :: 9 | goto :eof 10 | 11 | :setup 12 | set HB_INSTALL_PREFIX=C:\xharbour 13 | SET CD_SRC=%CD% 14 | :: I use visual studio 2017 x86 on a win64 system. 15 | CALL "c:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars32.bat" 16 | if %ERRORLEVEL%==1 CALL "c:\Program Files\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars32.bat" 17 | if %ERRORLEVEL%==1 CALL "c:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars32.bat" 18 | echo on 19 | cd %CD_SRC% 20 | set path=%PATH%;%HB_INSTALL_PREFIX%\bin;%HB_INSTALL_PREFIX%\bin\win\msvc 21 | goto start 22 | -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "harbour-lang-server", 3 | "description": "Server part of Harbour language support for visual studio code.", 4 | "version": "0.6.0", 5 | "author": "aperricone", 6 | "main": "dist/hb_server.js", 7 | "bin": { 8 | "hbLanguageServer": "dist/hb_server.js" 9 | }, 10 | "license": "GPL", 11 | "repository": { 12 | "url": "https://github.com/APerricone/harbourCodeExtension" 13 | }, 14 | "engines": { 15 | "node": "*" 16 | }, 17 | "dependencies": { 18 | "npm-check-updates": "^16.3.11", 19 | "true-case-path": "^2.2.1", 20 | "vscode-languageserver": "^8.0.2", 21 | "vscode-languageserver-textdocument": "^1.0.4", 22 | "vscode-uri": "^3.0.6" 23 | }, 24 | "devDependencies": { 25 | "@types/node": "^18.8.4", 26 | "copy-webpack-plugin": "^11.0.0", 27 | "webpack": "^5.74.0", 28 | "webpack-cli": "^4.10.0" 29 | }, 30 | "scripts": { 31 | "prelanch": "webpack --mode development", 32 | "webpack2": "webpack --mode production" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /client/webpack.config.js: -------------------------------------------------------------------------------- 1 | //@ts-check 2 | 3 | 'use strict'; 4 | 5 | const path = require('path'); 6 | 7 | const config = { 8 | target: 'node', 9 | entry: { 10 | "extension": './src/extension.js', 11 | "debugger": './src/debugger.js' 12 | }, 13 | output: { 14 | path: path.resolve(__dirname, 'dist'), 15 | libraryTarget: 'commonjs2', 16 | devtoolModuleFilenameTemplate: '../[resource-path]' 17 | }, 18 | devtool: 'source-map', 19 | externals: { 20 | vscode: 'commonjs vscode' 21 | }, 22 | resolve: { 23 | extensions: ['.js'], 24 | alias: { 25 | '@yagisumi/win-output-debug-string': path.join(__dirname,'node_modules/@yagisumi/win-output-debug-string/build/Release/win_output_debug_string.node') 26 | } 27 | }, 28 | module: { 29 | rules: [ 30 | {test: /\.node$/, use: 'node-loader'}, 31 | { 32 | test: /\.js$/, 33 | exclude: /node_modules/ 34 | } 35 | ] 36 | }, 37 | node: { 38 | __dirname: false 39 | } 40 | }; 41 | module.exports = config; 42 | -------------------------------------------------------------------------------- /client/syntaxes/language-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": { 3 | // symbol used for single line comment. Remove this entry if your language does not support line comments 4 | "lineComment": "//", 5 | // symbols used for start and end a block comment. Remove this entry if your language does not support block comments 6 | "blockComment": [ "/*", "*/" ] 7 | }, 8 | // symbols used as brackets 9 | "brackets": [ 10 | ["{", "}"], 11 | ["[", "]"], 12 | ["(", ")"] 13 | ], 14 | // symbols that are auto closed when typing 15 | "autoClosingPairs": [ 16 | ["{", "}"], 17 | ["[", "]"], 18 | ["(", ")"], 19 | {"open": "\"","close": "\"", "notIn": ["string", "comment"] }, 20 | {"open": "'","close": "'", "notIn": ["string", "comment"] }, 21 | {"open": "/*","close": "*/", "notIn": ["string"] } 22 | ], 23 | // symbols that that can be used to surround a selection 24 | "surroundingPairs": [ 25 | ["{", "}"], 26 | ["[", "]"], 27 | ["(", ")"], 28 | ["\"", "\""], 29 | ["'", "'"] 30 | ] 31 | } -------------------------------------------------------------------------------- /client/harbourIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /client/LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 - present Antonino Perricone 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /client/harbour.code-snippets: -------------------------------------------------------------------------------- 1 | { 2 | "cicle-for": { 3 | "scope": "harbour", 4 | "prefix": "for", 5 | "body": ["for $1:=${2:1} to $3", 6 | "\t$0", 7 | "next"] 8 | }, 9 | "cicle-foreach": { 10 | "scope": "harbour", 11 | "prefix": "for each", 12 | "body": ["for each $1 in $2", 13 | "\t$0", 14 | "next"] 15 | }, 16 | "do while": { 17 | "scope": "harbour", 18 | "prefix": "do while", 19 | "body": ["do while $1", 20 | "\t$0", 21 | "enddo"] 22 | }, 23 | "if": { 24 | "scope": "harbour", 25 | "prefix": "if", 26 | "body": ["if $1", 27 | "\t$0", 28 | "endif"] 29 | }, 30 | "switch": { 31 | "scope": "harbour", 32 | "prefix": "switch", 33 | "body": ["switch $1", 34 | "\tcase $2", 35 | "\t\t$0", 36 | "\t\texit", 37 | "endswitch"] 38 | }, 39 | "do case": { 40 | "scope": "harbour", 41 | "prefix": "do case", 42 | "body": ["do case", 43 | "\tcase $2", 44 | "\t\t$0", 45 | "endcase"] 46 | }, 47 | "try-catch": { 48 | "scope": "harbour", 49 | "prefix": "try", 50 | "body": ["try $1", 51 | "\t$0", 52 | "catch ${2:oErr}", 53 | "\t", 54 | "end"] 55 | } 56 | } 57 | 58 | -------------------------------------------------------------------------------- /client/src/utils.js: -------------------------------------------------------------------------------- 1 | const vscode = require('vscode'); 2 | const fs = require("fs"); 3 | 4 | 5 | /** 6 | * 7 | * @param {vscode.CancellationToken} token 8 | * @return {Promise>>} 9 | */ 10 | function getAllWorkspaceFiles(token) { 11 | /** @type{Array} */ 12 | var promises = []; 13 | for(let d=0;d{ 20 | if(token.isCancellationRequested) { 21 | reject(token); 22 | return; 23 | } 24 | fs.readdir(uri.fsPath, {withFileTypes: true},(err,ff)=>{ 25 | if(token.isCancellationRequested) { 26 | reject(token); 27 | return; 28 | } 29 | res(ff); 30 | }) 31 | }); 32 | promises.push(r); 33 | } 34 | return Promise.all(promises); 35 | } 36 | 37 | exports.getAllWorkspaceFiles = getAllWorkspaceFiles; -------------------------------------------------------------------------------- /server/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Utilizzare IntelliSense per esplorare possibili attributi di debug di Node.js . 3 | // Al passaggio del mouse vengono visualizzate le descrizioni degli attributi esistenti. 4 | // Per ulteriori informazioni, visitare: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "attach", 10 | "name": "Attach", 11 | "port": 21780, 12 | "preLaunchTask": "prelanch" 13 | }, 14 | { 15 | "type": "node", 16 | "request": "launch", 17 | "name": "Launch Program", 18 | "args": [ 19 | "-v" 20 | ] 21 | }, 22 | { 23 | "type": "node", 24 | "request": "launch", 25 | "name": "test_parse", 26 | "program": "${workspaceRoot}/test_parse.js", 27 | "console": "integratedTerminal" 28 | }, 29 | { 30 | "type": "node", 31 | "request": "launch", 32 | "name": "parseHBDoc", 33 | "program": "${workspaceRoot}/parseHBDoc.js" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /client/src/docCreator.js: -------------------------------------------------------------------------------- 1 | const vscode = require('vscode'); 2 | 3 | // reuse the bracket-match style 4 | var decoration; 5 | /** @type{client.LanguageClient} */ 6 | var client; 7 | 8 | function activate(context,_client) { 9 | client=_client; 10 | vscode.window.onDidChangeTextEditorSelection((e) => WriteDoc(e) ); 11 | } 12 | 13 | function WriteDoc(evt) { 14 | if(evt.kind!=1) return; //only keyboard 15 | var editor = evt.textEditor 16 | if(!editor) return; 17 | if(!editor.document) return; 18 | if(editor.document.languageId!="harbour") return; 19 | if( evt.selections.length>1 || 20 | evt.selections[0].start.line!=evt.selections[0].end.line || 21 | evt.selections[0].start.character!=evt.selections[0].end.character) 22 | return; 23 | var destRange = new vscode.Range(evt.selections[0].start.line,0,evt.selections[0].start.line,100); 24 | var line = editor.document.getText(destRange) 25 | if(!line.startsWith("/* $DOC$")) return; 26 | var param = {textDocument:{uri:editor.document.uri.toString()}, sel:destRange}; 27 | param["eol"]=editor.document.eol; 28 | client.sendRequest("harbour/docSnippet",param).then(snippet=>{ 29 | if(snippet) { 30 | evt.textEditor.insertSnippet(new vscode.SnippetString(snippet),destRange); 31 | } 32 | }); 33 | } 34 | 35 | exports.activate = activate; 36 | -------------------------------------------------------------------------------- /client/src/decorator.js: -------------------------------------------------------------------------------- 1 | const vscode = require('vscode'); 2 | 3 | // reuse the bracket-match style 4 | 5 | var decoration; 6 | /** @type{client.LanguageClient} */ 7 | var client; 8 | function activate(context,_client) 9 | { 10 | client=_client; 11 | decoration = vscode.window.createTextEditorDecorationType({ 12 | borderStyle: 'solid', 13 | borderWidth: '1px', 14 | borderColor: new vscode.ThemeColor("editorBracketMatch.border"), 15 | backgroundColor: new vscode.ThemeColor("editorBracketMatch.background") 16 | }); 17 | vscode.window.onDidChangeTextEditorSelection((e) => showGroups(e) ); 18 | } 19 | 20 | function showGroups(evt) 21 | { 22 | var section = vscode.workspace.getConfiguration('harbour'); 23 | if(!section.decorator) return; 24 | 25 | var editor = evt.textEditor 26 | if(!editor) return; 27 | if(!editor.document) return; 28 | if(editor.document.languageId!="harbour") return; 29 | if(evt.selections.length!=1) 30 | { 31 | evt.textEditor.setDecorations(decoration, []); 32 | return; 33 | } 34 | var sel = evt.selections[0]; 35 | client.sendRequest("harbour/groupAtPosition",{textDocument:{uri:editor.document.uri.toString()}, sel:sel}).then(ranges=>{ 36 | var places = []; 37 | for (let k = 0; k < ranges.length; k++) { 38 | const rr = ranges[k]; 39 | places.push({ range: new vscode.Range(rr.line,rr.startCol,rr.line,rr.endCol) }); 40 | } 41 | 42 | evt.textEditor.setDecorations(decoration, places); 43 | }) 44 | } 45 | 46 | exports.activate = activate; 47 | -------------------------------------------------------------------------------- /client/src/myLocalize.js: -------------------------------------------------------------------------------- 1 | const { KeyObject } = require("crypto"); 2 | const fs = require("fs"); 3 | const path = require('path'); 4 | const localize = require("vscode-nls").loadMessageBundle(); 5 | 6 | var messages,messagesFall; 7 | function Init() 8 | { 9 | if(process.env.VSCODE_NLS_CONFIG) 10 | reInit(JSON.parse(process.env.VSCODE_NLS_CONFIG)); 11 | else 12 | reInit(""); 13 | } 14 | Init(); 15 | function reInit(config) 16 | { 17 | try 18 | { 19 | messages = JSON.parse(fs.readFileSync(path.resolve(__dirname, path.join('..',"package.nls."+config.locale+".json")), 'utf8')); 20 | messagesFall = JSON.parse(fs.readFileSync(path.resolve(__dirname, path.join('..',"package.nls.json")), 'utf8')); 21 | } 22 | catch (error) { 23 | messages = JSON.parse(fs.readFileSync(path.resolve(__dirname, path.join('..',"package.nls.json")), 'utf8')); 24 | messagesFall = messages; 25 | } 26 | } 27 | 28 | function indexTrim(str, ch) { 29 | var start = 0, 30 | end = str.length; 31 | 32 | while(start < end && str[start] === ch) 33 | ++start; 34 | 35 | while(end > start && str[end - 1] === ch) 36 | --end; 37 | 38 | return (start > 0 || end < str.length) ? str.substring(start, end) : str; 39 | } 40 | 41 | function myLocalize() 42 | { 43 | var arg = Array.prototype.slice.call(arguments); 44 | /** @type {String} */ 45 | let key = indexTrim(arg[0],'%'); 46 | if(key in messages) { 47 | key = messages[key]; 48 | } else if(key in messagesFall) { 49 | key = messagesFall[key]; 50 | } else { 51 | key = "Error: '" + key + "' not found"; 52 | } 53 | arg[0]=key; 54 | arg.splice(0,0,null); 55 | return localize.apply(null, arg) 56 | } 57 | 58 | exports.reInit = reInit; 59 | exports.localize = myLocalize; -------------------------------------------------------------------------------- /test/debugger.md: -------------------------------------------------------------------------------- 1 | # Debugger commands 2 | The sended lines and the command are show with some variable between curly parenthesis, this names are used in the explanetion. 3 | 4 | The debugger sends a 5 | >START 6 | 7 | when the program just starts 8 | 9 | >STOP 10 | 11 | when the execution stops (TODO: other types of stop support) 12 | 13 | ## GO 14 | start the program until there is a breakpoint or an AltD call (TODO: error support) 15 | 16 | ## STEP 17 | run to next line of code even if is in another procedure 18 | 19 | ## NEXT 20 | run to next line of code without calls 21 | 22 | ## BREAKPOINT 23 | Add/remove a breakpoint, it must be followed by a line in this form: 24 | 25 | > {+/-}:{prgFile}:{line} 26 | 27 | It will be responded by a line in this form 28 | 29 | >BREAK:{prgFile}:{lineReq}:{lineSet}:{reason} 30 | 31 | where {lineReq} is the requested line, and {lineSet} is the nearest next line where it can be set, or -1 if the request is for deletion or the debbuger is not be able to set the breakpoint, in this case the {reason} is filled with "invalid line" or "invalid module". 32 | 33 | 34 | ## STACK 35 | require for stack, it is returned in this form: 36 | > STACK {len}\ 37 | {prgFile}:{line}:{functionNames} 38 | 39 | with the second line repeated {len} times 40 | 41 | ## LOCALS, STATICS, PRIVATES, PRIVATE_CALLEE, PUBLICS, GLOBALS, EXTERNALS 42 | Require for variable list of this type, it must be followed by a line in this form: 43 | > {stack}:{start}:{count} 44 | 45 | where {stack} is the current stack, {start} is the first index of request variables (send 1 or below to request from first) and {count} is the number of request variables. 46 | 47 | They are returned in this way: 48 | > {prefix}:{idx1}:{idx2}:{idx3}:{name}:{type}:{value} \ 49 | > END 50 | 51 | the first line is repeated for every variable returned. \ 52 | For Array, hash and classes the value is the lenght of contained children. 53 | if you send a line like: 54 | 55 | > {prefix}:{idx1}:{idx2}:{idx3} 56 | 57 | followed by the stack, start 'n' count request, it return its children. 58 | -------------------------------------------------------------------------------- /client/formatter-settings/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: var(--vscode-font-family); 3 | font-weight: var(--vscode-font-weight); 4 | font-size: var(--vscode-font-size); 5 | } 6 | 7 | * { 8 | font-family: inherit; 9 | } 10 | 11 | h1 { 12 | font-size: 1.85em; 13 | } 14 | 15 | input { 16 | border-color: var(--vscode-editorWidget-border); 17 | background-color: var(--vscode-editorWidget-background); 18 | color: var(--vscode-editorWidget-foreground); 19 | } 20 | 21 | *:focus { 22 | outline-color: var(--vscode-focusBorder) !important; 23 | border-color: var(--vscode-focusBorder) !important; 24 | } 25 | 26 | label { 27 | border: 1px solid transparent; 28 | display: block; 29 | } 30 | label:focus-within { 31 | background: var(--vscode-settings-focusedRowBackground); 32 | border-color: var(--vscode-notebook-focusedRowBorder); 33 | } 34 | label:hover, div:hover { 35 | background: var(--vscode-settings-focusedRowBackground); 36 | } 37 | 38 | select { 39 | background-color: var(--vscode-editorWidget-background); 40 | color: var(--vscode-editorWidget-foreground); 41 | border-color: var(--vscode-editorWidget-border); 42 | width: 25em; 43 | padding: 0.15px 0.6em; 44 | height: 2em; 45 | } 46 | 47 | input[type="checkbox"] { 48 | width: 1rem; 49 | height: 1rem; 50 | -webkit-appearance: none; 51 | } 52 | 53 | input[type="checkbox"]::before { 54 | content: ''; 55 | font-family: codicon; 56 | width: 1rem; 57 | height: 1rem; 58 | border-color: var(--vscode-editorWidget-border); 59 | background: var(--vscode-editorWidget-background); 60 | color: var(--vscode-editorWidget-foreground); 61 | display: inline-block; 62 | text-align: center; 63 | } 64 | 65 | input[type="checkbox"]:checked:before { 66 | content: '\eab2'; 67 | } 68 | label { 69 | user-select: none; 70 | cursor: pointer; 71 | } 72 | 73 | #preview { 74 | display: block; 75 | position: fixed; 76 | top: 2rem; 77 | bottom: 2rem; 78 | right: 2rem; 79 | width: 40%; 80 | border: 1px solid var(--vscode-editorWidget-border); 81 | background: var(--vscode-editorWidget-background); 82 | white-space: pre; 83 | font-family: var(--vscode-editor-font-family); 84 | padding: 1rem; 85 | overflow: hidden; 86 | } 87 | 88 | #preview > .keyword { 89 | color: var(--vscode-debugTokenExpression-name); 90 | } 91 | 92 | #preview > .comment { 93 | color: var(--vscode-input-placeholderForeground); 94 | } 95 | -------------------------------------------------------------------------------- /client/src/debugProvider.js: -------------------------------------------------------------------------------- 1 | const vscode = require('vscode'); 2 | const path = require("path"); 3 | const fs = require("fs"); 4 | const cp = require("child_process"); 5 | const os = require("os"); 6 | const localize = require("./myLocalize.js").localize; 7 | const getAllWorkspaceFiles = require("./utils.js").getAllWorkspaceFiles; 8 | const taskProvider = require('./taskProvider.js'); 9 | 10 | class HarbourDBGProvider { 11 | provideDebugConfigurations(folder,token) { 12 | return new getAllWorkspaceFiles(token).then((values)=>{ 13 | var retValue = [{ 14 | "type": "harbour-dbg", 15 | "request": "launch", 16 | "name": "Launch currentFile", 17 | "preLaunchTask": localize("harbour.task.HBMK2.provideName3") 18 | }]; 19 | if(token.isCancellationRequested) { 20 | return; 21 | } 22 | for(let j=0;j debugger verso il programma hanno forma COMANDO:PARAM1:PARAM2:PARAM3...\r\n. 5 | Quelli dal <- programma verso il debugger hanno forma COMANDO\r\nPARAM1\r\nPARAM2\r\n. 6 | 7 | ## GO -> 8 | Indica al programma di proseguire l'esecuzione 9 | 10 | ## NEXT -> 11 | Indica al programma di eseguire la prossima istruzione 12 | 13 | ## STEP -> 14 | Indica al programma di eseguire la prossima istruzione allo stesso livello 15 | 16 | ## EXIT -> 17 | Indica al programma di uscire dal metoo corrente 18 | 19 | ## PAUSE -> 20 | Indica al programma di interrompere l'esecuzione alla prima istruzione ***compilata con i simboli di debug***. 21 | 22 | ## STOP <- 23 | Indica che il programma si è interrotto. è seguito da una descrizione testuale del perché si è fermato, ad esempio "Break" perché ha trovato un breakpoint, "Pause" perche é stato procesato il comando PAUSE, ecc... 24 | 25 | ## ERROR <- 26 | Indica che il programma si è interrotto a seguito di un'errore 27 | 28 | ## INERROR -> 29 | Chiede se si è in errore, il programma restituisce T se si è in errore. ***Inutile*** 30 | 31 | ## BREAKPOINT -> 32 | Indica che sto per inviare un break point. la seconda riga ha l'elenco dei parametri separati da : due punti. 33 | * \+ per i breapoint da aggiungere, - per quelli da togliere 34 | * il nome del file 35 | * la riga 36 | * opzionalmente ? seguito da (separato da due punti) un comando in harbour da esegure dove i : sono sostituiti da ;. Il breakpoint scatta solo se la condizione è vera. 37 | * opzionalmente C seguito da (separato da due punti) un numero ad indicare dopo quante volte il breakpoint scatterà 38 | * opzionalmente L seguito da (separato da due punti) una stringa dove le parte tra parentesi graffe vengono eseguite (***da cambiare***) 39 | 40 | ## LOG <- 41 | Richiede la stampa di una stringa di debug, che seguie il "LOG:" 42 | 43 | ## LOCALS, PUBLICS, PRIVATES, PRIVATE_CALLEE, STATICS -> 44 | Richede la lista delle variabili nello scopo indicato dal comando. la seconda riga ha l'elenco dei parametri separati da : due punti. 45 | * il livello dello stack 46 | * l'indice del primo elemento da tornare, partendo da 1 47 | * il numero di element da tornare, 0 per averli tutti. 48 | il programma risponderò con un messaggio che comincia con lo stesso comando, seguito da le informazioni separate da 2 punti: 49 | * la prima parte del comando per avere i figli di questa variabile, 3 lettere 50 | * il libello dello stack 51 | * l'id di questa variabile (numerico) 52 | * il nome dell'id di questa variabile 53 | * il nome 54 | 55 | ## EXPRESSION -> 56 | Richiede l'esecuzione di un comando. la seconda riga ha l'elenco dei parametri separati da : due punti. 57 | * il livello nello stack 58 | * l'espressione dove i : sono stituiti da ; 59 | Per ognuno di questi comandi il debugger risponderà con un messaggio che comincia con **EXPRESSION** seguito da le informazioni separate da 2 punti: 60 | * il livello dello stack 61 | * il tipo del risultato, può valere U, N, C, L, A, H, O ecc.. 62 | * il risultato da mostrare, nel caso di N,C,L oppure il numero di figli nel caso A,H,O 63 | 64 | -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | # Antonino Perricone's extension for visual studio code about Harbour and xHarbour programming languages 2 | 3 | [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=LHCBE7ERSSVDJ&source=url) 4 | [![Version](https://vsmarketplacebadges.dev/version-short/aperricone.harbour.svg)](https://marketplace.visualstudio.com/items?itemName=aperricone.harbour) 5 | [![Installs](https://vsmarketplacebadges.dev/installs-short/aperricone.harbour.svg)](https://marketplace.visualstudio.com/items?itemName=aperricone.harbour) 6 | [![Ratings](https://vsmarketplacebadges.dev/rating-short/aperricone.harbour.svg)](https://marketplace.visualstudio.com/items?itemName=aperricone.harbour) 7 | 8 | do you like this extension? Help me to make it better with a donation, click the first button. 9 | 10 | ## Features 11 | 12 | - [syntax hightlight](https://github.com/APerricone/harbourCodeExtension/wiki/Syntax-hightlight), with [Edgard Lorraine Messias](https://github.com/edgardmessias) 13 | - [Debug support](https://github.com/APerricone/harbourCodeExtension/wiki/Debugger) 14 | - [Diagnostic infos](https://github.com/APerricone/harbourCodeExtension/wiki/Diagnostics-Lint) 15 | - Symbol Definitions Within a Document provider (access it by pressing CTRL+SHIFT+O or CTRL+P then @) 16 | - Symbol Definitions in workspace provider (access it by pressing CTRL+T or CTRL+P then #) 17 | 18 | ## Documentation links 19 | See the [wiki](https://github.com/APerricone/harbourCodeExtension/wiki) for more information. 20 | An introdution for harbour developers can be found in these articles: 21 | - [Harbour Wiki - Developing and Debugging Harbour Programs with VSCODE](https://harbour.wiki/index.asp?page=PublicArticles&mode=show&id=190401174818&sig=6893630672) by Eric Lendvai 22 | - [Harbour magazine - Visual Studio Code for Harbour](https://medium.com/harbour-magazine/visual-studio-code-for-harbour-e148f9c1861a) by José Luis Sánchez ([available in spanish too](https://medium.com/harbour-magazine/visual-studio-code-para-harbour-85b0646ff312)) 23 | 24 | ## Requirements 25 | Sometime is necessary to set `harbour.compilerExecutable` with complete path. 26 | 27 | ## Extension Settings 28 | This extension contributes the following settings: 29 | 30 | * `harbour.validating`: enable/disable the validation every open and save of harbour files. 31 | * `harbour.compilerExecutable`: sometime is necessary to set the path of the harbour executable to make validation works. 32 | * `harbour.extraIncludePaths`: add path where found the includes to avoid "file not found" error. 33 | * `harbour.extraOptions`: other options to pass to harbour compiler. 34 | * `harbour.warningLevel`: sets the warning level for validation. 35 | * `harbour.decorator`: if true enables the feature of decoration of correspondents if/endif, for/next, while/endwhile, etc etc 36 | 37 | ## How to use the debugger 38 | You can use the command "Harbour: Get debugger code" to get the source of the debbugger, save it to a file naming it as you like, for example dbg_lib-prg. You can include this file in your project or **BETTER** create a library with this file to link in your project. 39 | 40 | > NOTE: don't forget to compile harbour file with debug information ***-b*** 41 | 42 | ### **IT IS STRONGLY RECOMMENDED TO UPDATE THE FILES EVERY EXTENSION VERSION** 43 | 44 | ## Known Issues 45 | 46 | -------------------------------------------------------------------------------- /server/test_parse.js: -------------------------------------------------------------------------------- 1 | var provider = require('./src/provider.js'); 2 | 3 | var p = new provider.Provider(); 4 | p.doGroups = true; 5 | var src = "..\\test\\minimal.prg"; 6 | src="..\\test\\class_c.prg" 7 | //src="C:\\Perry\\beta\\c_artmod.prg" 8 | //src = "c:\\fwh\\include\\fivewin.ch" 9 | //src="C:\\Harbour32\\tests\\hbpp\\hbpptest.prg" 10 | console.log(new Date()) 11 | var s = Number(Date.now()) 12 | p.parseFile(src).then(()=> { 13 | console.log(new Date()) 14 | console.log(Number(Date.now())-s) 15 | console.log( Object.keys(p.references).length ) 16 | /* 17 | for (var refId in p.references) if (p.references.hasOwnProperty(refId)) { 18 | const rr = p.references[refId] 19 | var msg = `reference ${refId}`; 20 | console.log(msg) 21 | msg = " ".repeat(msg.length) 22 | var baseType = rr[0].type, oneDifferent = false; 23 | for (let i = 0; i < rr.length; i++) { 24 | const ref = rr[i]; 25 | console.log(msg+`${ref.type} in ${ref.line}:${ref.col}`) 26 | if(ref.type!=baseType) oneDifferent = true 27 | } 28 | if(oneDifferent) console.log("**************************") 29 | } 30 | process.exit();*/ 31 | for (var fn in p.funcList) { 32 | if (p.funcList.hasOwnProperty(fn)) { 33 | var info = p.funcList[fn]; 34 | var msg = `${info.kind}: ${info.name} (${info.foundLike})`; 35 | if(info.parent) msg+= ` of ${info.parent.name}` 36 | msg+= ` in ${info.document}(${info.startLine}:${info.startCol})-(${info.endLine}:${info.endCol})` 37 | if(info.comment) msg+= ` (${info.comment})` 38 | console.log(msg) 39 | } 40 | } 41 | for(var db in p.databases) if (p.databases.hasOwnProperty(db)) { 42 | console.log(`database ${p.databases[db].name}`); 43 | for(var f in p.databases[db].fields) if (p.databases[db].fields.hasOwnProperty(f)) { 44 | console.log(` field ${p.databases[db].fields[f]}`); 45 | } } 46 | for(var i=0;i ${thisPart.snippet} found if ${thisPart.regEx}`); 71 | } 72 | } 73 | process.exit(); 74 | }); 75 | -------------------------------------------------------------------------------- /test/dbg_ex.prg: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | //* 4 | PROCEDURE __dbgEntry( nMode, uParam1, uParam2, uParam3, uParam4 ) 5 | local i, tmp, j, vv 6 | ? "__dbgEntry", nMode,":", uParam1,"-", uParam2,"-", uParam3,"-", uParam4 7 | 8 | switch nMode 9 | CASE HB_DBG_GETENTRY 10 | 11 | __dbgSetEntry() 12 | exit 13 | 14 | CASE HB_DBG_ACTIVATE 15 | //__dbgSetTrace(uParam1) 16 | ? "level:",__dbgProcLevel() 17 | for i:=1 to len(uParam3) 18 | ? "Stack " + alltrim(str(i))+":"+uParam3[i,HB_DBG_CS_MODULE]+"-"+uParam3[i,HB_DBG_CS_FUNCTION]+; 19 | "("+alltrim(str(uParam3[i,HB_DBG_CS_LINE]))+")*"+alltrim(str(uParam3[i,HB_DBG_CS_LEVEL]))+; 20 | " "+alltrim(str(len(uParam3[i,HB_DBG_CS_LOCALS])))+" locals, "; 21 | +alltrim(str(len(uParam3[i,HB_DBG_CS_STATICS])))+" statics, " 22 | for j:=1 to len(uParam3[i,HB_DBG_CS_LOCALS]) 23 | tmp := uParam3[i,HB_DBG_CS_LOCALS,j] 24 | vv := __dbgVMVarLGet( __dbgProcLevel() - tmp[ HB_DBG_VAR_FRAME ], tmp[ HB_DBG_VAR_INDEX ] ) 25 | ? "Local " + alltrim(str(i))+":#" + alltrim(str(tmp[HB_DBG_VAR_INDEX])) + ; 26 | tmp[HB_DBG_VAR_NAME] + "("+tmp[HB_DBG_VAR_TYPE]+":" + ; 27 | alltrim(str(tmp[HB_DBG_VAR_FRAME])) + ") " + valtype(vv), ; 28 | vv 29 | next 30 | ? "HB_MV_PRIVATE_LOCAL", __mvDbgInfo( HB_MV_PRIVATE_LOCAL, uParam3[i,HB_DBG_CS_LEVEL]) 31 | next 32 | for i:=1 to len(uParam4) 33 | ? "Module " + alltrim(str(i))+":"+uParam4[i,HB_DBG_MOD_NAME]+ ; 34 | " "+alltrim(str(len(uParam4[i,HB_DBG_MOD_STATICS])))+" statics, "; 35 | +alltrim(str(len(uParam4[i,HB_DBG_MOD_GLOBALS])))+" globals," 36 | next 37 | tmp := __dbgGetSourceFiles(uParam1) 38 | for i:=1 to len(tmp) 39 | ? "File " + alltrim(str(i))+":" + tmp[i] 40 | next 41 | exit 42 | ENDSWITCH 43 | 44 | //*/ 45 | proc main() 46 | local i 47 | AltD() 48 | ? "Perry" 49 | AltraFunzione() 50 | ? i:=2 51 | ? i 52 | return 53 | 54 | proc AltraFunzione 55 | local p := "sei fuori" 56 | local a := [1,2,3] 57 | memvar test 58 | private test := "non io" 59 | ? p 60 | ? "più righe" 61 | ? "per provare" 62 | return 63 | 64 | /* notes from src/debug/debugger.prg: 65 | __DbgEntry ACTIVATE -> breakpoint arrived, 66 | default there is a breakpoint at startup, without 'go' next line is a breakpoint, 67 | if not 'trace' next line even if is inside a called procedure is a breakpoint 68 | uParam1 --> debugInfo 69 | 70 | Commands: 71 | __dbgSetGo(debugInfo) --> play the program 72 | __dbgSetTrace(debugInfo) --> if called does not enter inside the next call 73 | __dbgSetCBTrace(debugInfo,lCB) --> trace includes codeblock too 74 | __dbgAddBreak(debugInfo,file,line) --> 75 | __dbgIsBreak(debugInfo,file,line) --> return id of breakpoint if setted 76 | __dbgDelBreak(debugInfo, id) 77 | __dbgAddWatch(debugInfo,cExpr,lTracePoint) --> 78 | __mvDbgInfo( HB_MV_PUBLIC|HB_MV_PRIVATE ) --> returns number of public/private variable 79 | __mvDbgInfo( HB_MV_PUBLIC|HB_MV_PRIVATE, idx, @cName ) -> returns the index of idx public variable (Sets its name in cName) 80 | __mvDbgInfo( HB_MV_PRIVATE_LOCAL, lv ) -> returns number of private variable at level lv of stack 81 | __dbgProcLevel() --> return the current level (len of stack?) 82 | 83 | gets of value of variable 84 | SWITCH aVar[ HB_DBG_VAR_TYPE ] 85 | CASE "G" ; RETURN __dbgVMVarGGet( aVar[ HB_DBG_VAR_FRAME ], aVar[ HB_DBG_VAR_INDEX ] ) 86 | CASE "L" ; RETURN __dbgVMVarLGet( __dbgProcLevel() - aVar[ HB_DBG_VAR_FRAME ], aVar[ HB_DBG_VAR_INDEX ] ) 87 | CASE "S" ; RETURN __dbgVMVarSGet( aVar[ HB_DBG_VAR_FRAME ], aVar[ HB_DBG_VAR_INDEX ] ) 88 | ENDSWITCH 89 | recap: 90 | localVariable: __dbgVMVarLGet(level,idx) --> LOC:LEVEL:IDX 91 | staticVariable: __dbgVMVarSGet(level,idx) --> STA:LEVEL:IDX 92 | privateVariable: __mvDbgInfo(HB_MV_PRIVATE,idx) --> PRI:IDX 93 | publicVariable: __mvDbgInfo(HB_MV_PUBLIC,idx) --> PUB:IDX 94 | */ 95 | 96 | -------------------------------------------------------------------------------- /test/class_c.prg: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define FORNITORE_PUNTIVENDITA "PUNT" 4 | #define FORNITORE_SEDE "SEDE" 5 | #define FORNITORE_SMA "SMA " 6 | #define FORNITORE_VARI "VARI" 7 | #define FORNITORE_VBPCSTORE "VBPC" 8 | #define FORNITORE_UNES "UNES" 9 | #define FORNITORE_UPIM "UPIM" 10 | 11 | class ClassTest 12 | DATA nFormat AS NUMERIC INIT 0 13 | DATA nWidth AS NUMERIC INIT 100 14 | DATA cText AS CHARACTER INIT "Colonna" 15 | DATA nIndex 16 | DATA nOrder 17 | DATA nWidthMin 18 | 19 | class Var pippo 20 | 21 | METHOD Test() //Directly C Code 22 | METHOD AddText(prova) 23 | endclass 24 | 25 | METHOD AddText(prova) 26 | ::cText += " - Added from Harbour code" + prova 27 | return nil 28 | 29 | procedure main() 30 | local col0 := ClassTest():New() 31 | local col1 := ClassTest():New() 32 | col1:cText := "Perry" 33 | col1:nWidthMin := 50 34 | col0:pippo := 10 35 | 36 | cazzo := figa+cazzo * 3+figa 37 | merda := merda * 3 38 | 39 | col0:Test() 40 | CallMember(col1) 41 | col1:Test() 42 | col0 := GetNewCol() 43 | col0:Test() 44 | return 45 | 46 | #pragma BEGINDUMP 47 | #include 48 | #include 49 | #include 50 | #include 51 | #if _STACK 52 | #include 53 | #endif 54 | // this code explain how to 55 | // code a harbour's class with c code 56 | 57 | #define CLASSNAME "CLASSTEST" 58 | struct CLASSTESTDATA 59 | { 60 | HB_USHORT classId; 61 | 62 | HB_SIZE nFormat; 63 | HB_SIZE nWidth; 64 | HB_SIZE cText; 65 | HB_SIZE nIndex; 66 | HB_SIZE nOrder; 67 | HB_SIZE nWidthMin; 68 | PHB_SYMB Test; 69 | } ClassTestData = {0}; 70 | 71 | void Init_ClassTest() { int i=0; 72 | if(ClassTestData.classId == 0) 73 | { 74 | ClassTestData.classId = hb_clsFindClass(CLASSNAME, NULL); 75 | ClassTestData.cText = hb_clsGetVarIndex(ClassTestData.classId,hb_dynsymGet("cText")); 76 | ClassTestData.nFormat = hb_clsGetVarIndex(ClassTestData.classId,hb_dynsymGet("nFormat")); 77 | ClassTestData.nWidth = hb_clsGetVarIndex(ClassTestData.classId,hb_dynsymGet("nWidth")); 78 | ClassTestData.nIndex = hb_clsGetVarIndex(ClassTestData.classId,hb_dynsymGet("nIndex")); 79 | ClassTestData.nOrder = hb_clsGetVarIndex(ClassTestData.classId,hb_dynsymGet("nOrder")); 80 | ClassTestData.nWidthMin = hb_clsGetVarIndex(ClassTestData.classId,hb_dynsymGet("nWidthMin")); 81 | } 82 | } 83 | 84 | HB_FUNC( CALLMEMBER ) 85 | { 86 | PHB_ITEM pItem = hb_param(1, HB_IT_OBJECT ); 87 | hb_objSendMsg(pItem,"AddText",0); 88 | hb_ret(); 89 | } 90 | 91 | HB_FUNC( GETNEWCOL ) 92 | { 93 | PHB_ITEM pItem = hb_itemNew(NULL); 94 | Init_ClassTest(); 95 | hb_clsAssociate( ClassTestData.classId ); 96 | pItem = hb_stackReturnItem(); 97 | hb_itemArrayPut(pItem,ClassTestData.cText, hb_itemPutC(NULL,"From C code")); 98 | hb_itemArrayPut(pItem,ClassTestData.nWidthMin, hb_itemPutNI(NULL, 611)); 99 | } 100 | 101 | HB_FUNC( CLASSTEST_TEST ) 102 | { 103 | PHB_ITEM pItem = hb_stackSelfItem(); 104 | PHB_ITEM data; 105 | Init_ClassTest(); 106 | 107 | if(pItem==0 || hb_objGetClass(pItem)!=ClassTestData.classId) 108 | { 109 | if(pItem==0) printf("no self\r\n"); 110 | else if(hb_objGetClass(pItem)!=ClassTestData.classId) printf("self is not a \"" CLASSNAME "\" (it is %i)\r\n", hb_objGetClass(pItem)); 111 | return; //invalid input 112 | } 113 | 114 | data = hb_itemArrayGet(pItem,ClassTestData.cText); 115 | printf("Column \"%s\"",hb_itemGetC(data)); 116 | data = hb_itemArrayGet(pItem,ClassTestData.nFormat); 117 | if(!HB_IS_NIL(data)) printf(" format:%i",hb_itemGetNI(data)); 118 | data = hb_itemArrayGet(pItem,ClassTestData.nIndex); 119 | if(!HB_IS_NIL(data)) printf(" index:%i",hb_itemGetNI(data)); 120 | data = hb_itemArrayGet(pItem,ClassTestData.nOrder); 121 | if(!HB_IS_NIL(data)) printf(" order:%i",hb_itemGetNI(data)); 122 | data = hb_itemArrayGet(pItem,ClassTestData.nWidth); 123 | if(!HB_IS_NIL(data)) printf(" width %i",hb_itemGetNI(data)); 124 | data = hb_itemArrayGet(pItem,ClassTestData.nWidthMin); 125 | if(!HB_IS_NIL(data)) printf(" min %i",hb_itemGetNI(data)); 126 | printf("\r\n"); 127 | hb_ret(); 128 | } 129 | 130 | #pragma ENDDUMP 131 | -------------------------------------------------------------------------------- /client/src/validation.js: -------------------------------------------------------------------------------- 1 | const vscode = require('vscode'); 2 | const cp = require("child_process"); 3 | const path = require("path"); 4 | const localize = require("./myLocalize.js").localize; 5 | const readline = require("readline"); 6 | 7 | var diagnosticCollection; 8 | 9 | function activate(context) 10 | { 11 | diagnosticCollection = vscode.languages.createDiagnosticCollection('harbour'); 12 | context.subscriptions.push(diagnosticCollection); 13 | 14 | vscode.workspace.onDidOpenTextDocument(validate,undefined, context.subscriptions); 15 | vscode.workspace.onDidSaveTextDocument(validate,undefined, context.subscriptions); 16 | vscode.workspace.onDidCloseTextDocument(removeValidation,undefined, context.subscriptions); 17 | if(vscode.window.activeTextEditor && vscode.window.activeTextEditor.document) 18 | validate(vscode.window.activeTextEditor.document); 19 | } 20 | 21 | function deactivate() 22 | { 23 | diagnosticCollection.dispose(); 24 | } 25 | 26 | var valRegEx = /^\r?(?:([^\(]*)\((\d+)\)\s+)?(Warning|Error)\s+([^\r\n]*)/ 27 | var lineContRegEx = /;(\s*(\/\/|&&|\/\*))?/ 28 | function validate(textDocument) 29 | { 30 | if(textDocument.languageId !== 'harbour' ) 31 | return; 32 | var section = vscode.workspace.getConfiguration('harbour'); 33 | if(!section.validating) 34 | return; 35 | var args = ["-s", "-q0", "-m", "-n0", "-w"+section.warningLevel, textDocument.fileName ]; 36 | var file_cwd = path.dirname(textDocument.fileName); 37 | for (var i = 0; i < section.extraIncludePaths.length; i++) { 38 | var pathVal = section.extraIncludePaths[i]; 39 | if(pathVal.indexOf("${workspaceFolder}")>=0) { 40 | pathVal=pathVal.replace("${workspaceFolder}",file_cwd) 41 | } 42 | args.push("-I"+pathVal); 43 | } 44 | args = args.concat(section.extraOptions.split(" ").filter(function(el) {return el.length != 0 || el=="-ge1"})); 45 | var diagnostics = {}; 46 | diagnostics[textDocument.fileName] = []; 47 | var doneSubjects = {}; 48 | function parseLine(subLine) 49 | { 50 | var r = valRegEx.exec(subLine); 51 | if(r) 52 | { 53 | if(!r[1]) r[1]=""; 54 | var lineNr = r[2]? parseInt(r[2])-1 : 0; 55 | var subject = r[4].match(/'([^']+)'/g); 56 | if(subject && subject.length>1 && subject[1].indexOf("(")>=0) 57 | { 58 | var nSub = subject[1].match(/\(([0-9]+)\)/); 59 | if(nSub) 60 | { 61 | lineNr = parseInt(nSub[1])-1; 62 | } 63 | } 64 | if(subject && subject.length>0) { 65 | if(lineNr in doneSubjects && doneSubjects[lineNr].indexOf(subject[0])>=0) return 66 | if(!(lineNr in doneSubjects)) doneSubjects[lineNr]=[]; 67 | doneSubjects[lineNr].push(subject[0]); 68 | } 69 | var line = textDocument.lineAt(lineNr) 70 | if(!(r[1] in diagnostics)) 71 | { 72 | diagnostics[r[1]] = []; 73 | } 74 | var putAll = true; 75 | if(subject) 76 | { 77 | var m; 78 | subject[0] = subject[0].substring(1,subject[0].length-1) 79 | var rr = new RegExp('\\b'+subject[0].replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&")+'\\b',"ig") 80 | var testLine = line; 81 | do { 82 | while(m=rr.exec(testLine.text)) 83 | { 84 | putAll = false; 85 | var diag = new vscode.Diagnostic(new vscode.Range(lineNr,m.index,lineNr,m.index+subject[0].length), r[4], 86 | r[3]=="Warning"? vscode.DiagnosticSeverity.Warning : vscode.DiagnosticSeverity.Error) 87 | if(r[4].indexOf("not used")>0) { 88 | diag.tags = [vscode.DiagnosticTag.Unnecessary]; 89 | } 90 | diagnostics[r[1]].push(diag) 91 | } 92 | if(lineNr==0) break; 93 | testLine = textDocument.lineAt(--lineNr); 94 | } while(lineContRegEx.test(testLine.text)) 95 | } 96 | if(putAll) 97 | diagnostics[r[1]].push(new vscode.Diagnostic(line.range, r[4], r[3]=="Warning"? 1 : 0)) 98 | } 99 | } 100 | var process = cp.spawn(section.compilerExecutable,args, { cwd: file_cwd }); 101 | process.on("error", e=> 102 | { 103 | vscode.window.showWarningMessage(localize("harbour.validation.NoExe",section.compilerExecutable)); 104 | }); 105 | var reader = readline.createInterface({ input: process.stderr}) 106 | reader.on("line",d=>parseLine(d)); 107 | //process.stderr.on('data', (v) => parseData(v,true)); 108 | //process.stdout.on('data', (v) => parseData(v,false)); 109 | process.on("exit",function(code) 110 | { 111 | for (var file in diagnostics) { 112 | if (diagnostics.hasOwnProperty(file)) { 113 | var infos = diagnostics[file]; 114 | diagnosticCollection.set(vscode.Uri.file(file), infos); 115 | } 116 | } 117 | }); 118 | } 119 | 120 | function removeValidation(textDocument) 121 | { 122 | diagnosticCollection.delete(textDocument.uri); 123 | } 124 | 125 | exports.activate = activate; 126 | exports.deactivate = deactivate; 127 | -------------------------------------------------------------------------------- /client/src/extension.js: -------------------------------------------------------------------------------- 1 | const vscode = require('vscode'); 2 | const path = require('path'); 3 | const client = require('vscode-languageclient'); 4 | const fs = require("fs"); 5 | const validation = require('./validation.js'); 6 | const decorator = require('./decorator.js'); 7 | const docCreator = require('./docCreator.js'); 8 | const taskProvider = require('./taskProvider.js'); 9 | const net = require("net"); 10 | const formatEditor = require("./formatEditor.js"); 11 | 12 | function activate(context) { 13 | vscode.languages.setLanguageConfiguration('harbour', { 14 | indentationRules: { 15 | increaseIndentPattern: /^\s*((?:(?:static|init|exit)\s+)?(?:proc(?:e(?:d(?:u(?:r(?:e)?)?)?)?)?|func(?:t(?:i(?:o(?:n)?)?)?)?)|class(?!\s*(?:var|data|method))|method|if|else(?:if)?|for|if|try|case|otherwise|(?:do\s+)?while|switch|begin)\b/i, 16 | decreaseIndentPattern: /^\s*(end\s*([a-z]*)?|next|else|elseif|return)\b/i, 17 | indentNextLinePattern: /;((?:\/\/|&&).*)?$/ 18 | } 19 | }); 20 | validation.activate(context); 21 | 22 | var serverModuleDbg = context.asAbsolutePath(path.join('..','server')); 23 | var serverModule = context.asAbsolutePath('server'); 24 | var debugOptions = { execArgv: ["--nolazy", "--inspect-brk=21780"] }; 25 | var serverOptions = { 26 | run : { module: serverModule, transport: client.TransportKind.ipc }, 27 | debug: { module: serverModuleDbg, transport: client.TransportKind.ipc , options: debugOptions } 28 | } 29 | var clientOptions = { 30 | documentSelector: ['harbour'], 31 | synchronize: { 32 | configurationSection: ['harbour','search','editor'] 33 | } 34 | } 35 | var cl = new client.LanguageClient('HarbourServer', 'Harbour Server', serverOptions, clientOptions); 36 | cl.registerProposedFeatures() 37 | context.subscriptions.push(cl.start()); 38 | vscode.commands.registerCommand('harbour.getDbgCode', () => { getDbgCode(context); }) 39 | vscode.commands.registerCommand("harbour.debugList", DebugList) 40 | vscode.commands.registerCommand("harbour.setupCodeFormat", () => { formatEditor.showEditor(context); }) 41 | decorator.activate(context,cl); 42 | docCreator.activate(context,cl); 43 | taskProvider.activate(); 44 | // https://code.visualstudio.com/updates/v1_30#:~:text=Finalized%20Debug%20Adapter%20Tracker%20API 45 | /*vscode.debug.registerDebugAdapterTrackerFactory('harbour-dbg', { 46 | createDebugAdapterTracker( ) { 47 | return { 48 | onWillReceiveMessage: m => console.log(`> ${m.seq} - C ${m.command} - ${m.arguments? JSON.stringify(m.arguments).substring(0,50) : "no-args"}`), 49 | onDidSendMessage: m => console.log(`< ${m.seq} - ${m.command ? "C" : "E"} ${m.command ? m.command : m.event} - ${m.body? JSON.stringify(m.body).substring(0,50) : 'no-body'}`) 50 | }; 51 | } 52 | });*/ 53 | } 54 | 55 | function DebugList(args) { 56 | return new Promise((resolve,reject) => { 57 | var picks = vscode.window.createQuickPick(); 58 | picks.placeholder = "select the process to attach with" 59 | picks.busy=true; 60 | picks.items=[]; 61 | var port = args.port? args.port :6110; 62 | var server = net.createServer(socket => { 63 | socket.on("data", data=> { 64 | try { 65 | while(true) { 66 | var lines = data.toString().split("\r\n"); 67 | if(lines.length<2) {//todo: check if they arrive in 2 tranches. 68 | break; 69 | } 70 | var clPath = path.basename(lines[0],path.extname(lines[0])).toLowerCase(); 71 | var processId = parseInt(lines[1]); 72 | if(args.program && args.program.length>0) { 73 | var exeTarget = path.basename(args.program,path.extname(args.program)).toLowerCase(); 74 | if(clPath!=exeTarget) break; 75 | } 76 | if(!picks.items.find((v)=>v.process==processId)) 77 | picks.items=picks.items.concat([{label:clPath+":"+processId, process:processId }]) 78 | break; 79 | } 80 | } catch(ex) { } 81 | socket.write("NO\r\n") 82 | socket.end(); 83 | }); 84 | }).listen(port); 85 | picks.onDidAccept(()=>{ 86 | picks.hide(); 87 | }); 88 | picks.onDidHide(()=> { 89 | server.close(); 90 | if(picks.selectedItems.length>0) { 91 | resolve(picks.selectedItems[0].process.toString()); 92 | } else 93 | resolve(""); 94 | }) 95 | 96 | picks.show();; 97 | }); 98 | } 99 | 100 | function getDbgCode(context) { 101 | fs.readFile(path.join(context.extensionPath,'extra','dbg_lib.prg'),(err,data) => 102 | { 103 | if(!err) 104 | vscode.workspace.openTextDocument({ 105 | content: data.toString(), 106 | language: 'harbour'}).then(doc => { 107 | vscode.window.showTextDocument(doc); 108 | }) 109 | }); 110 | } 111 | 112 | function deactivate() { 113 | validation.deactivate(); 114 | } 115 | 116 | exports.activate = activate; 117 | exports.deactivate = deactivate; 118 | 119 | -------------------------------------------------------------------------------- /client/package.nls.json: -------------------------------------------------------------------------------- 1 | { 2 | "harbour.validating": "Every time a file is opened or saved the executable is called to validate the code(Lint)", 3 | "harbour.compilerExecutable": "complete path of the harbour executable", 4 | "harbour.extraOptions": "other options to pass to harbour compiler", 5 | "harbour.extraIncludePaths": "Extra include paths to use during the compilation (for include)", 6 | "harbour.warningLevel": "Warning level", 7 | "harbour.decorator": "Enables the decoration of correspondents if/endif, for/next, while/endwhile, etc etc", 8 | "harbour.workspaceDepth": "Depth of sub-folder of workspace where looking for sources", 9 | "harbour.initialConfigurations": "Launch Program", 10 | "harbour.configurationSnippetsDesc": "Start and debug a harbour program with debug symbols", 11 | "harbour.configurationSnippets": "Launch ${2:Program}", 12 | "harbour.configurationSnippetsDesc2": "Debug a running harbour program with debug symbols specifying the process", 13 | "harbour.configurationSnippets2": "Attach ${2:Program}", 14 | "harbour.configurationSnippetsDesc3": "Debug a running harbour program with debug symbols, choosing the process", 15 | "harbour.configurationSnippets3": "Select process and attach", 16 | "harbour.launch.workspaceRoot": "Attribute 'workspaceRoot' is deprecated, use 'sourcePaths' instead", 17 | "harbour.launch.program": "Absolute path of executable", 18 | "harbour.launch.workingDir": "Working directory", 19 | "harbour.launch.arguments": "Arguments to pass to executable", 20 | "harbour.launch.stopOnEntry": "Automatically stop after launch", 21 | "harbour.launch.sourcePaths": "Directories where search for source files", 22 | "harbour.launch.terminalType": "Type of terminal to use to run the application", 23 | "harbour.launch.port": "Port used by debugger, It must be the same value of DBG_PORT in debugger code line 6", 24 | "harbour.dbgError1": "Unable to start {0} in {1}, check that all path exists", 25 | "harbour.prematureExit": "Exit early with code {0}", 26 | "harbour.dbgNoModule": "Module not found", 27 | "harbour.dbgNoLine": "Invalid line", 28 | "harbour.dbgError.all": "Stop on all errors", 29 | "harbour.dbgError.notSeq": "Stop only on out-of-sequence errors", 30 | "harbour.validation.NoExe": "Unable to start {0}, check the 'harbour.compilerExecutable' value", 31 | "harbour.dbgCodeCmd": "Harbour: Get debugger code", 32 | "harbour.task.input": "file to compile", 33 | "harbour.task.output": "output type", 34 | "harbour.task.ctype": "Type of C output", 35 | "harbour.task.HBMK2.input": "file to build", 36 | "harbour.task.HBMK2.output": "output file name", 37 | "harbour.task.HBMK2.extraArgs": "Free extra arguments", 38 | "harbour.task.HBMK2.debug": "if true includes the libray for debugging on VSCode", 39 | "harbour.task.HBMK2.platform": "default target platform", 40 | "harbour.task.HBMK2.compiler": "override C compiler", 41 | "harbour.task.HBMK2.setupBatch": "A batch or a shell script that set up the hbmk2 environment", 42 | "harbour.task.portableName": "Generate Harbour Portable Object (hrb)", 43 | "harbour.task.cCodeName": "Generate C file", 44 | "harbour.task.HBMK2.provideName": "build {0}", 45 | "harbour.task.HBMK2.provideName2": "build current file", 46 | "harbour.task.HBMK2.provideName3": "build current file for debugging", 47 | "harbour.task.HBMK2.errorBatch": "Unable to start setup batch", 48 | "harbour.task.HBMK2.setup": "setting up the enviroment...", 49 | "harbour.task.HBMK2.start": "Start HBMK2", 50 | "harbour.attach.process": "process id to connect", 51 | "harbour.formatter.cmd": "Harbour: setup code style", 52 | "harbour.formatter.title": "Harbour: setup code style", 53 | "harbour.formatter.indent": "Indents", 54 | "harbour.formatter.indent.funcBody": "Indent function body", 55 | "harbour.formatter.indent.local": "Indent local, private etc inside function body", 56 | "harbour.formatter.indent.logical": "Indent after if", 57 | "harbour.formatter.indent.cycle": "Indent after for, while", 58 | "harbour.formatter.indent.switch": "Indent after switch, do case", 59 | "harbour.formatter.indent.case": "Indent after case body", 60 | "harbour.formatter.replace": "Replacements", 61 | "harbour.formatter.replace.not": "replace .not. with !", 62 | "harbour.formatter.replace.asterisk": "replace start line comment", 63 | "harbour.formatter.replace.amp": "replace comment &&", 64 | "harbour.formatter.case": "Case", 65 | "harbour.formatter.case.commands": "case of command", 66 | "harbour.formatter.case.knowFunction": "case of know function", 67 | "harbour.formatter.case.unknowFunction": "case of unknow function", 68 | "harbour.formatter.case.directives": "case of precompile directives #", 69 | "harbour.formatter.enum.value.ignore": "ignore", 70 | "harbour.formatter.enum.value.use": "use {0}", 71 | "harbour.formatter.enum.value.upperCase": "UPPER CASE", 72 | "harbour.formatter.enum.value.lowerCase": "lower case", 73 | "harbour.formatter.enum.value.title": "Title", 74 | "harbour.formatter.enum.value.asDefine": "as definition" 75 | } 76 | -------------------------------------------------------------------------------- /client/src/formatEditor.js: -------------------------------------------------------------------------------- 1 | const vscode = require('vscode'); 2 | const fs = require("fs"); 3 | const path = require('path'); 4 | const localize = require("./myLocalize.js").localize; 5 | 6 | function escapeHTML(html) { 7 | html = html.replace(/&/g, "&"); 8 | html = html.replace(//g, ">"); 10 | html = html.replace(/"/g, """); 11 | html = html.replace(/'/g, "'"); 12 | return html 13 | } 14 | 15 | /** @param {vscode.ExtensionContext} context */ 16 | function showEditor(context) { 17 | const panel = vscode.window.createWebviewPanel( 18 | 'harbourFmtEditor', 19 | localize('harbour.formatter.title'), 20 | vscode.ViewColumn.Active, {} 21 | ); 22 | var package = JSON.parse(fs.readFileSync(path.join(context.extensionPath,"package.json"), 'utf8')) 23 | package = package.contributes.configuration.properties; 24 | var section = vscode.workspace.getConfiguration('harbour').formatter; 25 | // And set its HTML content 26 | const localResources = vscode.Uri.file(path.join(context.extensionPath,"formatter-settings")); 27 | const codiconsUri = vscode.Uri.joinPath(context.extensionUri, 'node_modules', '@vscode/codicons', 'dist'); 28 | panel.webview.options = { 29 | localResourceRoots: [localResources,codiconsUri], 30 | enableScripts: true 31 | } 32 | const baseUri = panel.webview.asWebviewUri(localResources); 33 | const cspSource = panel.webview.cspSource; 34 | var debug = typeof v8debug === 'object'; 35 | var html = ` 36 | 40 | 41 | Cat Coding 42 | 43 | 44 | 45 | 46 | `; 47 | 48 | for(let subZone in section) { 49 | let k0 = `harbour.formatter.${subZone}`; 50 | html += `

${localize(k0)}

` 51 | for(let zone in section[subZone]) { 52 | let cnf = section[subZone][zone]; 53 | let k = k0+`.${zone}`; 54 | let cfg = package[k]; 55 | html += `` 87 | } 88 | html += "
" 89 | } 90 | html += `
`; 91 | html += ``; 92 | panel.webview.onDidReceiveMessage((m)=> onEditorMessage(m)); 93 | panel.webview.html =html; 94 | } 95 | 96 | function onEditorMessage(m) { 97 | switch (m.command) { 98 | case "currConfig": 99 | updateConfig(m.value) 100 | break; 101 | 102 | default: 103 | break; 104 | } 105 | } 106 | 107 | function updateConfig(readedValue) { 108 | var currValue = vscode.workspace.getConfiguration('harbour'); 109 | var section = readedValue.formatter; 110 | for(let subZone in section) { 111 | let k0 = `formatter.${subZone}`; 112 | for(let zone in section[subZone]) { 113 | let k = k0+`.${zone}`; 114 | var ins = currValue.inspect(k); 115 | var rv = section[subZone][zone]; 116 | if(rv==ins.defaultValue) 117 | currValue.update(k,undefined); 118 | else 119 | currValue.update(k,rv); 120 | } 121 | } 122 | } 123 | 124 | exports.showEditor = showEditor; 125 | -------------------------------------------------------------------------------- /client/package.nls.es.json: -------------------------------------------------------------------------------- 1 | { 2 | "harbour.validating": "Cada vez que se abre o se salva un archivo, se ejecuta Harbour para validar el código", 3 | "harbour.compilerExecutable": "El camino completo hacia el ejecutable Harbour", 4 | "harbour.extraOptions": "Otras opciones para pasar al compilador de Harbour", 5 | "harbour.extraIncludePaths": "Otras rutas para incluir durante la compilación (para incluir)", 6 | "harbour.warningLevel": "Nivel de alertas", 7 | "harbour.decorator": "Active el resaltado de los correspondientes if / endif, for / next, while / endwhile, etc. etc", 8 | "harbour.workspaceDepth": "Profundidad de la subcarpeta del espacio de trabajo donde se buscan código fuente", 9 | "harbour.initialConfigurations": "Iniciar el programa", 10 | "harbour.configurationSnippetsDesc": "Iniciar y Depurar un programa Harbour con símbolos de depuración", 11 | "harbour.configurationSnippets": "Iniciar ${2:programa}", 12 | "harbour.configurationSnippetsDesc2": "Depurar un programa Harbour en ejecución con símbolos de depuración, especificando el nombre del proceso", 13 | "harbour.configurationSnippets2": "Unir ${2:Program}", 14 | "harbour.configurationSnippetsDesc3": "Depurar un programa Harbour en ejecución con símbolos de depuración, eligiendo el proceso", 15 | "harbour.configurationSnippets3": "Elige un proceso y conéctate", 16 | "harbour.launch.workspaceRoot": "el atributo 'workspaceRoot' está en desuso, use 'sourcePaths'", 17 | "harbour.launch.program": "Ruta absoluta del ejecutable", 18 | "harbour.launch.workingDir": "Directorio de trabajo", 19 | "harbour.launch.arguments": "Argumentos a pasars al ejecutable", 20 | "harbour.launch.stopOnEntry": "Detener automáticamente después de comenzar", 21 | "harbour.launch.sourcePaths": "Rutas para buscar código fuente", 22 | "harbour.launch.terminalType": "Tipo de terminal a utilizar para ejecutar la aplicación", 23 | "harbour.launch.port": "Puerto utilizado por el depurador, debe ser el mismo valor de DBG_PORT en código del depurador línea 6", 24 | "harbour.dbgError1": "No se puede iniciar {0} en {1}, compruebe que existen todas las rutas", 25 | "harbour.prematureExit": "Sal antes con el código {0}", 26 | "harbour.dbgNoModule": "módulo no encontrado", 27 | "harbour.dbgNoLine": "lí­nea inválida", 28 | "harbour.dbgError.all": "Parar en todos los errores", 29 | "harbour.dbgError.notSeq": "Parar sólo en errores fuera de secuencia", 30 | "harbour.validation.NoExe": "No se puede iniciar {0}, verifique el valor de 'harbour.compilerExecutable'", 31 | "harbour.dbgCodeCmd": "Harbour: Consigue el código del depurador", 32 | "harbour.task.input": "archivo para compilar", 33 | "harbour.task.output": "tipo de output", 34 | "harbour.task.ctype": "tipo de output C", 35 | "harbour.task.HBMK2.input": "archivo HBP para build", 36 | "harbour.task.HBMK2.extraArgs": "Parámetros libres adicionales", 37 | "harbour.task.HBMK2.debug": "Si es true, también se incluirá el código de depuración para VSCode", 38 | "harbour.task.HBMK2.platform": "Selecciona la CPU de destino", 39 | "harbour.task.HBMK2.compiler": "sustituye la detección automática del compilar de C", 40 | "harbour.task.HBMK2.setupBatch": "Un script por lotes o shell que establece el entorno para ejecutar HBMK2", 41 | "harbour.task.portableName": "Generar un archivo Harbour Portable Object (hrb)", 42 | "harbour.task.cCodeName": "Generar un archivo C", 43 | "harbour.task.HBMK2.provideName": "Build {0}", 44 | "harbour.task.HBMK2.errorBatch": "No se puede iniciar el lote de configuración", 45 | "harbour.task.HBMK2.setup": "Configurando el entorno...", 46 | "harbour.task.HBMK2.start": "Inicie HBMK2", 47 | "harbour.attach.process": "ID del proceso para conectarse", 48 | "harbour.formatter.cmd": "Harbour: configuración estilo de código", 49 | "harbour.formatter.title": "Harbour: configuración estilo de código", 50 | "harbour.formatter.indent": "Sangrados", 51 | "harbour.formatter.indent.funcBody": "Sangría cuerpo de la función", 52 | "harbour.formatter.indent.local": "Sangría local, private, etc. dentro del cuerpo de la función", 53 | "harbour.formatter.indent.logical": "Sangría después if", 54 | "harbour.formatter.indent.cycle": "Sangría después for, while", 55 | "harbour.formatter.indent.switch": "Sangría después switch, do case", 56 | "harbour.formatter.indent.case": "Indent after case body", 57 | "harbour.formatter.replace": "Reemplazos", 58 | "harbour.formatter.replace.not": "Reemplazar .not. por !", 59 | "harbour.formatter.replace.asterisk": "Reemplazar comentario de inicio línea", 60 | "harbour.formatter.replace.amp": "Reemplazar comentario &&", 61 | "harbour.formatter.case": "Mayúsculas y minúsculas", 62 | "harbour.formatter.case.commands": "Mayúsculas de los comandos", 63 | "harbour.formatter.case.knowFunction": "Mayúsculas de conocidas funciones", 64 | "harbour.formatter.case.unknowFunction": "Mayúsculas de desconocida funciones", 65 | "harbour.formatter.case.directives": "Mayúsculas de directivas de precompilación #", 66 | "harbour.formatter.enum.value.ignore": "ignorar", 67 | "harbour.formatter.enum.value.use": "usas {0}", 68 | "harbour.formatter.enum.value.upperCase": "TODO MAYÚSCULAS", 69 | "harbour.formatter.enum.value.lowerCase": "todo minúsculas", 70 | "harbour.formatter.enum.value.title": "Capital primero", 71 | "harbour.formatter.enum.value.asDefine": "como definición" 72 | } 73 | -------------------------------------------------------------------------------- /test/dbg_test.prg: -------------------------------------------------------------------------------- 1 | /// Add some line empty 2 | /// it is necessary to test the offset 3 | /// of modules inside the harbourd debug 4 | /// system. 5 | /// no code unltil line 14 6 | /// include, static are not code :) 7 | 8 | ///OK 9 | #include 10 | #include 11 | 12 | STATIC TestStatic_file 13 | MEMVAR t_aGlobal 14 | 15 | class oggetto 16 | protected: 17 | DATA soo INIT {1=>"one", 2=>"two",3=>"three"} 18 | exported: 19 | DATA noo AS NUMERIC 20 | DATA ioo, rdefrr 21 | classDATA newData INIT "newData" 22 | METHOD aBitmap( n ) INLINE ( If( empty(n) .or. (n > 0 .and. n <= 10), 5 , nil ) ) 23 | METHOD otherMedhod() 24 | endclass 25 | 26 | METHOD otherMedhod() CLASS oggetto 27 | local test:= ::soo 28 | if empty(::soo) 29 | ::soo := "nil" 30 | endif 31 | return ::soo + " " + str(::noo) 32 | 33 | class figlio inherit oggetto 34 | data dFiglio INIT {^ 2018/12/20 } 35 | data zz INIT "Antonino Perricone" 36 | constructor new() 37 | endclass 38 | 39 | METHOD new() class figlio 40 | ::soo := {"old"=>::soo,4=>"four",5=>"five",6=>"six"} 41 | return Self 42 | 43 | PROC MyErrorBlock(e) 44 | ? "ERRORISSIMO " 45 | 46 | func test(a,b) 47 | return a+b 48 | 49 | proc main( ) 50 | local i as numeric, j := Do("test",3,4) 51 | local c := figlio():New() 52 | local bs := "{|a,c| QOut(c:otherMedhod()) }" 53 | loca b := {|c| QOut(c:otherMedhod()) } 54 | STAT TestStatic, TestStatic2 55 | //memvar i,j 56 | //ErrorBlock({|e| MyErrorBlock(e) }) 57 | TestStatic_File := oggetto():New() //{1,1,2,3,5,8,13,21,34,55,89,144} 58 | TestStatic_File:ioo := {"prova"=>{"val1"=>"val","val2"=>"valval"}} 59 | c:ioo := {"prova"=>{"val1"=>"val","val2"=>"valval"}} 60 | TestStatic2 := {1,1,2,3,5,8,13,21,34,55,89,144} 61 | ? "S",valtype(TestStatic),valtype(TestStatic2) 62 | c:newData := {1,2,3,4,5} 63 | c:ioo := oggetto():New() 64 | c:ioo:newData := {6,7,8,9,10} 65 | hb_SetMacro( HB_SM_HARBOUR, .T. ) 66 | for i:=1 to 10 67 | j:=i*2 68 | ? "i vale &( str(i) ), j vale &(j), formula &(i*2)" 69 | next 70 | AltD() 71 | ? "Perry" 72 | //begin sequence with {|| QOut("eh") } 73 | // eval(&bs,"",c) 74 | //end sequence 75 | //eval(b,"",c) 76 | AltraFunzione() 77 | ? i:=2 78 | ? i 79 | eval(&("{|| TestLib() }")) 80 | testLotParameter(1,[2,3],{4,5},"6,7",'A,2',; 81 | "PROVA,TEST","Pippo",{6,5,3,2,1},234 ; 82 | ) 83 | for i:=1 to 10 84 | ? i 85 | next 86 | return 87 | 88 | // Another function, it is to test the comment with // 89 | func AltraFunzione( ) 90 | local p := "sei fuori" 91 | local a := {{'ciao'=>'belli'},{20,10},"AAA"} 92 | memvar test,test2,hh 93 | public test := {"non io"} 94 | public hh := {'pp'=>3,'pi'=>3.14,4=>{1,2}} 95 | private test2 := {"altro"} //unused 96 | a[1,{^ 2018/12/22 }] := 'date with expressions' 97 | Called() 98 | ? p 99 | ? "più righe" 100 | ? "per provare" 101 | return a 102 | 103 | * Called by who? 104 | proc Called() 105 | memvar test2 106 | local timeStamp1 := {^ 1978/11/06 17:10:23.324 } 107 | //local date := d"2017/05/23" 108 | //local timeStamp2 := t"14:00" 109 | local test := "1978-06-11 17:10:23.324" 110 | static provaStat := 611 111 | ? test2 112 | 113 | NOTE Testing a lib 114 | proc TestLib() 115 | LOCAL bb := {|| TestLibInside()} 116 | FakeLib(bb) 117 | 118 | /* Multi line comment 119 | Test with empty line too 120 | */ 121 | proc TestLibInside() 122 | LOCAL bb := {|a| TestLibInside2(a)} 123 | FakeLib(bb,4) 124 | 125 | * Multi line with asterisc 126 | * Another line 127 | proc TestLibInside2(v) 128 | LOCAL a := "a" 129 | LOCAL arr := {4,3,2,1} 130 | ? a,v 131 | 132 | proc testLotParameter(a/*a param*/,b/*b param*/,c/*c param*/,; 133 | d/*d param*/,e/*e param*/,f ; //f param 134 | ,g,h,i,j,k,l,m,n,o,p,q,r) 135 | return 136 | 137 | #pragma -B- 138 | proc FakeLib(bBlock,par) 139 | eval(bBlock, par) 140 | 141 | #pragma -B+ 142 | 143 | /* notes from src/debug/debugger.prg: 144 | __DbgEntry ACTIVATE -> breakpoint arrived, 145 | default there is a breakpoint at startup, without 'go' next line is a breakpoint, 146 | if 'trace' next line even if is inside a called procedure is a breakpoint 147 | uParam1 --> debugInfo 148 | 149 | __dbgInvokeDebug() if .T. it stopped is caused by an AltD() 150 | 151 | 152 | Commands: 153 | __dbgSetGo(debugInfo) --> play the program 154 | __dbgSetTrace(debugInfo) --> set a breakpoint in the first line of the next called procedure 155 | __dbgSetCBTrace(debugInfo,lCB) --> trace includes codeblock too 156 | __dbgAddBreak(debugInfo,file,line) --> 157 | __dbgIsBreak(debugInfo,file,line) --> return id of breakpoint if setted 158 | __dbgDelBreak(debugInfo, id) 159 | __dbgAddWatch(debugInfo,cExpr,lTracePoint) --> 160 | __mvDbgInfo( HB_MV_PUBLIC|HB_MV_PRIVATE ) --> returns number of public/private variable 161 | __mvDbgInfo( HB_MV_PUBLIC|HB_MV_PRIVATE, idx, @cName ) -> returns the index of idx public variable (Sets its name in cName) 162 | __mvDbgInfo( HB_MV_PRIVATE_LOCAL, lv ) -> returns number of private variable at level lv of stack 163 | __dbgProcLevel() --> return the current level (len of stack?) 164 | 165 | gets of value of variable 166 | SWITCH aVar[ HB_DBG_VAR_TYPE ] 167 | CASE "G" ; RETURN __dbgVMVarGGet( aVar[ HB_DBG_VAR_FRAME ], aVar[ HB_DBG_VAR_INDEX ] ) 168 | CASE "L" ; RETURN __dbgVMVarLGet( __dbgProcLevel() - aVar[ HB_DBG_VAR_FRAME ], aVar[ HB_DBG_VAR_INDEX ] ) 169 | CASE "S" ; RETURN __dbgVMVarSGet( aVar[ HB_DBG_VAR_FRAME ], aVar[ HB_DBG_VAR_INDEX ] ) 170 | ENDSWITCH 171 | 172 | */ 173 | 174 | 175 | -------------------------------------------------------------------------------- /client/package.nls.it.json: -------------------------------------------------------------------------------- 1 | { 2 | "harbour.validating": "Ogni volta che un file è aperto o salvato, viene eseguito harbour per validare il codice (lint)", 3 | "harbour.compilerExecutable": "Il percorso completo dell'eseguibile di harbour", 4 | "harbour.extraOptions": "altre opzioni da passare al compilatore harbour", 5 | "harbour.extraIncludePaths": "Altre percorsi da includere durante la compilazione (per include)", 6 | "harbour.warningLevel": "livello degli avvisi", 7 | "harbour.decorator": "Abilita l'evidenziazione dei corrispettivi if/endif, for/next, while/endwhile, ecc ecc", 8 | "harbour.workspaceDepth": "Profondita delle sottocartelle del workspace dove cercare i sorgenti", 9 | "harbour.initialConfigurations": "Avvia il programma", 10 | "harbour.configurationSnippetsDesc": "Avvia ed esegue il debug un programma harbour con i simboli di debug", 11 | "harbour.configurationSnippets": "Avvia ${2:programma}", 12 | "harbour.configurationSnippetsDesc2": "Esegue il debug di un programma harbour con i simboli di debug già in esecuzione, specificando il nome del processo", 13 | "harbour.configurationSnippets2": "Collega ${2:Program}", 14 | "harbour.configurationSnippetsDesc3": "Esegue il debug di un programma harbour con i simboli di debug già in esecuzione, scegliendo il processo", 15 | "harbour.configurationSnippets3": "Scegli un processo e collegati", 16 | "harbour.launch.workspaceRoot": "l'attributo 'workspaceRoot' è in disuso, usa 'sourcePaths'", 17 | "harbour.launch.program": "Percorso assoluto dell'eseguibile", 18 | "harbour.launch.workingDir": "Directory di lavoro", 19 | "harbour.launch.arguments": "Argomenti da passare all'eseguibile", 20 | "harbour.launch.stopOnEntry": "Fermati automaticamente dopo l'avvio", 21 | "harbour.launch.sourcePaths": "Percorsi dove cercare i sorgenti", 22 | "harbour.launch.terminalType": "Tipo di terminale da usare per avviare l'applicazione", 23 | "harbour.launch.port": "Porta usata dal debugger, deve essere uguale a DBG_PORT nel codice del debugger linea 6", 24 | "harbour.dbgError1": "Incapace di avviare {0} in {1}, controlla che tutti i percorsi esistano", 25 | "harbour.prematureExit": "Uscito anticipatamente con codice {0}", 26 | "harbour.dbgNoModule": "modulo non trovato", 27 | "harbour.dbgNoLine": "linea non valida", 28 | "harbour.dbgError.all": "Fermati su tutti gli errori", 29 | "harbour.dbgError.notSeq": "Fermati sugli errori fuori dalle sequenze", 30 | "harbour.validation.NoExe": "Incapace di avviare {0}, controlla il valore di 'harbour.compilerExecutable'", 31 | "harbour.dbgCodeCmd": "Harbour: Ottieni il codice del debugger", 32 | "harbour.task.input": "file da compilare", 33 | "harbour.task.output": "tipo di output", 34 | "harbour.task.ctype": "tipo di output C", 35 | "harbour.task.HBMK2.input": "file HBP da buildare", 36 | "harbour.task.HBMK2.output": "file di uscita", 37 | "harbour.task.HBMK2.extraArgs": "Parametri extra liberi", 38 | "harbour.task.HBMK2.debug": "Se true, verrà incluso anche il codice di debug per VSCode", 39 | "harbour.task.HBMK2.platform": "Piattaforma di destinazione di default", 40 | "harbour.task.HBMK2.compiler": "Sovrascrive la selezione automatica del compilatore C", 41 | "harbour.task.HBMK2.setupBatch": "Un batch o uno script shell che imposta l'ambiente dove eseguire HBMK2", 42 | "harbour.task.portableName": "Genera un Harbour Portable Object (hrb)", 43 | "harbour.task.cCodeName": "Genera un file C", 44 | "harbour.task.HBMK2.provideName": "Builda {0}", 45 | "harbour.task.HBMK2.provideName2": "Builda file corrente", 46 | "harbour.task.HBMK2.provideName3": "Builda file corrente per il debugging", 47 | "harbour.task.HBMK2.errorBatch": "Incapace di avviare il batch di setup", 48 | "harbour.task.HBMK2.setup": "Impostando l'ambiente...", 49 | "harbour.task.HBMK2.start": "Avvio HBMK2", 50 | "harbour.attach.process": "ID del processo a cui connettersi", 51 | "harbour.formatter.cmd": "Harbour: Impostazione stile codice", 52 | "harbour.formatter.title": "Harbour: Impostazione stile codice", 53 | "harbour.formatter.indent": "Rientri", 54 | "harbour.formatter.indent.funcBody": "Rientra corpo funzione", 55 | "harbour.formatter.indent.local": "Rientra local, private ecc dentro il corpo della funzione", 56 | "harbour.formatter.indent.logical": "Rientra dopo if", 57 | "harbour.formatter.indent.cycle": "Rientra dopo for, while", 58 | "harbour.formatter.indent.switch": "Rientra dopo switch, do case", 59 | "harbour.formatter.indent.case": "Rientra del corpo del case", 60 | "harbour.formatter.replace": "Sostituzioni", 61 | "harbour.formatter.replace.not": "Sostituisci .not. con !", 62 | "harbour.formatter.replace.asterisk": "Sostituisci commento di inizio linea", 63 | "harbour.formatter.replace.amp": "Sostituisce commento &&", 64 | "harbour.formatter.case": "Maiuscole e minuscole", 65 | "harbour.formatter.case.commands": "maiuscolo dei comandi", 66 | "harbour.formatter.case.knowFunction": "maiuscolo delle funzioni conosciute", 67 | "harbour.formatter.case.unknowFunction": "maiuscolo delle funzioni sconosciute", 68 | "harbour.formatter.case.directives": "maiuscolo delle dirrettive di precompilazione #", 69 | "harbour.formatter.enum.value.ignore": "ignora", 70 | "harbour.formatter.enum.value.use": "usa {0}", 71 | "harbour.formatter.enum.value.upperCase": "TUTTO MAIUSCOLO", 72 | "harbour.formatter.enum.value.lowerCase": "tutto minuscolo", 73 | "harbour.formatter.enum.value.title": "Prima maiuscola", 74 | "harbour.formatter.enum.value.asDefine": "come definito" 75 | } 76 | -------------------------------------------------------------------------------- /server/parseHBDoc.js: -------------------------------------------------------------------------------- 1 | var fs = require("fs"); 2 | var readline = require("readline"); 3 | var path = require("path") 4 | 5 | var nTodo = 0; 6 | var docs = []; 7 | var stdMethods = []; 8 | 9 | //walkPath(/home/perry/harbour-src) 10 | walkPath("c:\\harbour") 11 | function walkPath(dir) { 12 | console.debug(`listing: ${dir}...`) 13 | fs.readdir(dir, function (err, ff) { 14 | if (ff == undefined) 15 | return; 16 | for (var i = 0; i < ff.length; i++) { 17 | let completePath = path.join(dir, ff[i]); 18 | let info = fs.statSync(completePath); 19 | const lowerFileName = ff[i].toLocaleLowerCase(); 20 | if(lowerFileName.endsWith(".txt")) { 21 | new parseFile(completePath); 22 | } else if(lowerFileName.endsWith(".hbx")) { 23 | parseHBX(completePath); 24 | } else if(info.isDirectory()) { 25 | walkPath(completePath) 26 | } 27 | } 28 | }); 29 | } 30 | 31 | ////parseDocEn(path.join(hbPath,"doc","en")); // parsed 332 procedures (over 1579 standard) 32 | //parseDocEn(hbPath); //parsed 833 procedures (over 1579 standard) 33 | ////parseDocEn("c:\\harbour\\contrib\\rddads\\doc\\en") 34 | //parseHBX(path.join(hbPath,"include","harbour.hbx")); 35 | 36 | function parseHBX(completePath) 37 | { 38 | nTodo++; 39 | let fileName = path.parse(completePath).name 40 | var ck = /DYNAMIC\s+([_a-z0-9]+)/i; 41 | var reader = readline.createInterface({input:fs.createReadStream(completePath,"utf8")}); 42 | reader.on("line",l => 43 | { 44 | var m = l.match(ck); 45 | if(m && m[1]) 46 | { 47 | stdMethods.push([m[1],fileName]); 48 | } 49 | }); 50 | reader.on("close",() => 51 | { 52 | nTodo--; 53 | if(nTodo==0) 54 | createDoc(); 55 | }) 56 | } 57 | function parseFile(path) 58 | { 59 | //console.debug(`parsing: ${path}...`) 60 | nTodo++; 61 | this.inDoc = false; 62 | this.lastSpecifyLine = "" 63 | this.nFound = 0 64 | this.reader = readline.createInterface({input:fs.createReadStream(path,"utf8")}); 65 | this.reader.on("line",l => this.parseLine(l)); 66 | this.reader.on("close",() => 67 | { 68 | if(this.nFound>0) 69 | console.debug(`${path}: found ${this.nFound} doc...`) 70 | nTodo--; 71 | if(nTodo==0) 72 | createDoc(); 73 | }) 74 | } 75 | 76 | function createDoc() 77 | { 78 | console.debug(`parsed ${docs.length} procedures (over ${stdMethods.length} standard)`) 79 | docs.sort( (a,b) => a.name.localeCompare(b.name)); 80 | stdMethods.sort( (a,b) => a[0].localeCompare(b[0])); 81 | var unDoc = [], extra = []; 82 | var is = 0, id =0; 83 | while(is{if(err) console.error(err);}); 98 | fs.writeFile("src/hbdocs.json", JSON.stringify(docs,undefined,1),(err)=>{if(err) console.error(err);}); 99 | } 100 | 101 | /** 102 | * @param {String} line 103 | */ 104 | parseFile.prototype.parseLine = function(line) 105 | { 106 | line = line.trim() 107 | if(!this.inDoc) 108 | { 109 | this.inDoc = line == "/* $DOC$"; 110 | if(this.inDoc){ 111 | this.nFound++; 112 | this.doc = {}; 113 | this.doc["label"] = undefined; 114 | this.doc["documentation"] = undefined; 115 | this.doc["arguments"] = []; 116 | } 117 | return 118 | } 119 | if(line == "*/") 120 | { 121 | if(this.doc && this.doc.label) 122 | docs.push(this.doc); 123 | this.doc = undefined; 124 | this.inDoc = false; 125 | return; 126 | } 127 | if(line.startsWith("$")) 128 | { 129 | this.lastSpecifyLine = line; 130 | return 131 | } 132 | switch(this.lastSpecifyLine) 133 | { 134 | case "$TEMPLATE$": 135 | this.currentTemplate = line; 136 | break; 137 | case "$ONELINER$": 138 | if(this.doc) 139 | { 140 | if(this.doc["documentation"]) 141 | this.doc["documentation"] += " " +line; 142 | else 143 | this.doc["documentation"] = line; 144 | } 145 | break; 146 | case "$SYNTAX$": 147 | if(this.doc) 148 | { 149 | if(line == "C Prototype") 150 | this.doc = undefined; 151 | else 152 | if(this.doc["label"]) 153 | this.doc["label"] += " " + line; 154 | else 155 | { 156 | var p = line.indexOf("("); 157 | if(p<0) 158 | { 159 | this.doc = undefined; 160 | break; 161 | } 162 | var name = line.substring(0,p) 163 | if(name.indexOf(" ")>0) 164 | { 165 | this.doc = undefined; 166 | break; 167 | } 168 | this.doc["name"] = name; 169 | this.doc["label"] = line; 170 | } 171 | } 172 | break; 173 | case "$ARGUMENTS$": 174 | if(this.doc && line.length>0) { 175 | var ck = /^\s*<[^>]+>/; 176 | var mm = line.match(ck); 177 | if(mm) { 178 | var arg = {label:mm[0],documentation: line.replace(mm[0],"").trim()}; 179 | this.doc.arguments.push(arg); 180 | }else if(this.doc.arguments.length>0) 181 | this.doc.arguments[this.doc.arguments.length-1].documentation += " " + line; 182 | } 183 | break; 184 | case "$RETURNS$": 185 | if(this.doc && line.length>0) { 186 | var ck = /^\s*<[^>]+>/; 187 | var mm = line.match(ck); 188 | if(mm) { 189 | if(this.doc.return) { 190 | this.doc.return.help += " " + line; 191 | } else { 192 | var arg = {name:mm[0], help: line.replace(mm[0],"").trim()}; 193 | this.doc.return = arg; 194 | } 195 | } else { 196 | if(this.doc.return) { 197 | this.doc.return.help += " " + line; 198 | } else 199 | this.doc.return = {name:"", help: line}; 200 | 201 | } 202 | } 203 | break; 204 | } 205 | /* templates: 206 | Document 207 | Function 208 | Command 209 | Procedure 210 | Run time error 211 | Class 212 | */ 213 | } 214 | -------------------------------------------------------------------------------- /client/formatter-settings/code.js: -------------------------------------------------------------------------------- 1 | /* eslint-env browser */ 2 | const vscode = acquireVsCodeApi(); 3 | 4 | var harbour = {}; 5 | function readConfig() { 6 | harbour = {}; 7 | $(".config").each((idx,ele) => { 8 | ele.onchange = onChange; 9 | var depth = ele.id.split("."); 10 | if(depth[0]!="harbour") return; 11 | var obj = {}, currDest = obj; 12 | for(let i=1;i=0; 40 | } 41 | const tabSize = 4; 42 | function naiveFormat(code) { 43 | var lines = code.replace("\r","").split("\n"); 44 | var currTab = 0; 45 | var inFunction = false, inIf = 0; 46 | for(let i=0;i/g, ">") 132 | html = html.replace(/"/g, """) 133 | html = html.replace(/'/g, "'") 134 | html = html.replace(/^(\s*\*.*)$/mg,"$1") 135 | html = html.replace(/((?:&&|\/\/).*)$/mg,"$1") 136 | html = html.replace(/^(\s*#\S+)/ig,"$1"); 137 | html = html.replace(/(func(?:t(?:i(?:o(?:n)?)?)?)?)/ig,"$1"); 138 | html = html.replace(/(proc(?:e(?:d(?:u(?:r(?:e)?)?)?)?)?)/ig,"$1"); 139 | html = html.replace(/(loca(?:l)?)/ig,"$1"); 140 | html = html.replace(/(retu(?:r(?:n)?)?)/ig,"$1"); 141 | html = html.replace(/(if|end|\!|\.not\.|switch|case|exit|for|to|next)/ig,"$1"); 142 | return html; 143 | } 144 | 145 | function setPreview(code) { 146 | $("#preview").html(naiveSyntaxHightlight(naiveFormat(code))); 147 | } 148 | 149 | // remember do NOT put spaces at beginning of lines!! 150 | const code =`#include 151 | 152 | * Fake function for formatting 153 | proc test(p1,p2) 154 | local a,b,n && illogic code 155 | a:=p1+p2 156 | b:=p1-p2 157 | if ! b>0 158 | a*=2 159 | endif 160 | if .not. a>0 161 | b/=2 162 | endif 163 | switch Mod(a,2) 164 | case 0 165 | a/=2 166 | exit 167 | case 1 168 | a*=2 169 | exit 170 | end switch 171 | for n:=1 to b 172 | b+=n 173 | next 174 | return a+b // how much is it? 175 | // end 176 | `; 177 | 178 | function onChange(e) { 179 | console.log("change") 180 | readConfig(); 181 | setPreview(code); 182 | } 183 | 184 | $(()=>{ 185 | readConfig(); 186 | //console.log(harbour); 187 | setPreview(code); 188 | }); 189 | -------------------------------------------------------------------------------- /test/builds.prg: -------------------------------------------------------------------------------- 1 | // hbmk2 builds -gtcgi 2 | 3 | #define CRLF chr(13)+chr(10) 4 | /* 5 | // '#include "err.prg"' + CRLF + ; 6 | "static superman, batman" + CRLF + ; 7 | 8 | "memvar arrow, flash" + CRLF + ; 9 | "" + CRLF + ; 10 | "class oggetto" + CRLF + ; 11 | " DATA soo AS STRING" + CRLF + ; 12 | " DATA noo AS NUMERIC" + CRLF + ; 13 | " DATA ioo" + CRLF + ; 14 | " METHOD aBitmap( n ) INLINE ( If( empty(n) .or. (n > 0 .and. n <= 10), 5 , nil ) )" + CRLF + ; 15 | " METHOD otherMedhod()" + CRLF + ; 16 | " METHOD oggProc()" + CRLF + ; 17 | "endclass" + CRLF + ; 18 | "METHOD otherMedhod() CLASS oggetto" + CRLF + ; 19 | "return nil" + CRLF + ; 20 | "METHOD oggProc() class oggetto" + CRLF + ; 21 | "return" + CRLF + ; 22 | */ 23 | proc main() 24 | LOCAL cTest := ; 25 | "#include " + CRLF + ; 26 | "" + CRLF + ; 27 | "proc test()" + CRLF + ; 28 | " LOCAL bTest := {|| pippo() }" + CRLF + ; 29 | " LOCAL i" + CRLF + ; 30 | " public arrow, flash" + CRLF + ; 31 | " private canary, firestorm" + CRLF + ; 32 | " j := 2" + CRLF + ; 33 | " test2('aa')" + CRLF + ; 34 | "return" + CRLF +; 35 | "" + CRLF + ; 36 | "func test2(pippo)" + CRLF + ; 37 | " FIELD a" + CRLF + ; 38 | " LOCAL i:= 1, j := 4, k:= 5" + CRLF + ; 39 | " memvar canary, firestorm" + CRLF + ; 40 | " i += 2" + CRLF + ; 41 | " ? i" + CRLF + ; 42 | "return pippo+1" 43 | LOCAL aMsg, i 44 | //LOCAL procedureRegEx := 45 | ? test2(cTest, @aMsg) 46 | ? valtype(aMsg), len(aMsg) 47 | for i:=1 to len(aMsg) 48 | ? aMsg[i,1], aMsg[i,2], aMsg[i,3], aMsg[i,4], aMsg[i,5] 49 | next 50 | callPP() 51 | return 52 | 53 | proc pippo_Pluto() 54 | LOCAL i:=4 55 | a := i!=4 56 | ? "arrivano pippo e pluto" 57 | ? i,j,; 58 | j,; 59 | i 60 | return 61 | 62 | #pragma BEGINDUMP 63 | #include 64 | #include 65 | #include 66 | 67 | 68 | static int pOpenFunc( void * cargo, char * zFileName, 69 | HB_BOOL fBefore, HB_BOOL fSysFile, HB_BOOL fBinary, 70 | HB_PATHNAMES * pIncludePaths, 71 | HB_BOOL * pfNested, FILE ** file_ptr, 72 | const char ** pBufPtr, HB_SIZE * pnLen, HB_BOOL * pfFree ) 73 | { 74 | HB_SYMBOL_UNUSED( cargo ); 75 | HB_SYMBOL_UNUSED( pfNested ); 76 | HB_SYMBOL_UNUSED( file_ptr ); 77 | HB_SYMBOL_UNUSED( pBufPtr ); 78 | HB_SYMBOL_UNUSED( pnLen ); 79 | HB_SYMBOL_UNUSED( pfFree ); 80 | 81 | HB_PATHNAMES* pInc = pIncludePaths; 82 | char *nameBuff; 83 | HB_SIZE nRead; 84 | int i; 85 | printf("open %s(%i,%i,%i):", zFileName, fBefore, fSysFile, fBinary); 86 | //printf(szText,szPar1,szPar2); 87 | printf("\r\n"); 88 | while(pInc) 89 | { 90 | printf(" %s(%i)\r\n",pInc->szPath,pInc->fFree); 91 | pInc = pInc->pNext; 92 | } 93 | nameBuff = (char*)hb_xalloc(35+strlen(zFileName)); 94 | if(fBefore) 95 | strcpy(nameBuff,"c:\\harbour\\include\\"); // windows 96 | //strcpy(nameBuff,"/home/perry/harbour-src/include/"); // linux 97 | else 98 | *nameBuff = 0; 99 | strcat(nameBuff,zFileName); 100 | 101 | *file_ptr = fopen(nameBuff,"rt"); 102 | if(*file_ptr) 103 | { 104 | printf("opened %s (%08X)\r\n",nameBuff, file_ptr); 105 | fseek(*file_ptr,0,SEEK_END); 106 | *pnLen = ftell(*file_ptr); 107 | *pBufPtr = (char*)hb_xalloc(*pnLen+1); 108 | //fseek(*file_ptr,0,SEEK_SET); 109 | fclose(*file_ptr); 110 | *file_ptr = fopen(nameBuff,"rt"); 111 | nRead = fread(&((*pBufPtr)[nRead]),1,*pnLen,*file_ptr); 112 | printf("readed %i/%i (%i)\r\n",nRead,*pnLen, ferror(*file_ptr)); 113 | *pnLen = nRead; 114 | fclose(*file_ptr); 115 | hb_xfree(nameBuff); 116 | return HB_PP_OPEN_OK; 117 | } 118 | hb_xfree(nameBuff); 119 | return HB_PP_OPEN_FILE; 120 | } 121 | 122 | static void pMsgFunc( void * cargo, int iErrorFmt, int iLine, 123 | const char * szModule, char cPrefix, int iValue, 124 | const char * szText, 125 | const char * szPar1, const char * szPar2 ) 126 | { 127 | HB_SYMBOL_UNUSED( iErrorFmt ); 128 | PHB_ITEM pMsgDest = (PHB_ITEM) ((PHB_COMP)cargo)->cargo; 129 | PHB_ITEM pMsgItem = hb_itemArrayNew(5); 130 | char szPrefix[2], *szMess, len; 131 | szPrefix[0] = cPrefix; szPrefix[1] = 0; 132 | hb_arraySetCConst(pMsgItem, 1, szPrefix); 133 | hb_arraySetNI(pMsgItem, 2, iValue); 134 | hb_arraySetCConst(pMsgItem, 3, szModule); 135 | hb_arraySetNI(pMsgItem, 4, iLine); 136 | len = strlen(szText);//sprintf(0, szText,szPar1,szPar2); 137 | if(szPar1) len += strlen(szPar1); 138 | if(szPar2) len += strlen(szPar2); 139 | szMess = (char*)hb_xalloc(len+1); 140 | //printf("msg %X (%i)\r\n", szMess, len); 141 | sprintf(szMess, szText,szPar1,szPar2); 142 | hb_arraySetC(pMsgItem, 5, szMess); 143 | hb_xfree(szMess); 144 | hb_arrayAdd(pMsgDest, pMsgItem); 145 | } 146 | 147 | static void hb_compInitVars( HB_COMP_DECL ) 148 | { 149 | HB_COMP_PARAM->functions.iCount = 0; 150 | HB_COMP_PARAM->functions.pFirst = NULL; 151 | HB_COMP_PARAM->functions.pLast = NULL; 152 | HB_COMP_PARAM->szAnnounce = NULL; 153 | HB_COMP_PARAM->fSwitchCase = HB_FALSE; 154 | 155 | HB_COMP_PARAM->symbols.iCount = 0; 156 | HB_COMP_PARAM->symbols.pFirst = NULL; 157 | HB_COMP_PARAM->symbols.pLast = NULL; 158 | HB_COMP_PARAM->pInitFunc = NULL; 159 | HB_COMP_PARAM->pLineFunc = NULL; 160 | HB_COMP_PARAM->pDeclFunc = NULL; 161 | 162 | HB_COMP_PARAM->iStaticCnt = 0; 163 | HB_COMP_PARAM->iVarScope = HB_VSCOMP_LOCAL; 164 | 165 | HB_COMP_PARAM->inlines.iCount = 0; 166 | HB_COMP_PARAM->inlines.pFirst = NULL; 167 | HB_COMP_PARAM->inlines.pLast = NULL; 168 | 169 | HB_COMP_PARAM->szFile = NULL; 170 | 171 | HB_COMP_PARAM->iModulesCount = 0; 172 | } 173 | 174 | HB_FUNC( TEST2 ) 175 | { 176 | const char * szSource = hb_parc( 1 ); 177 | const char * szFileName = "perry.prg"; 178 | PHB_ITEM pMsgDest = hb_param(2,HB_IT_BYREF ); 179 | HB_COMP_DECL; 180 | int iStatus = 0; 181 | PHB_HFUNC pFunc; 182 | PHB_HVAR pVar; 183 | PHB_HCLASS pClasses; 184 | PHB_HDECLARED pDeclared; 185 | 186 | //printf("dest: %X\r\n",pMsgDest); 187 | 188 | HB_COMP_PARAM = hb_comp_new(); 189 | if( pMsgDest ) 190 | { 191 | HB_COMP_PARAM->cargo = pMsgDest; 192 | HB_COMP_PARAM->outMsgFunc = pMsgFunc; 193 | hb_arrayNew(pMsgDest,0); 194 | } 195 | HB_COMP_PARAM->iWarnings = 3; 196 | HB_COMP_PARAM->fDebugInfo = HB_FALSE; 197 | HB_COMP_PARAM->fLineNumbers = HB_TRUE; 198 | HB_COMP_PARAM->fGauge = HB_FALSE; 199 | 200 | hb_compChkEnvironment( HB_COMP_PARAM ); 201 | HB_COMP_PARAM->iSyntaxCheckOnly = 1; 202 | hb_compInitPP( HB_COMP_PARAM, pOpenFunc ); 203 | hb_compIdentifierOpen( HB_COMP_PARAM ); 204 | 205 | hb_compInitVars( HB_COMP_PARAM ); 206 | if( ! hb_pp_inBuffer( HB_COMP_PARAM->pLex->pPP, szFileName, szSource, strlen( szSource ), 0 ) ) 207 | { 208 | hb_compOutErr( HB_COMP_PARAM, "Cannot create preprocessor buffer." ); 209 | iStatus = EXIT_FAILURE; 210 | hb_comp_free( HB_COMP_PARAM ); 211 | return; 212 | } 213 | HB_COMP_PARAM->iModulesCount = 1; 214 | HB_COMP_PARAM->currLine = hb_pp_line( HB_COMP_PARAM->pLex->pPP ) + 1; 215 | HB_COMP_PARAM->currModule = hb_compIdentifierNew( 216 | HB_COMP_PARAM, szFileName, HB_IDENT_COPY ); 217 | 218 | hb_comp_yyparse( HB_COMP_PARAM ); 219 | 220 | printf(" **** \r\n"); 221 | pFunc = HB_COMP_PARAM->functions.pFirst; 222 | while( pFunc) 223 | { 224 | // if( ( pFunc->funFlags & HB_FUNF_FILE_DECL ) == 0 ) 225 | { 226 | //hb_compOptimizeFrames( HB_COMP_PARAM, pFunc ); 227 | #ifdef _USE_MYHB 228 | printf("%s (%i) - %X\r\n",pFunc->szName,pFunc->iDeclLine, pFunc->funFlags); 229 | #else 230 | printf("%s - %X\r\n",pFunc->szName, pFunc->funFlags); 231 | #endif 232 | pVar = pFunc->pLocals; 233 | while(pVar) 234 | { 235 | #ifdef _USE_MYHB 236 | printf("L-->%s (at %i(%i:%i))\r\n",pVar->szName,pVar->iDeclLine,pVar->iStartCol,pVar->iEndCol); 237 | #else 238 | printf("L-->%s (at %i)\r\n",pVar->szName,pVar->iDeclLine); 239 | #endif 240 | pVar = pVar->pNext; 241 | } 242 | pVar = pFunc->pStatics; 243 | while(pVar) 244 | { 245 | #ifdef _USE_MYHB 246 | printf("L-->%s (at %i(%i:%i))\r\n",pVar->szName,pVar->iDeclLine,pVar->iStartCol,pVar->iEndCol); 247 | #else 248 | printf("L-->%s (at %i)\r\n",pVar->szName,pVar->iDeclLine); 249 | #endif 250 | pVar = pVar->pNext; 251 | } 252 | pVar = pFunc->pFields; 253 | while(pVar) 254 | { 255 | #ifdef _USE_MYHB 256 | printf("L-->%s (at %i(%i:%i))\r\n",pVar->szName,pVar->iDeclLine,pVar->iStartCol,pVar->iEndCol); 257 | #else 258 | printf("L-->%s (at %i)\r\n",pVar->szName,pVar->iDeclLine); 259 | #endif 260 | pVar = pVar->pNext; 261 | } 262 | pVar = pFunc->pMemvars; 263 | while(pVar) 264 | { 265 | #ifdef _USE_MYHB 266 | printf("L-->%s (at %i(%i:%i))\r\n",pVar->szName,pVar->iDeclLine,pVar->iStartCol,pVar->iEndCol); 267 | #else 268 | printf("L-->%s (at %i)\r\n",pVar->szName,pVar->iDeclLine); 269 | #endif 270 | pVar = pVar->pNext; 271 | } 272 | pFunc = pFunc->pNext; 273 | } 274 | } 275 | //* 276 | pClasses = HB_COMP_PARAM->pFirstClass; 277 | while(pClasses) 278 | { 279 | #ifdef _USE_MYHB 280 | printf("class: %s(%i)\r\n", pClasses->szName, pClasses->iDeclLine); 281 | #else 282 | printf("class: %s\r\n", pClasses->szName); 283 | #endif 284 | // here I can search for declared function with same name and change it type in class 285 | pDeclared = pClasses->pMethod; 286 | while(pDeclared) 287 | { 288 | // here I can search for declared function with className_DeclaredName and change it type in method 289 | // 290 | #ifdef _USE_MYHB 291 | printf("--> %s (%i) >'%c' - %i\r\n", pDeclared->szName,pDeclared->iDeclLine, pDeclared->cType, pDeclared->iParamCount); 292 | #else 293 | printf("--> %s >'%c' - %i\r\n", pDeclared->szName, pDeclared->cType, pDeclared->iParamCount); 294 | #endif 295 | pDeclared = pDeclared->pNext; 296 | } 297 | pClasses = pClasses->pNext; 298 | } 299 | pDeclared = HB_COMP_PARAM->pFirstDeclared; 300 | while(pDeclared) 301 | { 302 | #ifdef _USE_MYHB 303 | printf("--> %s (%i) >'%c' - %i\r\n", pDeclared->szName,pDeclared->iDeclLine, pDeclared->cType, pDeclared->iParamCount); 304 | #else 305 | printf("--> %s >'%c' - %i\r\n", pDeclared->szName, pDeclared->cType, pDeclared->iParamCount); 306 | #endif 307 | pDeclared = pDeclared->pNext; 308 | } 309 | //*/ 310 | hb_comp_free(HB_COMP_PARAM); 311 | hb_retni(iStatus); 312 | } 313 | 314 | HB_FUNC( TEST ) 315 | { 316 | const char * szSource = hb_parc( 1 ); 317 | int argc = 0, i; 318 | const char ** argv= 0; 319 | int iResult; 320 | HB_BYTE * pBuffer; 321 | HB_SIZE nLen; 322 | PHB_ITEM pParam = hb_param( 2, HB_IT_ARRAY ); 323 | if( pParam ) 324 | { 325 | argc = hb_arrayLen( pParam ); 326 | argv = ( const char ** ) hb_xgrab( sizeof( char * ) * ( argc + 1 ) ); 327 | for( i = 1; i <= argc; ++i ) 328 | { 329 | argv[ i-1 ] = hb_arrayGetCPtr( pParam, i ); 330 | } 331 | } 332 | printf("call\r\n"); 333 | iResult = hb_compMainExt( argc, argv, &pBuffer, &nLen, szSource, 0, 0, 0, pMsgFunc ); 334 | //hb_compCompile( pComp, "{SOURCE}", szSource, iStartLine ); 335 | printf("%i\r\n",iResult); 336 | if( iResult == EXIT_SUCCESS && pBuffer ) 337 | hb_retclen_buffer( ( char * ) pBuffer, nLen ); 338 | } 339 | 340 | HB_FUNC( CALLPP ) 341 | { 342 | PHB_DYNS pDyns = hb_dynsymFind( "PIPPO_PLUTO" ); 343 | if( pDyns && ! hb_dynsymIsFunction( pDyns ) ) 344 | pDyns = NULL; 345 | if( pDyns ) 346 | { 347 | hb_vmPushDynSym( pDyns ); 348 | hb_vmPushNil(); 349 | // push other params 350 | hb_vmDo( 0 ); //<-- nParams 351 | } 352 | } 353 | 354 | #pragma ENDDUMP 355 | 356 | -------------------------------------------------------------------------------- /client/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to the "Harbour and xHarbour" extension will be documented in this file. 3 | 4 | # 1.0.7 5 | - **Server** fixed completion on trigger character 6 | - **Debugger** fixed start on non-windows system [#87](https://github.com/APerricone/harbourCodeExtension/issues/86) 7 | 8 | 9 | # 1.0.6 10 | - **Server** better classData, classVar, classMethod support 11 | - **Syntax** better classData, classVar, classMethod support 12 | - **Debugger** better handshake 13 | 14 | # 1.0.5 15 | - **Server** Added classData, classVar, classMethod support [#86](https://github.com/APerricone/harbourCodeExtension/issues/86) 16 | - **Syntax** Added classData, classVar, classMethod support 17 | - **Validation** Better Ambiguous reference support [#85](https://github.com/APerricone/harbourCodeExtension/issues/85) 18 | 19 | # 1.0.4 20 | - **Debugger** Added messages in case of early exit [#84](https://github.com/APerricone/harbourCodeExtension/issues/84) 21 | - **Debugger** Added wapi_OutputDebugString/hb_OutDebug support on windows using [@yagisumi/win-output-debug-string](https://github.com/yagisumi/node-win-output-debug-string) 22 | - **Sever** Added some documented in not-standard way functions and procedures 23 | 24 | # 1.0.3 25 | - **Server** fixed some formatter behaviour 26 | - **Debugger** better completition 27 | 28 | # 1.0.2 29 | - **Debugger** Added workareas 30 | - **Server** first version of formatter 31 | - **Client** added code style configurator 32 | 33 | # 1.0.1 34 | - **server** fixed table name reader [#73](https://github.com/APerricone/harbourCodeExtension/issues/73) 35 | - **server** better go to declarection [#74](https://github.com/APerricone/harbourCodeExtension/issues/74) 36 | 37 | # 1.0.0 38 | - **server** fixed crash [#70](https://github.com/APerricone/harbourCodeExtension/issues/70) 39 | 40 | # 0.9.16 41 | - **server** fixed crash on space before -> [#69](https://github.com/APerricone/harbourCodeExtension/issues/69) 42 | 43 | # 0.9.15 44 | - **server** fixed freeze looking for references last word of the file 45 | - **server** even better performance on long splitted line [#68](https://github.com/APerricone/harbourCodeExtension/issues/68) (the sample file come from 1.7sec to 0.17 on my PC) 46 | 47 | # 0.9.14 48 | - **server** better performance on long splitted line [#68](https://github.com/APerricone/harbourCodeExtension/issues/68) 49 | - **server** first support for [semantic token](https://code.visualstudio.com/api/language-extensions/semantic-highlight-guide) 50 | - **server** first support for "[find all references](https://code.visualstudio.com/api/language-extensions/programmatic-language-features#find-all-references-to-a-symbol)" 51 | - **validation** hightlight of unused symbol 52 | - **syntax** added shared keyword [#64](https://github.com/APerricone/harbourCodeExtension/issues/64) 53 | 54 | # 0.9.13 55 | - **debugger** better stability 56 | 57 | # 0.9.12 58 | - **debugger** better stability 59 | - **task** better stability 60 | - **task** correct management of batch option 61 | 62 | # 0.9.11 63 | - **server** fixes case of unfound parent [#57](https://github.com/APerricone/harbourCodeExtension/issues/57) 64 | - **syntax** fixes [memvar aliasing syntax highlighting #58](https://github.com/APerricone/harbourCodeExtension/issues/58), 65 | [Multiline "inline" class methods syntax highlighting #59](https://github.com/APerricone/harbourCodeExtension/issues/59), 66 | [Try catch syntax highlighting #60](https://github.com/APerricone/harbourCodeExtension/issues/60) by [Edgard Lorraine Messias](https://github.com/edgardmessias) 67 | - **debugger** better step out and step next support 68 | - **server** better code folding see [#56](https://github.com/APerricone/harbourCodeExtension/issues/56) 69 | - **task** added temporary variable solver waiting for [VSCode #81007](https://github.com/microsoft/vscode/issues/81007) 70 | 71 | Many thanks to [Seth Hovestol](https://github.com/Hovestar) for bug reporting 72 | 73 | # 0.9.10 74 | - **debugger** added process list on attach, attach by process Id 75 | - **task** added Harbour and HBMK2 tasks, BETA 76 | - **server** added completition and go to definition on #pragma include [#45](https://github.com/APerricone/harbourCodeExtension/issues/45) 77 | - **syntax** better operator and keyworld list 78 | - **debugger** better filename uppercase/lowercase check using external library 79 | - **general** updated used libraries 80 | 81 | # 0.9.9 82 | - **server** fixed error message "cannot read property" [#43](https://github.com/APerricone/harbourCodeExtension/issues/43) 83 | - **server** restored define "go to definition" 84 | - **validation** trying to solve problem of wrong file name 85 | 86 | # 0.9.8 87 | - fix crash 88 | 89 | # 0.9.7 90 | - missing files 91 | 92 | # 0.9.6 93 | - **server** [better outline and breadcump](https://github.com/APerricone/harbourCodeExtension/raw/master/images/0_9_6.png) 94 | - **debugger** fixed compilation with xHarbour, see #38 95 | - **server** better group nearest support 96 | - **syntax** fixed classdata syntax highlight 97 | - **server** better define support 98 | - **server** better "case" folding 99 | - **decorator** use of editorBracketMatch colors 100 | 101 | # 0.9.5 102 | - **debugger** resolved breakpoint invalid on far source, fix ([#35](https://github.com/APerricone/harbourCodeExtension/issues/35)) 103 | - **debugger** resolved file not found on relative path, fix ([#36](https://github.com/APerricone/harbourCodeExtension/issues/36)) 104 | 105 | # 0.9.4 106 | - **server** added Folder provider 107 | - **decorator** use of server 108 | - **server** better performance, stability + some fixes ([#32](https://github.com/APerricone/harbourCodeExtension/issues/32)) 109 | - **syntax** minor fixes 110 | - **server** Added harbourDoc support 111 | - **client** Added auto harbourDoc generation on **/* $DOC$** 112 | 113 | # 0.9.3 114 | - **server** fixed wordBasedSuggestions for methods and fields 115 | - **debugger** added ATTACH support 116 | - **debugger** better stack format 117 | - **debugger** better management of eval error 118 | 119 | # 0.9.2 120 | - **server** speed-up completition 121 | - **server** use of editor.wordBasedSuggestions setting 122 | - **syntax** Fixed multiline string on screen (aka TEXT/ENDTEXT) 123 | 124 | # 0.9.1 125 | - **server** Fix error pressing CTRL on empty space [#28](https://github.com/APerricone/harbourCodeExtension/issues/28) 126 | - **syntax** Fixed multiline string on screen (aka TEXT/ENDTEXT) 127 | 128 | # 0.9.0 129 | - **server** add hover for defines 130 | - **syntax** a lot of fixes by [Edgard Lorraine Messias](https://github.com/edgardmessias) 131 | - **server** added information about class during completition 132 | 133 | # 0.8.12 134 | - **debugger** Added options for error management 135 | - **server** Fix some crash 136 | - **syntax** use of [Edgard Lorraine Messias](https://github.com/edgardmessias) syntax 137 | - **server** Fixed deletion of wrong fields 138 | 139 | # 0.8.10 - 0.8.11 140 | - restored files 141 | 142 | # 0.8.9 143 | - **server** Fix some crash 144 | 145 | # 0.8.8 146 | - **server** New incude file management 147 | - **server** Added word based suggestions [#16](https://github.com/APerricone/harbourCodeExtension/issues/16) 148 | - **server** Added keyword suggestions 149 | - **debugger** Added support for multiline string 150 | - **debugger** Added terminalType option 151 | - **debugger** Added handshake 152 | - **server** Added define on complettion and definition 153 | - **server** Added public and data in go to workspace symbol 154 | - **debugger** fix statics in some conditions 155 | 156 | # 0.8.7 157 | - **server** Added check if C file is a compiled prg [#12](https://github.com/APerricone/harbourCodeExtension/issues/12) 158 | - **server** Removed unused code to avoid performance issues 159 | - **validation** correct working dir 160 | 161 | # 0.8.6 162 | - **server** added workspaceDepth to fix [#11](https://github.com/APerricone/harbourCodeExtension/issues/11) 163 | - **server** changed behaviour of search inside symbols, to match VSCode behaviour. 164 | - **server** fix name of member all lowercase 165 | - **server** better field management on completition 166 | - **server** better word match 167 | - **server** better database management 168 | - **validation** Better support for relative include path 169 | 170 | ## 0.8.5 171 | - **decorator** restored correct behaviour 172 | - **server** use of DocumentSymbol 173 | - **server** removed current word from completition 174 | - **debugger** fixed crash on expression with colon 175 | 176 | ## 0.8.4 177 | - **Server** fixed crash on completition 178 | 179 | ## 0.8.3 180 | - **Server** added completition and goto definition on include 181 | - **Server** fixed crash on completition on beginning of file 182 | - **Server** removed duplicated completitionItem 183 | - **Server** fixed static management on completition 184 | - **Server** fixed link show on onDefinition for files 185 | 186 | ## 0.8.2 187 | - **Code** added some snippets 188 | - **Icon** changed icon 189 | - **Server** fix crash in case of file outside a workspace 190 | 191 | ## 0.8.1 192 | - **server** Added missing file 193 | 194 | ## 0.8.0 195 | - **syntax hightlight**: [management of command/translate directive](https://github.com/APerricone/harbourCodeExtension/raw/master/images/command.png) 196 | - **syntax hightlight**: added abbreviations for local, public, private, etc 197 | - **server** Added field management 198 | - **server** Added completition support 199 | - **server** on workspace symbol you can search a object method adding colon. 200 | 201 | ## 0.7.9 202 | - **Server**: [Added multi workspace support](https://github.com/APerricone/harbourCodeExtension/issues/9) 203 | - **Debugger**: Added completition support (beta) 204 | - **Server**: better support on no-workspace environment. 205 | - **Server**: Fixed gotoDefinition for long names 206 | - **Debugger**: fixed management of access/assign class data. 207 | 208 | ## 0.7.8 209 | - **Server**: show comment before function declaration as help 210 | - **Debugger**: [Added support for copy expression, copy value and add to watch](https://github.com/APerricone/harbourCodeExtension/wiki/Debugger#copy-expression). 211 | - **Debugger**: Changed view of date and time value on xHarbour to use a valid xHarbour format. 212 | 213 | ## 0.7.7 214 | - **Debugger**: Added beta xHarbour support 215 | - **Debugger**: Fixed case when the module name contains colon 216 | - **Debugger**: Fixed Log message without carriage return 217 | - **syntax hightlight**: simplified datetime regex 218 | - **syntax hightlight**: better 'for' support 219 | - **syntax hightlight**: added keywords 220 | 221 | ## 0.7.6 222 | - **syntax hightlight**: fix for datetime constant 223 | - **syntax hightlight**: allow min #pragma and macro for inline multiline string 224 | - **validation**: added validation of opened file 225 | - **syntax hightlight**: added __streaminclude syntax and fix __stream syntax 226 | - **decorator**: removed harbour decorator in not-harbour files. 227 | 228 | ## 0.7.5 229 | - Added **localization**: English, Italian and Spanish (thanks to José Luis Sánchez for review) 230 | 231 | ## 0.7.4 232 | - **Debugger**: added sourcePaths in debugger, to allow to specify more than one directory with code. 233 | 234 | ## 0.7.3 235 | - **Fix**: Get debugger code on linux and mac 236 | 237 | ## 0.7.2 238 | - **Syntax**: fixed text/endtext 239 | 240 | ## 0.7.1 241 | - **Debugger**: Better support for conditional breakpoint and hit count breakoint 242 | - **Syntax**: Added TEXT/ENDTEXT 243 | 244 | ## 0.7.0 245 | - **Debugger**: [beta] added interception of error 246 | - **Debugger**: Better support for statics. 247 | 248 | ## 0.6.9 249 | - **Debugger**: fixed crash adding/removing breakpoints when the program running 250 | - **Debugger**: fixed freeze starting debug program without debugger 251 | - **Validator**: fixed "invalid filename" error in validation 252 | 253 | ## 0.6.8 254 | - **Added command**: "Harbour: Get debugger code" 255 | - **Debugger**: fixed startOnEntry = false 256 | - **Debugger**: Added support for conditional breakpoint, hit count breakoint and LogPoint 257 | 258 | 259 | ## 0.6.7 260 | - added setting to disable the decorator 261 | - better decorator code 262 | 263 | ## 0.6.6 264 | - enabled **decorator** (marks correspondent if, else, endif, for, next ect ect), BETA. 265 | - **Fix**ed stall on signature request 266 | 267 | ## 0.6.5 268 | - **Server**: parse c file searching harbour function 269 | - **Fix**: crash on signature for static proc/func 270 | 271 | ## 0.6.4 272 | - **Fix**: arguments counting when lone bracket are presents inside string 273 | 274 | ## 0.6.3 275 | - **Fix**ed debugger 276 | 277 | ## 0.6.2 278 | - **Fix**ed server 279 | 280 | ## 0.6.1 281 | - **Fix**ed arguments counting when commas are presents inside string or inside curly or squared brackets 282 | - Added message when unable to start the executable on **debug**ging 283 | 284 | ## 0.6.0 285 | - Added signature for 342 standard procedure 286 | - Manage of special case of New 287 | - Fixed debugger.js on new node/code versions 288 | - Better validator support for executables 289 | 290 | ## 0.5.11 291 | - Fixed debugger expression managing 292 | - Added problem matcher for harbour 293 | 294 | ## 0.5.10 295 | - Fixed crash on server in particular case 296 | 297 | ## 0.5.9 298 | - Fixed signature help on method and on multiline declaration 299 | - Added looking on sub folder for workspace symbol 300 | 301 | ## 0.5.8 302 | - Added support for Signature help 303 | 304 | ## 0.5.7 305 | - Fixed public and private hash and array watch (Thanks to Lailton Fernando Mariano for found the bug) 306 | - Added support for non string hash keys 307 | - removed "Globals" and "Externals" scope until they are not supported. 308 | 309 | **need recompile the library from test\dbg_lib.prg** 310 | 311 | ## 0.5.6 312 | - minor fixes on syntax 313 | 314 | ## 0.5.5 315 | - added support for multiline text using #pragma 316 | 317 | ## 0.5.4 318 | - added debugger initial configuration to allow creation of launch.json with harbour 319 | - minor fixes on tmlanguage. 320 | 321 | ## 0.5.3 322 | - **validation**: added harbour.extraOptions to send extra options to harbour compiler. 323 | 324 | ## 0.5.2 325 | - **debugger**: better support for object expression, need recompile the library from test\dbg_lib.prg 326 | 327 | ## 0.5.1 328 | - minimal optimization on debugger 329 | 330 | ## 0.5.0 331 | - restored version counter 332 | 333 | ## 0.4.7 334 | - fixed debugger 335 | 336 | ## 0.4.6 337 | - removed decorator (i don't like if) 338 | - fixed square brace preceded by an upper case character (it is not string) 339 | 340 | ## 0.4.5 341 | - added data and parameter kind of symbols provider 342 | - added "do case" in decorator 343 | 344 | ## 0.4.4 345 | - Show matches on 'if-else-endif', 'for-exit-loop-next' (in test) 346 | - Added "go to definition" that works only on current workspace. 347 | 348 | ## 0.4.3 349 | - fixed some windows issues 350 | 351 | ## 0.4.1 352 | - send symbol kind in the correct way to have icons 353 | 354 | ## 0.4.0 355 | - added Language server 356 | - Added workspace symbol provider 357 | 358 | ## 0.3.5 359 | - fixed crashes in debugger (need recompile the library too) 360 | 361 | ## 0.3.4 362 | - fixed double callstack with new VSCode 363 | 364 | ## 0.3.3 365 | - Fixed expression evaluation 366 | - better validation message when the error contains a regEx character 367 | 368 | ## 0.3.2 369 | - removed refused debug prints 370 | 371 | ## 0.3.1 372 | - Added missing method on debugger... still not working 373 | - better validation message when the correct line is inside the message 374 | - recognization of method procedure and method function 375 | 376 | ## 0.3.0 377 | - New Debug library, it is totally rewritten without C code, it allows new features like: 378 | - pause support 379 | - add/remove breakpoint during running 380 | - step out 381 | - error catch 382 | - other bugfixes 383 | - validation only on problem if it is only a word 384 | 385 | ## 0.2.3 386 | - fix validation when diagnostic is in another file. 387 | - fix typo on debugger 388 | 389 | ## 0.2.1 390 | - minor fixes 391 | 392 | ## 0.2.0 393 | - first version of symbol provider. 394 | 395 | ## 0.1.5 396 | - Removed server code and use of harbour executable to provide diagnostic informations. 397 | 398 | ## 0.1.0 399 | - semi complete debugging support (see [README](README.md#DEBUG) to know how integrate.) 400 | 401 | ## 0.0.9 402 | - first version of debugger 403 | 404 | ## 0.0.3 405 | - better syntax support 406 | 407 | ## 0.0.2 408 | - custom icon creation 409 | 410 | ## 0.0.1 411 | - Initial release 412 | - first version of harbour syntax 413 | -------------------------------------------------------------------------------- /client/src/taskProvider.js: -------------------------------------------------------------------------------- 1 | const vscode = require('vscode'); 2 | const path = require("path"); 3 | const fs = require("fs"); 4 | const cp = require("child_process"); 5 | const os = require("os"); 6 | const localize = require("./myLocalize.js").localize; 7 | const getAllWorkspaceFiles = require("./utils.js").getAllWorkspaceFiles; 8 | 9 | /** 10 | * 11 | * @param {String} v 12 | */ 13 | function resolvePredefinedVariables(v) { 14 | function replace(what,solved) { 15 | if(v.indexOf(what)>=0) { 16 | do { 17 | v=v.replace(what,solved); 18 | } while(v.indexOf(what)>=0); 19 | } 20 | } 21 | var textDocument = undefined; 22 | var parsed = undefined; 23 | if(vscode && vscode.window && vscode.window.activeTextEditor && vscode.window.activeTextEditor.document) { 24 | textDocument =vscode.window.activeTextEditor.document; 25 | parsed = path.parse(textDocument.uri.fsPath); 26 | } 27 | var workspace0 = undefined, relativeParsed = undefined, relativePath = undefined; 28 | if(textDocument) { 29 | workspace0 = vscode.workspace.getWorkspaceFolder(textDocument.uri); 30 | } 31 | if(workspace0) { 32 | relativePath = path.relative(workspace0.uri.fsPath,textDocument.uri.fsPath); 33 | relativeParsed = path.parse(relativePath); 34 | }else 35 | workspace0 = vscode.workspace.workspaceFolders[0]; 36 | replace("${workspaceFolder}", workspace0.uri.fsPath); //the path of the folder opened in VS Code 37 | replace("${workspaceFolderBasename}", workspace0.name) //the name of the folder opened in VS Code without any slashes (/) 38 | replace("${file}", textDocument? textDocument.uri.fsPath : ""); // - the current opened file 39 | replace("${relativeFile}", relativePath? relativePath : ""); // the current opened file relative to workspaceFolder 40 | replace("${relativeFileDirname}", relativeParsed? relativeParsed.dir : ""); //the current opened file's dirname relative to workspaceFolder 41 | replace("${fileBasename}", parsed? parsed.base : ""); //the current opened file's basename 42 | replace("${fileBasenameNoExtension}", parsed? parsed.name:""); //the current opened file's basename with no file extension 43 | replace("${fileDirname}", parsed? path.basename(parsed.dir):""); //the current opened file's dirname 44 | replace("${fileExtname}", parsed? parsed.ext:""); //the current opened file's extension 45 | //replace("${cwd}"); //the task runner's current working directory on startup 46 | //replace("${lineNumber}"); //the current selected line number in the active file 47 | //replace("${selectedText}"); //the current selected text in the active file 48 | //replace("${execPath}"); //the path to the running VS Code executable 49 | //replace("${defaultBuildTask}"); //the name of the default build task 50 | return v; 51 | } 52 | 53 | /** 54 | * @implements {vscode.TaskProvider} 55 | */ 56 | class HRBTask { 57 | constructor() { 58 | } 59 | 60 | GetArgs(fileName) { 61 | var section = vscode.workspace.getConfiguration('harbour'); 62 | var args = ["-w"+section.warningLevel, fileName ]; 63 | for (var i = 0; i < section.extraIncludePaths.length; i++) { 64 | var pathVal = resolvePredefinedVariables(section.extraIncludePaths[i]); 65 | args.push("-I"+pathVal); 66 | } 67 | return args.concat(section.extraOptions.split(" ").filter(function(el) {return el.length != 0})); 68 | } 69 | 70 | provideTasks(token) { 71 | var textDocument = undefined; 72 | if(vscode && vscode.window && vscode.window.activeTextEditor && vscode.window.activeTextEditor.document) 73 | textDocument =vscode.window.activeTextEditor.document; 74 | var retValue = []; 75 | if(textDocument && textDocument.languageId == 'harbour' ) { 76 | var section = vscode.workspace.getConfiguration('harbour'); 77 | var args = this.GetArgs(textDocument.fileName); 78 | var file_cwd = path.dirname(textDocument.fileName); 79 | retValue.push(new vscode.Task({ 80 | "type": "Harbour", 81 | "input": "${file}", 82 | "output": "portable" 83 | }, vscode.TaskScope.Workspace, localize("harbour.task.portableName"),"Harbour", 84 | new vscode.ShellExecution(section.compilerExecutable,args.concat(["-gh"]),{ 85 | cwd: file_cwd 86 | }),"$harbour")); 87 | retValue.push(new vscode.Task({ 88 | "type": "Harbour", 89 | "input": "${file}", 90 | "output": "C code", 91 | "c-type": "compact" 92 | }, vscode.TaskScope.Workspace, localize("harbour.task.cCodeName"),"Harbour", 93 | new vscode.ShellExecution(section.compilerExecutable,args.concat(["-gc0"]),{ 94 | cwd: file_cwd 95 | }),"$harbour")); 96 | } 97 | return retValue; 98 | } 99 | /** 100 | * 101 | * @param {vscode.Task} task 102 | * @param {vscode.CancellationToken} token 103 | */ 104 | resolveTask(task, token) { 105 | var input=resolvePredefinedVariables(task.definition.input); 106 | var ext = path.extname(input); 107 | if(ext!=".prg") 108 | return undefined; 109 | var retTask = new vscode.Task(task.definition, vscode.TaskScope.Workspace,"build "+input ,"Harbour"); 110 | 111 | var args = this.GetArgs(input); 112 | if(task.definition.output=="C code") { 113 | if("c-type" in task.definition) { 114 | var id = ["compact","normal", 115 | "verbose","real C Code"].indexOf(task.definition["c-type"]); 116 | if(id>=0) { 117 | args = args.concat(["-gc"+id]); 118 | } else args = args.concat(["-gc"]); 119 | } else args = args.concat(["-gc"]); 120 | } else 121 | args = args.concat(["-gh"]); 122 | var file_cwd = path.dirname(vscode.window.activeTextEditor.document.fileName); 123 | var section = vscode.workspace.getConfiguration('harbour'); 124 | retTask.execution = new vscode.ShellExecution(section.compilerExecutable,args.concat(["-gc"]),{ 125 | cwd: file_cwd 126 | }); 127 | if(!Array.isArray(task.problemMatchers) || task.problemMatchers.length==0 ) 128 | retTask.problemMatchers = ["$harbour"]; 129 | else 130 | retTask.problemMatchers = task.problemMatchers; 131 | return retTask; 132 | } 133 | } 134 | 135 | var myTerminals = {}; 136 | /** 137 | * 138 | * @param {vscode.Task} task 139 | */ 140 | function getTerminalFn(task) { 141 | if(!(task.name in myTerminals)) { 142 | myTerminals[task.name]=undefined 143 | } 144 | return () => { 145 | if(!myTerminals[task.name]) 146 | myTerminals[task.name]=new HBMK2Terminal(task); 147 | // check if the batch changed 148 | var taskBatch = getBatch(task); 149 | if((myTerminals[task.name].batch || taskBatch) && taskBatch!=myTerminals[task.name].batch) { 150 | myTerminals[task.name]=new HBMK2Terminal(task); 151 | } 152 | // 153 | var ret=myTerminals[task.name]; 154 | ret.append(task); 155 | return ret; 156 | } 157 | } 158 | 159 | function ToAbsolute(fileName) { 160 | if(path.isAbsolute(fileName)) 161 | return fileName; 162 | for (let i = 0; i < vscode.workspace.workspaceFolders.length; i++) { 163 | let thisDir = vscode.workspace.workspaceFolders[i]; 164 | /** @type {vscode.Uri} */ 165 | let uri = vscode.Uri.parse(thisDir.uri) 166 | if (uri.scheme != "file") continue; 167 | const p = path.join(uri.fsPath,fileName); 168 | if(fs.existsSync(p)) { 169 | return p; 170 | break; 171 | } 172 | } 173 | return undefined; 174 | } 175 | 176 | function getBatch(task) { 177 | var batch = task.definition.setupBatch; 178 | var platform = process.platform; 179 | if(platform=='win32') platform="windows"; 180 | if(platform=='darwin') platform="osx"; 181 | //TODO: other platforms 182 | if(platform in task.definition) { 183 | var platformSpecific = task.definition[platform]; 184 | if(platformSpecific.env) { 185 | var extraEnv = platformSpecific.env; 186 | for (const p in extraEnv) { 187 | if (extraEnv.hasOwnProperty(p)) { 188 | this.env[p] = extraEnv[p]; 189 | } 190 | } 191 | } 192 | if(platformSpecific.setupBatch) 193 | batch=platformSpecific.setupBatch; 194 | } 195 | return batch; 196 | } 197 | 198 | /** @implements {vscode.Pseudoterminal} */ 199 | class HBMK2Terminal { 200 | /** 201 | * @param {String} platform The parameter platfor of those tasks 202 | * @param {String} compiler The parameter compiler of those tasks 203 | * @param {batch} batch The parameter setupBatch of those tasks 204 | */ 205 | constructor(task) { 206 | this.name = task.name; 207 | myTerminals[task.name]=this; 208 | this.write = ()=>{}; 209 | this.closeEvt = ()=>{}; 210 | this.tasks = []; 211 | /** @type {boolean} indicates that this HBMK2Terminal is executing the setup shell or batch */ 212 | this.settingUp = false; 213 | this.env=process.env; 214 | if(task.definition.options && task.definition.options.env) { 215 | var extraEnv = task.definition.options.env; 216 | for (const p in extraEnv) { 217 | if (extraEnv.hasOwnProperty(p)) { 218 | this.env[p] = extraEnv[p]; 219 | } 220 | } 221 | } 222 | var batch = getBatch(task); 223 | this.batch=batch; 224 | if(batch) { 225 | batch=ToAbsolute(batch); 226 | if(!batch) { 227 | this.unableToStart=true; 228 | return; 229 | } 230 | this.settingUp = true; 231 | var cmd="setup"; //TODO: make unique 232 | if(os.platform()=='win32') { 233 | cmd+=".bat"; 234 | fs.writeFileSync(cmd, 235 | `call \"${batch}\"\r\nset\r\n`) 236 | } else { 237 | cmd="./"+cmd+".sh"; 238 | fs.writeFileSync(cmd, 239 | `sh \"${batch}\"\r\printenv\r\n`) 240 | } 241 | var tc = this; 242 | var env1 = {}; 243 | function onData(data) { 244 | /** @type{String[]} */ 245 | var str = data.toString().split(/[\r\n]{1,2}/); 246 | for(let i=0;i { 260 | fs.unlink(cmd, ()=>{}); 261 | tc.env=env1; 262 | tc.settingUp = false; 263 | tc.start(); 264 | }); 265 | } 266 | this.write=()=>{}; 267 | } 268 | onDidWrite(fn) { 269 | this.write=fn; 270 | } 271 | onDidClose(fn) { 272 | this.closeEvt=fn; 273 | } 274 | open(/*initialDimensions*/) { 275 | this.start(); 276 | } 277 | append(t) { 278 | this.tasks.push(t); 279 | } 280 | close() { 281 | if(this.p) { 282 | this.p.kill(); 283 | } 284 | myTerminals[this.name]=undefined; 285 | } 286 | start() { 287 | if(this.unableToStart) { 288 | this.write(localize("harbour.task.HBMK2.errorBatch")+".\r\n"); 289 | this.closeEvt(); 290 | return; 291 | } 292 | if(this.settingUp){ 293 | this.write(localize("harbour.task.HBMK2.setup")+"\r\n"); 294 | return; 295 | } 296 | if(this.tasks.length==0) 297 | this.closeEvt(0); 298 | var task = this.tasks.splice(0,1)[0]; 299 | var inputFile = ToAbsolute(resolvePredefinedVariables(task.definition.input)) || task.definition.input; 300 | var section = vscode.workspace.getConfiguration('harbour'); 301 | 302 | var args = [inputFile, "-w"+section.warningLevel]; 303 | if(task.definition.debugSymbols) { 304 | args.push("-b"); 305 | args.push(path.resolve(__dirname, path.join('..','extra','dbg_lib.prg'))); 306 | } 307 | if(task.definition.output) args.push("-o"+task.definition.output); 308 | if(Array.isArray(task.definition.extraArgs)) args=args.concat(task.definition.extraArgs); 309 | if(task.definition.platform) args.push("-plat="+task.definition.platform); 310 | if(task.definition.compiler) args.push("-comp="+task.definition.compiler); 311 | var file_cwd = path.dirname(inputFile); 312 | var hbmk2Path = path.join(path.dirname(section.compilerExecutable), "hbmk2") 313 | this.write(localize("harbour.task.HBMK2.start")+"\r\n") 314 | this.p = cp.spawn(hbmk2Path,args,{cwd:file_cwd,env:this.env}); 315 | var tc = this; 316 | this.p.stderr.on('data', data => 317 | tc.write(data.toString()) 318 | ); 319 | this.p.stdout.on('data', data => 320 | tc.write(data.toString()) 321 | ); 322 | this.p.on("close", (r) => { 323 | tc.p = undefined; 324 | tc.closeEvt(r) 325 | }); 326 | this.p.on("error", (r)=> { 327 | tc.p = undefined; 328 | tc.closeEvt(-1); 329 | }); 330 | } 331 | } 332 | 333 | /** 334 | * @implements {vscode.TaskProvider} 335 | */ 336 | class HBMK2Task { 337 | getValidTask(name,input, definition, problemMatches) { 338 | var retTask = new vscode.Task({ 339 | "type": "HBMK2", 340 | "input": input 341 | //"c-type": "compact" 342 | }, vscode.TaskScope.Workspace, name ,"HBMK2"); 343 | retTask.definition = definition; 344 | retTask.execution = new vscode.CustomExecution(getTerminalFn(retTask)); 345 | if(!Array.isArray(problemMatches) || problemMatches.length==0 ) 346 | retTask.problemMatchers = ["$harbour","$msCompile"]; 347 | return retTask; 348 | } 349 | 350 | /** 351 | * 352 | * @param {vscode.CancellationToken} token 353 | */ 354 | provideTasks(token) { 355 | if(!vscode.workspace.workspaceFolders || vscode.workspace.workspaceFolders.length==0) 356 | return []; 357 | var HBMK2This = this; 358 | return new Promise((resolve,reject)=> { 359 | var retValue=[]; 360 | var textDocument = undefined; 361 | if(vscode && vscode.window && vscode.window.activeTextEditor && vscode.window.activeTextEditor.document) 362 | textDocument =vscode.window.activeTextEditor.document; 363 | if(textDocument && textDocument.languageId == 'harbour' ) { 364 | var task = new vscode.Task({ 365 | "type": "HBMK2", 366 | "input": "${file}" 367 | }, vscode.TaskScope.Workspace, localize("harbour.task.HBMK2.provideName2") ,"HBMK2"); 368 | task.execution = new vscode.CustomExecution(getTerminalFn(task)); 369 | task.problemMatchers = ["$harbour","$msCompile"]; 370 | var task2 = new vscode.Task({ 371 | "type": "HBMK2", 372 | "input": "${file}", 373 | "debugSymbols": true, 374 | "output": "${fileBasenameNoExtension}_dbg" 375 | }, vscode.TaskScope.Workspace, localize("harbour.task.HBMK2.provideName3") ,"HBMK2"); 376 | task2.execution = new vscode.CustomExecution(getTerminalFn(task)); 377 | task2.problemMatchers = ["$harbour","$msCompile"]; 378 | retValue.push(task,task2); 379 | } 380 | getAllWorkspaceFiles(token).then((values)=>{ 381 | if(token.isCancellationRequested) { 382 | reject(token); 383 | return; 384 | } 385 | for(let j=0;j>"+line+"\r\n","stdout")) 73 | if (line.length == 0) continue; 74 | if (this.processLine) { 75 | this.processLine(line); 76 | continue; 77 | } 78 | if (line.startsWith("STOP")) { 79 | this.sendEvent(new debugadapter.StoppedEvent(line.substring(5), 1)); 80 | continue; 81 | } 82 | if (line.startsWith("STACK")) { 83 | this.sendStack(line); 84 | continue; 85 | } 86 | if (line.startsWith("BREAK")) { 87 | this.processBreak(line); 88 | continue; 89 | } 90 | if (line.startsWith("ERROR") && !line.startsWith("ERROR_VAR")) { 91 | //console.log("ERROR") 92 | var stopEvt = new debugadapter.StoppedEvent("error", 1, line.substring(6)); 93 | this.sendEvent(stopEvt); 94 | continue; 95 | } 96 | if (line.startsWith("EXPRESSION")) { 97 | this.processExpression(line); 98 | continue; 99 | } 100 | if (line.startsWith("LOG")) { 101 | this.sendEvent(new debugadapter.OutputEvent(line.substring(4) + "\r\n", "stdout")) 102 | continue; 103 | } 104 | if (line.startsWith("INERROR")) { 105 | this.sendScope(line[8] == 'T') 106 | continue; 107 | } 108 | if (line.startsWith("COMPLETITION")) { 109 | this.processCompletion(line); 110 | continue; 111 | } 112 | for (var j = this.variables.length - 1; j >= 0; j--) { 113 | if (line.startsWith(this.variables[j].command)) { 114 | this.sendVariables(j, line); 115 | break; 116 | } 117 | } 118 | if (j != this.variables.length) continue; 119 | } 120 | } 121 | 122 | /** 123 | * @param response{debugprotocol.InitializeResponse} 124 | * @param args{debugprotocol.InitializeRequestArguments} 125 | */ 126 | initializeRequest(response, args) { 127 | if (args.locale) { 128 | require("./myLocalize.js").reInit(args); 129 | } 130 | 131 | response.body = response.body || {}; 132 | response.body.supportsConfigurationDoneRequest = true; 133 | response.body.supportsDelayedStackTraceLoading = false; 134 | response.body.supportsConditionalBreakpoints = true; 135 | response.body.supportsHitConditionalBreakpoints = true; 136 | response.body.supportsLogPoint = true; 137 | response.body.supportsCompletionsRequest = true; 138 | response.body.supportsTerminateRequest = true; 139 | response.body.exceptionBreakpointFilters = [ 140 | { 141 | label: localize('harbour.dbgError.all'), 142 | filter: 'all', 143 | default: false 144 | }, 145 | { 146 | label: localize('harbour.dbgError.notSeq'), 147 | filter: 'notSeq', 148 | default: true 149 | } 150 | ]; 151 | //response.body.supportsEvaluateForHovers = true; too risky 152 | this.sendResponse(response); 153 | } 154 | 155 | configurationDoneRequest(response, args) { 156 | if (this.startGo) { 157 | this.command("GO\r\n"); 158 | this.sendEvent(new debugadapter.ContinuedEvent(1, true)); 159 | } 160 | this.sendResponse(response); 161 | } 162 | 163 | /** 164 | * 165 | * @param {debugprotocol.DebugProtocol.LaunchRequest} response 166 | * @param {debugprotocol.DebugProtocol.LaunchRequestArguments} args 167 | */ 168 | launchRequest(response, args) { 169 | var port = args.port ? args.port : 6110; 170 | var tc = this; 171 | this.justStart = true; 172 | this.sourcePaths = []; //[path.dirname(args.program)]; 173 | if ("workspaceRoot" in args) { 174 | this.sourcePaths.push(args.workspaceRoot); 175 | } 176 | if ("sourcePaths" in args) { 177 | this.sourcePaths = this.sourcePaths.concat(args.sourcePaths); 178 | } 179 | for (let idx = 0; idx < this.sourcePaths.length; idx++) { 180 | try { 181 | this.sourcePaths[idx] = trueCase.trueCasePathSync(this.sourcePaths[idx]); 182 | } catch (ex) { 183 | // path no found 184 | this.sourcePaths.splice(idx, 1); 185 | idx--; 186 | } 187 | } 188 | this.Debugging = !args.noDebug; 189 | this.startGo = args.stopOnEntry === false || args.noDebug === true; 190 | // starts the server 191 | var server = net.createServer(socket => { 192 | tc.evaluateClient(socket, server, args) 193 | }).listen(port); 194 | // starts the program 195 | //console.log("start the program"); 196 | switch (args.terminalType) { 197 | case 'external': 198 | case 'integrated': 199 | this.runInTerminalRequest({ 200 | "kind": args.terminalType, 201 | "cwd": args.workingDir, 202 | "args": [args.program].concat(args.arguments ? args.arguments : []) 203 | }, undefined, runResp =>{ 204 | if(runResp && runResp.body && runResp.body.processId) { 205 | tc.setProcess(runResp.body.processId) 206 | } 207 | }) 208 | break; 209 | case 'none': 210 | default: 211 | var process; 212 | if (args.arguments) 213 | process = cp.spawn(args.program, args.arguments, { cwd: args.workingDir }); 214 | else 215 | process = cp.spawn(args.program, { cwd: args.workingDir }); 216 | process.on("error", e => { 217 | tc.sendEvent(new debugadapter.OutputEvent(localize("harbour.dbgError1", args.program, args.workingDir), "stderr")) 218 | tc.sendEvent(new debugadapter.TerminatedEvent()); 219 | return 220 | }); 221 | process.on("exit", (code,signal) => { 222 | tc.sendEvent(new debugadapter.ExitedEvent(code)); 223 | if(!tc.processId) { 224 | tc.sendEvent(new debugadapter.OutputEvent(localize("harbour.prematureExit", code), "stderr")) 225 | tc.sendEvent(new debugadapter.TerminatedEvent()); 226 | } 227 | }); 228 | process.stderr.on('data', data => 229 | tc.sendEvent(new debugadapter.OutputEvent(data.toString(), "stderr")) 230 | ); 231 | process.stdout.on('data', data => 232 | tc.sendEvent(new debugadapter.OutputEvent(data.toString(), "stdout")) 233 | ); 234 | if(process.pid) 235 | this.setProcess(process.pid) 236 | break; 237 | } 238 | this.sendResponse(response); 239 | } 240 | 241 | /** 242 | * 243 | * @param {debugprotocol.DebugProtocol.AttachResponse} response 244 | * @param {debugprotocol.DebugProtocol.AttachRequestArguments} args 245 | */ 246 | attachRequest(response, args) { 247 | var port = args.port ? args.port : 6110; 248 | if (args.process <= 0 && (args.program || "").length == 0) { 249 | response.success = false; 250 | response.message = "invalid parameter"; 251 | this.sendResponse(response); 252 | return; 253 | } 254 | var tc = this; 255 | this.justStart = true; 256 | this.sourcePaths = []; //[path.dirname(args.program)]; 257 | if ("workspaceRoot" in args) { 258 | this.sourcePaths.push(args.workspaceRoot); 259 | } 260 | if ("sourcePaths" in args) { 261 | this.sourcePaths = this.sourcePaths.concat(args.sourcePaths); 262 | } 263 | for (let idx = 0; idx < this.sourcePaths.length; idx++) { 264 | try { 265 | this.sourcePaths[idx] = trueCase.trueCasePathSync(this.sourcePaths[idx]); 266 | } catch (ex) { 267 | // path no found 268 | this.sourcePaths.splice(idx, 1); 269 | idx--; 270 | } 271 | } 272 | this.Debugging = !args.noDebug; 273 | this.startGo = true; 274 | // starts the server 275 | var server = net.createServer(socket => { 276 | tc.evaluateClient(socket, server, args) 277 | }).listen(port); 278 | this.sendResponse(response); 279 | } 280 | 281 | setProcess(pid) { 282 | var tc = this 283 | if(!pid) { 284 | return 285 | } 286 | if(this.processId) { 287 | if(this.processId != pid) { 288 | // uncomment for debugging 289 | //throw new Error("2 pid?! "+this.processId +" and "+ pid) 290 | } 291 | return 292 | } 293 | this.processId = pid; 294 | winMonitor?.start(mInfo=>{ 295 | if(mInfo.pid==pid) { 296 | this.sendEvent(new debugadapter.OutputEvent(mInfo.message + "\r\n", "console")) 297 | } 298 | }) 299 | var interval = setInterval(() => { 300 | try { 301 | process.kill(pid, 0); 302 | } catch (error) { 303 | winMonitor?.stop() 304 | tc.sendEvent(new debugadapter.TerminatedEvent()); 305 | clearInterval(interval); 306 | } 307 | }, 1000) 308 | } 309 | 310 | disconnectRequest(response, args) { 311 | this.command("DISCONNECT\r\n"); 312 | this.sendResponse(response); 313 | } 314 | 315 | terminateRequest(response, args) { 316 | process.kill(this.processId, 'SIGKILL'); 317 | this.sendResponse(response); 318 | } 319 | 320 | /** 321 | * 322 | * @param {net.Socket} socket 323 | * @param {net.Server} server 324 | * @param {debugprotocol.DebugProtocol.LaunchRequestArguments|debugprotocol.DebugProtocol.AttachRequestArguments} args 325 | */ 326 | evaluateClient(socket, server, args) { 327 | var tc = this; 328 | 329 | socket.on("data", data => { 330 | try { 331 | if (tc.socket == socket) { 332 | tc.processInput(data.toString()) 333 | return; 334 | } 335 | // the client sended exe name and process ID 336 | var lines = data.toString().split("\r\n"); 337 | if (lines.length < 2) {//todo: check if they arrive in 2 tranches. 338 | socket.write("NO\r\n") 339 | socket.end(); 340 | return; 341 | } 342 | var processId = parseInt(lines[1]); 343 | if(tc.processId) { 344 | if(tc.processId!=processId) { 345 | socket.write("NO\r\n") 346 | socket.end(); 347 | return; 348 | } 349 | } else { 350 | if (args.program && args.program.length > 0) { 351 | var exeTarget = path.basename(args.program, path.extname(args.program)).toLowerCase(); 352 | var clPath = path.basename(lines[0], path.extname(lines[0])).toLowerCase(); 353 | if (clPath != exeTarget) { 354 | socket.write("NO\r\n") 355 | socket.end(); 356 | return; 357 | } 358 | } 359 | 360 | if (args.process && args.process > 0 && args.process != processId) { 361 | socket.write("NO\r\n") 362 | socket.end(); 363 | return; 364 | } 365 | } 366 | 367 | socket.write("HELLO\r\n") 368 | tc.setProcess(processId); 369 | //connected! 370 | tc.sendEvent(new debugadapter.InitializedEvent()); 371 | server.close(); 372 | tc.socket = socket; 373 | socket.removeAllListeners("data"); 374 | socket.on("data", data => { 375 | tc.processInput(data.toString()) 376 | }); 377 | socket.write(tc.queue); 378 | this.justStart = false; 379 | tc.queue = ""; 380 | } catch (ex) { 381 | socket.write("NO\r\n") 382 | socket.end(); 383 | } 384 | }); 385 | } 386 | 387 | command(cmd) { 388 | if (this.justStart) 389 | this.queue += cmd; 390 | else 391 | this.socket.write(cmd); 392 | } 393 | 394 | /// STACK 395 | /** 396 | * @param response{DebugProtocol.StackTraceResponse} response to send 397 | * @param args{DebugProtocol.StackTraceArguments} arguments 398 | */ 399 | stackTraceRequest(response, args) { 400 | // reset references 401 | this.variables = []; 402 | 403 | if (this.stack.length == 0) 404 | this.command("STACK\r\n"); 405 | this.stack.push(response); 406 | this.stackArgs.push(args); 407 | } 408 | 409 | threadsRequest(response) { 410 | response.body = 411 | { 412 | threads: 413 | [ //TODO: suppport multi thread 414 | new debugadapter.Thread(1, "Main Thread") 415 | ] 416 | }; 417 | this.sendResponse(response) 418 | } 419 | 420 | sendStack(line) { 421 | var nStack = parseInt(line.substring(6)); 422 | var frames = []; 423 | frames.length = nStack; 424 | var j = 0; 425 | this.processLine = function (line) { 426 | var infos = line.split(":"); 427 | for (let i = 0; i < infos.length; i++) infos[i] = infos[i].replace(";", ":") 428 | var completePath = infos[0] 429 | var found = false; 430 | if (infos[0].length > 0) { 431 | if (path.isAbsolute(infos[0]) && fs.existsSync(infos[0])) { 432 | completePath = infos[0]; 433 | found = true; 434 | try { 435 | completePath = trueCase.trueCasePathSync(infos[0]); 436 | } catch (ex) { } 437 | } else 438 | for (let i = 0; i < this.sourcePaths.length; i++) { 439 | if (fs.existsSync(path.join(this.sourcePaths[i], infos[0]))) { 440 | completePath = path.join(this.sourcePaths[i], infos[0]); 441 | found = true; 442 | try { 443 | completePath = trueCase.trueCasePathSync(infos[0], this.sourcePaths[i]); 444 | } catch (ex) { 445 | try { 446 | completePath = trueCase.trueCasePathSync(completePath); 447 | } catch (ex2) { } 448 | } 449 | break; 450 | } 451 | } 452 | } 453 | if (found) infos[0] = path.basename(completePath); 454 | frames[j] = new debugadapter.StackFrame(j, infos[2], 455 | new debugadapter.Source(infos[0], completePath), 456 | parseInt(infos[1])); 457 | j++; 458 | if (j == nStack) { 459 | while (this.stack.length > 0) { 460 | var args = this.stackArgs.shift(); 461 | var resp = this.stack.shift(); 462 | args.startFrame = args.startFrame || 0; 463 | args.levels = args.levels || frames.length; 464 | args.levels += args.startFrame; 465 | resp.body = { 466 | stackFrames: frames.slice(args.startFrame, args.levels) 467 | }; 468 | this.sendResponse(resp); 469 | } 470 | this.processLine = undefined; 471 | } 472 | } 473 | } 474 | 475 | /// VARIABLES 476 | scopesRequest(response, args) { 477 | // save wanted stack 478 | this.currentStack = args.frameId + 1; 479 | 480 | this.scopeResponse = response 481 | this.command("INERROR\r\n") 482 | } 483 | 484 | sendScope(inError) { 485 | var commands = []; 486 | if (inError) commands.push("ERROR_VAR") 487 | commands = commands.concat(["LOCALS", "PUBLICS", "PRIVATES", "PRIVATE_CALLEE", "STATICS", "WORKAREAS"]); 488 | var n = this.variables.findIndex((v) => v.command==commands[0]); 489 | if (n < 0) { 490 | n = this.variables.length; 491 | // TODO: put these 3 members together on 'AOS' 492 | commands.forEach((cmd) => { 493 | this.variables.push(new HBVar(cmd)); 494 | }) 495 | } 496 | var scopes = []; 497 | if (inError) scopes.push(new debugadapter.Scope("Error", ++n)) 498 | scopes = scopes.concat([ 499 | new debugadapter.Scope("Local", ++n), 500 | new debugadapter.Scope("Public", ++n), 501 | new debugadapter.Scope("Private local", ++n), 502 | new debugadapter.Scope("Private external", ++n), 503 | new debugadapter.Scope("Statics", ++n), 504 | new debugadapter.Scope("Workareas", ++n) 505 | ]) 506 | var response = this.scopeResponse; 507 | response.body = { scopes: scopes }; 508 | this.sendResponse(response) 509 | } 510 | 511 | /** 512 | * @param {String} cmd 513 | */ 514 | sendAreaHeaders(response, cmd) { 515 | // AREA:Alias:Area:fCount:recno:reccount:scope: 516 | // 0 1 2 3 4 5 6 517 | var infos = this.areasInfos[parseInt(cmd.substring(4))]; 518 | var vars = []; 519 | var baseEval = infos[1] + "->" 520 | var v, recNo = parseInt(infos[4]), recCount = parseInt(infos[5]); 521 | v = new debugadapter.Variable("recNo", infos[4]); 522 | if (recNo > recCount) v.value = "eof" 523 | if (recNo <= 0) v.value = "bof" 524 | v.evaluateName = baseEval + "(recNo())" 525 | vars.push(v); 526 | v = new debugadapter.Variable("recCount", infos[5]); 527 | v.evaluateName = baseEval + "(recCount())" 528 | vars.push(v); 529 | v = new debugadapter.Variable("Scope", '"' + infos[6] + '"') 530 | v.evaluateName = baseEval + "(OrdName(IndexOrd()))" 531 | v.type = "C" 532 | vars.push(v); 533 | var columns = new debugadapter.Variable("Fields", "") 534 | columns.indexedVariables = parseInt(infos[3]); 535 | columns.variablesReference = this.getVarReference(cmd + ":FIELDS", baseEval); 536 | vars.push(columns); 537 | response.body = { variables: vars } 538 | this.sendResponse(response) 539 | } 540 | 541 | variablesRequest(response, args) { 542 | if (args.variablesReference <= this.variables.length) { 543 | var hbStart = args.start ? args.start + 1 : 1; 544 | var hbCount = args.count ? args.count : 0; 545 | var cmd = this.variables[args.variablesReference - 1].command; 546 | if (cmd.startsWith("AREA") && cmd.indexOf(":") < 0) { 547 | this.sendAreaHeaders(response, cmd) 548 | return; 549 | } 550 | this.variables[args.variablesReference - 1].response = response; 551 | this.command(`${cmd}\r\n${this.currentStack}:${hbStart}:${hbCount}\r\n`); 552 | } else 553 | this.sendResponse(response) 554 | } 555 | 556 | getVarReference(line, evalTxt) { 557 | var r = this.variables.findIndex((v) => v.command==line); 558 | if (r >= 0) return r + 1; 559 | var infos = line.split(":"); 560 | if (infos.length > 4) { //the value can contains : , we need to rejoin it. 561 | infos[2] = infos.splice(2).join(":").slice(0, -1); 562 | infos.length = 3; 563 | line = infos.join(":") + ":" 564 | } 565 | var hbVar = new HBVar(line) 566 | hbVar.evaluation = evalTxt; 567 | this.variables.push(hbVar) 568 | //this.sendEvent(new debugadapter.OutputEvent("added variable command:'"+line+"'\r\n","stdout")) 569 | return this.variables.length; 570 | } 571 | 572 | getVariableFormat(dest, type, value, valueName, line, id) { 573 | if (type == "C") { 574 | value = value.replace(/\\\$\\n/g, "\n") 575 | value = value.replace(/\\\$\\r/g, "\r") 576 | } 577 | dest[valueName] = value; 578 | dest.type = type; 579 | if (["E", "B", "P"].indexOf(dest.type) == -1 && id>=0 && id") 631 | //v = this.getVariableFormat(v,infos[5],infos[6],"value",line,id); 632 | vars.push(v); 633 | return 634 | } 635 | line = infos[0] + ":" + infos[1] + ":" + infos[2] + ":" + infos[3]; 636 | if (infos.length > 7) { //the value can contains : , we need to rejoin it. 637 | infos[6] = infos.splice(6).join(":"); 638 | } 639 | var v = new debugadapter.Variable(infos[4], infos[6]); 640 | v = this.getVariableFormat(v, infos[5], infos[6], "value", line, id); 641 | vars.push(v); 642 | } 643 | } 644 | 645 | /// PROGRAM FLOW 646 | continueRequest(response, args) { 647 | this.command("GO\r\n"); 648 | this.sendResponse(response); 649 | } 650 | 651 | nextRequest(response, args) { 652 | this.command("NEXT\r\n"); 653 | this.sendResponse(response); 654 | } 655 | 656 | stepInRequest(response, args) { 657 | this.command("STEP\r\n"); 658 | this.sendResponse(response); 659 | } 660 | 661 | stepOutRequest(response, args) { 662 | this.command("EXIT\r\n"); 663 | this.sendResponse(response); 664 | } 665 | 666 | pauseRequest(response, args) { 667 | this.command("PAUSE\r\n"); 668 | this.sendResponse(response); 669 | } 670 | 671 | /// breakpoints 672 | setBreakPointsRequest(response, args) { 673 | var message = ""; 674 | // prepare a response 675 | response.body = { "breakpoints": [] }; 676 | response.body.breakpoints = []; 677 | response.body.breakpoints.length = args.breakpoints.length; 678 | // check if the source is already configurated for breakpoints 679 | var src = args.source.name.toLowerCase(); 680 | var dest 681 | if (!(src in this.breakpoints)) { 682 | this.breakpoints[src] = {}; 683 | } 684 | // mark all old breakpoints for deletion 685 | dest = this.breakpoints[src]; 686 | for (var i in dest) { 687 | if (dest.hasOwnProperty(i)) { 688 | dest[i] = "-" + dest[i]; 689 | } 690 | } 691 | // check current breakpoints 692 | dest.response = response; 693 | for (var i = 0; i < args.breakpoints.length; i++) { 694 | var breakpoint = args.breakpoints[i]; 695 | response.body.breakpoints[i] = new debugadapter.Breakpoint(false, breakpoint.line); 696 | var thisBreakpoint = "BREAKPOINT\r\n" 697 | thisBreakpoint += `+:${src}:${breakpoint.line}` 698 | if ('condition' in breakpoint && breakpoint.condition.length > 0) { 699 | thisBreakpoint += `:?:${breakpoint.condition.replace(/:/g, ";")}` 700 | } 701 | if ('hitCondition' in breakpoint) { 702 | thisBreakpoint += `:C:${breakpoint.hitCondition}` 703 | } 704 | if ('logMessage' in breakpoint) { 705 | thisBreakpoint += `:L:${breakpoint.logMessage.replace(/:/g, ";")}` 706 | } 707 | if (dest.hasOwnProperty(breakpoint.line) && dest[breakpoint.line].substring(1) == thisBreakpoint) { // breakpoint already exists 708 | dest[breakpoint.line] = thisBreakpoint 709 | response.body.breakpoints[i].verified = true; 710 | } else { 711 | //require breakpoint 712 | message += thisBreakpoint + "\r\n" 713 | dest[breakpoint.line] = thisBreakpoint; 714 | } 715 | } 716 | // require delete old breakpoints 717 | var n1 = 0; 718 | for (var i in dest) { 719 | if (dest.hasOwnProperty(i) && i != "response") { 720 | if (dest[i].substring(0, 1) == "-") { 721 | message += "BREAKPOINT\r\n" 722 | message += `-:${src}:${i}\r\n` 723 | dest[i] = "-"; 724 | } 725 | } 726 | } 727 | this.checkBreakPoint(src); 728 | //this.sendEvent(new debugadapter.OutputEvent("send: "+message,"console")) 729 | this.command(message) 730 | //this.sendResponse(response) 731 | } 732 | 733 | processBreak(line) { 734 | //this.sendEvent(new debugadapter.OutputEvent("received: "+line+"\r\n","console")) 735 | var aInfos = line.split(":"); 736 | var dest 737 | if (!(aInfos[1] in this.breakpoints)) { 738 | //error 739 | return 740 | } 741 | aInfos[2] = parseInt(aInfos[2]); 742 | aInfos[3] = parseInt(aInfos[3]); 743 | dest = this.breakpoints[aInfos[1]] 744 | var idBreak = dest.response.body.breakpoints.findIndex(b => b.line == aInfos[2]); 745 | if (idBreak == -1) { 746 | if (aInfos[2] in dest) { 747 | delete dest[aInfos[2]]; 748 | this.checkBreakPoint(aInfos[1]); 749 | } 750 | return; 751 | } 752 | if (aInfos[3] > 1) { 753 | dest.response.body.breakpoints[idBreak].line = aInfos[3]; 754 | dest.response.body.breakpoints[idBreak].verified = true; 755 | dest[aInfos[2]] = 1; 756 | } else { 757 | dest.response.body.breakpoints[idBreak].verified = false; 758 | if (aInfos[4] == 'notfound') 759 | dest.response.body.breakpoints[idBreak].message = localize('harbour.dbgNoModule') 760 | else 761 | dest.response.body.breakpoints[idBreak].message = localize('harbour.dbgNoLine') 762 | dest[aInfos[2]] = 1; 763 | } 764 | this.checkBreakPoint(aInfos[1]); 765 | } 766 | 767 | checkBreakPoint(src) { 768 | var dest = this.breakpoints[src]; 769 | for (var i in dest) { 770 | if (dest.hasOwnProperty(i) && i != "response") { 771 | if (dest[i] != 1) { 772 | return; 773 | } 774 | } 775 | } 776 | //this.sendEvent(new debugadapter.OutputEvent("Response "+src+"\r\n","console")) 777 | this.sendResponse(dest.response); 778 | } 779 | 780 | /// Exception / error 781 | setExceptionBreakPointsRequest(response, args) { 782 | var errorType = args.filters.length; 783 | // 0 - no stop on error 784 | // 1 - stop only out-of-sequence 785 | // 2 - stop all 786 | if (errorType == 1 && args.filters[0] != 'notSeq') { 787 | errorType++; 788 | } 789 | this.command(`ERRORTYPE\r\n${errorType}\r\n`) 790 | //TODO: list all possibile harbour exceptions 791 | /*response.body = {}; 792 | response.body.breakpoints = [ 793 | {id:0,message:"Unknown error"}, 794 | {id:1,message:"Argument error"}, 795 | {id:2,message:"Bound error"}, 796 | {id:3,message:"String overflow"}, 797 | {id:4,message:"Numeric overflow"}, 798 | {id:5,message:"Zero divisor"}, 799 | {id:6,message:"Numeric error"}, 800 | {id:7,message:"Syntax error"}, 801 | {id:8,message:"Operation too complex"}, 802 | {id:11,message:"Memory low"}, 803 | {id:12,message:"Undefined function"}, 804 | {id:13,message:"No exported method"}, 805 | {id:14,message:"Variable does not exist"}, 806 | {id:15,message:"Alias does not exist"}, 807 | {id:16,message:"No exported variable"}, 808 | {id:17,message:"Illegal characters in alias"}, 809 | {id:18,message:"Alias already in use"}, 810 | {id:20,message:"Create error"}, 811 | {id:21,message:"Open error"}, 812 | {id:22,message:"Close error"}, 813 | {id:23,message:"Read error"}, 814 | {id:24,message:"Write error"}, 815 | {id:25,message:"Print error"}, 816 | {id:30,message:"Operation not supported"}, 817 | {id:31,message:"Limit exceeded"}, 818 | {id:32,message:"Corruption detected"}, 819 | {id:33,message:"Data type error"}, 820 | {id:34,message:"Data width error"}, 821 | {id:35,message:"Workarea not in use"}, 822 | {id:36,message:"Workarea not indexed"}, 823 | {id:37,message:"Exclusive required"}, 824 | {id:38,message:"Lock required"}, 825 | {id:39,message:"Write not allowed"}, 826 | {id:40,message:"Append lock failed"}, 827 | {id:41,message:"Lock Failure"}, 828 | {id:45,message:"Object destructor failure"}, 829 | {id:46,message:"array access"}, 830 | {id:47,message:"array assign"}, 831 | {id:48,message:"array dimension"}, 832 | {id:49,message:"not an array"}, 833 | {id:50,message:"conditional"} 834 | ];*/ 835 | this.sendResponse(response); 836 | } 837 | 838 | /// Evaluation 839 | evaluateRequest(response, args) { 840 | response.body = {}; 841 | response.body.result = args.expression; 842 | this.evaluateResponses.push(response); 843 | this.command(`EXPRESSION\r\n${args.frameId + 1 || this.currentStack}:${args.expression.replace(/:/g, ";")}\r\n`) 844 | } 845 | 846 | /** 847 | * Evaluate the return from an expression request 848 | * @param line{string} the income line 849 | */ 850 | processExpression(line) { 851 | // EXPRESSION:{frame}:{type}:{result} 852 | var infos = line.split(":"); 853 | if (infos.length > 4) { //the value can contains : , we need to rejoin it. 854 | infos[3] = infos.splice(3).join(":"); 855 | } 856 | var resp = this.evaluateResponses.shift(); 857 | var line = "EXP:" + infos[1] + ":" + resp.body.result.replace(/:/g, ";") + ":"; 858 | resp.body.name = resp.body.result 859 | if (infos[2] == "E") { 860 | resp.success = false; 861 | resp.message = infos[3]; 862 | } else 863 | resp.body = this.getVariableFormat(resp.body, infos[2], infos[3], "result", line); 864 | this.sendResponse(resp); 865 | } 866 | 867 | /// Completition 868 | 869 | /** 870 | * @param response{DebugProtocol.CompletionsResponse} 871 | * @param args{DebugProtocol.CompletionsArguments} 872 | */ 873 | completionsRequest(response, args) { 874 | this.completionsResponse = response; 875 | let completitonText = args.text.split(/[\r\n]{1,2}/); 876 | if (args.line) { 877 | completitonText = completitonText[args.line - 1]; 878 | } else { 879 | completitonText = completitonText[0]; 880 | } 881 | completitonText = completitonText.substring(0, args.column - 1); 882 | let lastWord = completitonText.match(/[\w\:]+$/i) 883 | if (lastWord) completitonText = lastWord[0]; 884 | this.command(`COMPLETITION\r\n${args.frameId + 1 || this.currentStack}:${completitonText}\r\n`) 885 | } 886 | 887 | /** 888 | * @param line{string} 889 | */ 890 | processCompletion() { 891 | this.processLine = function (line) { 892 | if (line == "END") { 893 | this.sendResponse(this.completionsResponse); 894 | this.processLine = undefined; 895 | return; 896 | } 897 | if (!this.completionsResponse.body) this.completionsResponse.body = {}; 898 | if (!this.completionsResponse.body.targets) this.completionsResponse.body.targets = []; 899 | var type = line.substr(0, line.indexOf(":")); 900 | line = line.substr(line.indexOf(":") + 1); 901 | var thisCompletion = new debugadapter.CompletionItem(line, 0); 902 | thisCompletion.type = type == "F" ? 'function' : 903 | type == "M" ? 'field' : 904 | type == "D" ? 'variable' : 'value'; 905 | // function/procedure -> function 906 | // method -> field 907 | // data -> variable 908 | // local/public/etc -> value 909 | this.completionsResponse.body.targets.push(thisCompletion); 910 | } 911 | } 912 | } 913 | 914 | 915 | /// END 916 | debugadapter.DebugSession.run(harbourDebugSession); 917 | --------------------------------------------------------------------------------