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 |
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 | [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=LHCBE7ERSSVDJ&source=url)
4 | [](https://marketplace.visualstudio.com/items?itemName=aperricone.harbour)
5 | [](https://marketplace.visualstudio.com/items?itemName=aperricone.harbour)
6 | [](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 |
--------------------------------------------------------------------------------