├── .gitattributes ├── .gitignore ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── images ├── Connections.gif ├── Definition.gif ├── DocumentSymbol.gif ├── Documentation.gif ├── RunCmd.gif └── plsql-language.png ├── main ├── package.json ├── src │ ├── client-oracle │ │ └── oracle.server.ts │ ├── connect │ │ ├── connect.controller.ts │ │ ├── connect.inputPannel.ts │ │ ├── connect.statusBar.ts │ │ └── connectUI.controller.ts │ ├── extension.ts │ ├── lib │ │ ├── definition.ts │ │ ├── docFormater.ts │ │ ├── plsqlNavigator.ts │ │ ├── plsqlParser.ts │ │ └── regEx │ │ │ └── RegExParser.ts │ ├── pldoc.controller.ts │ ├── plsql.settings.ts │ ├── plsqlChannel.ts │ ├── plsqlCompletionCustom.ts │ ├── plsqlNavigator.vscode.ts │ ├── plsqlParser.vscode.ts │ ├── provider │ │ ├── plsqlCompletionItem.provider.ts │ │ ├── plsqlDefinition.provider.ts │ │ ├── plsqlDocumentSymbol.provider.ts │ │ ├── plsqlHover.provider.ts │ │ └── plsqlSignature.provider.ts │ └── query │ │ ├── query.controller.ts │ │ └── query.gridview.ts ├── test │ ├── index.ts │ ├── plsqlCompletionItem.test.ts │ ├── plsqlDefinition.test.ts │ ├── plsqlDocumentSymbol.test.ts │ ├── sql │ │ ├── .vscode │ │ │ └── settings.json │ │ ├── plsql.completion.json │ │ ├── query.sql │ │ ├── src │ │ │ ├── PACKAGES │ │ │ │ ├── PCK$1.sql │ │ │ │ └── PCK_2.sql │ │ │ └── PACKAGES_BODIES │ │ │ │ ├── PCK$1.sql │ │ │ │ └── PCK_2.sql │ │ ├── xyz_myDml.sql │ │ ├── xyz_myFunc.sql │ │ ├── xyz_myPackage.sql │ │ ├── xyz_myPackage2.pkb │ │ ├── xyz_myPackage2.pks │ │ ├── xyz_myProc.sql │ │ ├── xyz_myTable.sql │ │ ├── xyz_myTable2.sql │ │ ├── xyz_myTrigger.sql │ │ └── xyz_myView.sql │ └── tsconfig.json └── tsconfig.json ├── package.json ├── plsql-language-custom ├── .vscode │ └── launch.json ├── .vscodeignore └── xyz.plsql-language-custom-0.0.1 │ ├── .vsixmanifest │ ├── README.md │ ├── package.json │ ├── plsql-language-custom.png │ └── syntaxes │ └── plsql-custom.tmLanguage ├── resources └── webview │ ├── connect.css │ ├── connect.html │ ├── connect.js │ ├── gridView.css │ ├── gridView.html │ └── gridView.js ├── server-oracle ├── package.json ├── src │ └── server.ts └── tsconfig.json ├── snippets ├── pldoc.json ├── plsql.completion.json └── plsql.snippets.json ├── syntaxes ├── plsql.configuration.json └── plsql.tmLanguage ├── tsconfig.json └── tslint.json /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.vsix 2 | todo.* 3 | out 4 | node_modules 5 | .history -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "extensionHost", 6 | "request": "launch", 7 | "name": "Launch Main", 8 | "runtimeExecutable": "${execPath}", 9 | "args": [ 10 | "${workspaceRoot}/main/test/sql", 11 | "--extensionDevelopmentPath=${workspaceRoot}" 12 | ], 13 | "stopOnEntry": false, 14 | "sourceMaps": true, 15 | "outFiles": ["${workspaceRoot}/main/out/src/**/*.js"] 16 | }, 17 | { 18 | "type": "node", 19 | "request": "attach", 20 | "name": "Attach to Server", 21 | "port": 6009, 22 | "restart": true, 23 | "outFiles": ["${workspaceRoot}/server-oracle/out/**/*.js"] 24 | }, 25 | { 26 | "type": "extensionHost", 27 | "request": "launch", 28 | "name": "Launch Tests", 29 | "runtimeExecutable": "${execPath}", 30 | "args": [ 31 | "${workspaceRoot}/main/test/sql", 32 | "--extensionDevelopmentPath=${workspaceRoot}", 33 | "--extensionTestsPath=${workspaceRoot}/main/out/test" 34 | ], 35 | "stopOnEntry": false, 36 | "sourceMaps": true, 37 | "outFiles": ["${workspaceRoot}/main/out/test/**/*.js"] 38 | } 39 | ] 40 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "local-history.exclude": [".history",".vscode","**/node_modules","typings","out"], 4 | 5 | "typescript.tsdk": "./node_modules/typescript/lib", 6 | "plsql-language.connections": [ 7 | { 8 | "ID": 3, 9 | "active": true, 10 | "database": "localhost:1521/xe", 11 | "password": "hr", 12 | "username": "hr", 13 | "name": "hr@localhost:1521/xe" 14 | } 15 | ], 16 | "plsql-language.connection.activeInfos": "hr/hr@localhost:1521/xe" 17 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // Available variables which can be used inside of strings. 2 | // ${workspaceRoot}: the root folder of the team 3 | // ${file}: the current opened file 4 | // ${fileBasename}: the current opened file's basename 5 | // ${fileDirname}: the current opened file's dirname 6 | // ${fileExtname}: the current opened file's extension 7 | // ${cwd}: the current working directory of the spawned process 8 | 9 | // A task runner that calls a custom npm script that compiles the extension. 10 | { 11 | "version": "2.0.0", 12 | "tasks": [{ 13 | "label": "compile", 14 | "dependsOn": [{ 15 | "type": "npm", 16 | "script": "compile:main" 17 | },{ 18 | "type": "npm", 19 | "script": "compile:server-oracle" 20 | } 21 | ], 22 | "group": { 23 | "kind": "build", 24 | "isDefault": true 25 | }, 26 | "problemMatcher": [] 27 | },{ 28 | "type": "npm", 29 | "script": "compile:main", 30 | "group": "build", 31 | "presentation": { 32 | "panel": "dedicated", 33 | "reveal": "never" 34 | }, 35 | "isBackground": false, 36 | "problemMatcher": [ 37 | "$tsc" 38 | ] 39 | },{ 40 | "type": "npm", 41 | "script": "compile:server-oracle", 42 | "group": "build", 43 | "presentation": { 44 | "panel": "dedicated", 45 | "reveal": "never" 46 | }, 47 | "isBackground": false, 48 | "problemMatcher": [ 49 | "$tsc" 50 | ] 51 | },{ 52 | "type": "npm", 53 | "script": "compile:test", 54 | "group": { 55 | "kind": "test", 56 | "isDefault": true 57 | }, 58 | "presentation": { 59 | "panel": "dedicated", 60 | "reveal": "never" 61 | }, 62 | "isBackground": false, 63 | "problemMatcher": [ 64 | "$tsc" 65 | ] 66 | }] 67 | } 68 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .history/** 3 | main/src/** 4 | server-oracle/src/** 5 | **/test 6 | **/*.map 7 | **/*.zip 8 | .gitignore 9 | .gitattributes 10 | **/tsconfig.json 11 | tslint.json 12 | plsql-language-custom/** 13 | todo.* 14 | **/node_modules/ag-grid-community/src/** 15 | **/node_modules/ag-grid-community/spec/** 16 | **/node_modules/oracledb/** 17 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.8.2 2 | * Fix bug in RegExpParser - _variable with keyword case_ - [#74](https://github.com/zabel-xyz/plsql-language/issues/74) 3 | 4 | ## 1.8.0 5 | * Fix bug in RegExpParser - _comment before package name_ - [#68](https://github.com/zabel-xyz/plsql-language/issues/68) 6 | * Fix bug in RegExpParser - _quote_ - [#70](https://github.com/zabel-xyz/plsql-language/issues/70) 7 | * Improve RegExpParser - _java_ - [#69](https://github.com/zabel-xyz/plsql-language/issues/69) 8 | * Improve execution of external sql comand - _use of params_ [see readme](https://github.com/zabel-xyz/plsql-language/blob/master/README.md) 9 | * New - _hidden_ property in connection settings 10 | 11 | ## 1.7.0 12 | * New: supports procedure and function with their name in quotes [#62](https://github.com/zabel-xyz/plsql-language/issues/62) 13 | * New: complete extension list [#28](https://github.com/zabel-xyz/plsql-language/issues/28) 14 | * New: supports multi-workspaces search [#40](https://github.com/zabel-xyz/plsql-language/issues/40) 15 | * Deprecated setting: plsql-language.searchFolder [read more](https://github.com/zabel-xyz/plsql-language/blob/master/README.md#define-search-paths) 16 | * Fix bug: Signature, Hover, Completion - bad encoding [#63](https://github.com/zabel-xyz/plsql-language/issues/63) 17 | 18 | ## 1.6.4 19 | * Use node oracledb@3.0.1 20 | 21 | ## 1.6.3 22 | * Fix bug in RegExpParser - _pragma_ - [#59](https://github.com/zabel-xyz/plsql-language/issues/59) 23 | * Fix bug in RegExpParser - Conditional compilation [#58](https://github.com/zabel-xyz/plsql-language/issues/58) / [#61](https://github.com/zabel-xyz/plsql-language/issues/61) 24 | * Fix bug in RegExpParser - Exclude _end_ in quote [#61](https://github.com/zabel-xyz/plsql-language/issues/61) 25 | 26 | ## 1.6.2 27 | * New: add view, table, trigger in PLSQL_Parser [#54](https://github.com/zabel-xyz/plsql-language/issues/54) 28 | 29 | ## 1.6.1 30 | * Fix bug: no connection is written in settings [#51](https://github.com/zabel-xyz/plsql-language/issues/51) 31 | 32 | ## 1.6.0 33 | * New: add oracledb connection to run current statement (can also be run from another extension) 34 | * New: connections settings management 35 | 36 | ## 1.5.0 37 | * New: add Signature help feature. (activated by default) `plsql-language.signatureHelp.enable` 38 | * New: add Hover feature. (desactivated by default) `plsql-language.hover.enable` [#24](https://github.com/zabel-xyz/plsql-language/issues/24) 39 | * Fix bug in regExpParser [#52](https://github.com/zabel-xyz/plsql-language/issues/52) 40 | * Fix bug with characters $# in object name [#53](https://github.com/zabel-xyz/plsql-language/issues/53) 41 | 42 | ## 1.4.2 43 | * Fix use of Breadcrumbs (add information for end of symbols) 44 | 45 | ## 1.4.0 46 | * Use new DocumentSymbolClass (according to vscode api documentation) to show hierarchie in outline 47 | 48 | ## 1.3.4 49 | * Fix issue with parser and nested case...end [#47](https://github.com/zabel-xyz/plsql-language/issues/47) 50 | * Fix issue with the detection of a bad word during auto-completion [#48](https://github.com/zabel-xyz/plsql-language/issues/48) 51 | * Fix issue with outline view [#49](https://github.com/zabel-xyz/plsql-language/issues/49) 52 | * Fix issue with regExParser [#46](https://github.com/zabel-xyz/plsql-language/issues/46) 53 | 54 | ## 1.3.3 55 | * Fix issues completion is case sensitive [#32](https://github.com/zabel-xyz/plsql-language/issues/32) 56 | * Change priority in auto-completion (1st search in plsql.completion.json) [#44](https://github.com/zabel-xyz/plsql-language/issues/44) 57 | * Fix issue with parser and trimming last character[#45](https://github.com/zabel-xyz/plsql-language/issues/45) 58 | * Fix issue with parser and case...end [#37](https://github.com/zabel-xyz/plsql-language/issues/37) 59 | * Add some keywords in highlight syntax [#34](https://github.com/zabel-xyz/plsql-language/issues/34) 60 | 61 | ## 1.3.1 62 | * Fix issues completion is case sensitive [#32](https://github.com/zabel-xyz/plsql-language/issues/32) 63 | * Fix issues completion - different icon for symbols [#32](https://github.com/zabel-xyz/plsql-language/issues/32) 64 | * Fix issues completion with loading hangs [#32](https://github.com/zabel-xyz/plsql-language/issues/32) 65 | 66 | ## 1.3.0 67 | * Fix issues with regExp parser (forward declaration and body declaration) [#37](https://github.com/zabel-xyz/plsql-language/issues/37) 68 | * Add intelliSense for package members (autocompletion from package files) [#32](https://github.com/zabel-xyz/plsql-language/issues/32) 69 | * Add custom intelliSense for tables members (autocompletion from plsql.completion.json) [#32](https://github.com/zabel-xyz/plsql-language/issues/32) 70 | 71 | ## 1.2.3 72 | * Fix case issue in order to work on Linux [#38](https://github.com/zabel-xyz/plsql-language/issues/38) 73 | 74 | ## 1.2.2 75 | * Fix case issue in navigation explained here [#35](https://github.com/zabel-xyz/plsql-language/issues/35) 76 | * Fix variable detection explained here [#8](https://github.com/zabel-xyz/plsql-language/issues/8) 77 | 78 | ## 1.2.1 79 | * Add a setting **commentInSymbols** in order to fix issue [#35](https://github.com/zabel-xyz/plsql-language/issues/35) 80 | * Add a setting **synonym** to use synonym of package name 81 | 82 | ## 1.2.0 83 | * Refactor parser to consider global constant, variable, type [#8](https://github.com/zabel-xyz/plsql-language/issues/8) 84 | 85 | ## 1.1.2 86 | * Fix issue with quote character [#31](https://github.com/zabel-xyz/plsql-language/issues/31) 87 | 88 | ## 1.1.1 89 | * Fix issue with setting searchFolder 90 | * Add a specific variables in PLDoc ${PLDOC_PARAM_TYPE}: type of parameter [#17](https://github.com/zabel-xyz/plsql-language/issues/17) 91 | 92 | ## 1.1.0 93 | * Fix configuration (brackets, autoClosingPairs) [#29](https://github.com/zabel-xyz/plsql-language/issues/29) 94 | * Add .pls file type [#27](https://github.com/zabel-xyz/plsql-language/issues/27) 95 | * Consider files.associations when search files to navigate 96 | * Support for multi-root workspace 97 | * The minimum supported version of VS Code is now 1.17.0 98 | 99 | ## 1.0.4 100 | * Fix symbole list (package, procedure, function inside variable name) [#21](https://github.com/zabel-xyz/plsql-language/issues/21) 101 | * Add .pck file type [#18](https://github.com/zabel-xyz/plsql-language/issues/18) 102 | 103 | ## 1.0.3 104 | * Fix case of module dateformat [#15](https://github.com/zabel-xyz/plsql-language/issues/15) 105 | 106 | ## 1.0.2 107 | * Fix navigation with schema 108 | * Improve documentation about pldoc 109 | 110 | ## 1.0.1 111 | * Fix documentation generation not always working [#13](https://github.com/zabel-xyz/plsql-language/issues/13) 112 | * Fix word based suggestions not working 113 | 114 | ## 1.0.0 115 | * Correct list of symbols with schema in package name [#11](https://github.com/zabel-xyz/plsql-language/issues/11) 116 | * Correct syntax highlighting (rem, prompt) [#10](https://github.com/zabel-xyz/plsql-language/issues/10) 117 | * Add automatic documentation (above functions and procedures) 118 | * Add some snippets 119 | * Use new version of TypeScript, VSCode... 120 | 121 | ## 0.0.14 122 | * Correct syntax highlighting (user) [#7](https://github.com/zabel-xyz/plsql-language/issues/7) 123 | * Allow to navigate when the cursor is on the keyword function or procedure 124 | 125 | ## 0.0.13 126 | * Add/Correct syntax highlighting 127 | * Add an exemple for advanced custom colorization 128 | 129 | ## 0.0.12 130 | * Correct syntax highlighting 131 | 132 | ## 0.0.10 / 0.0.11 133 | * Correct syntax highlighting, some keywords are too greedy (add,in,...) [#7](https://github.com/zabel-xyz/plsql-language/issues/7) 134 | * Colorize COMMENT keyword 135 | 136 | ## 0.0.9 137 | * Add some keywords, functions to improve colorization of syntax [#4](https://github.com/zabel-xyz/plsql-language/issues/4) 138 | [#6](https://github.com/zabel-xyz/plsql-language/pull/6) 139 | * Edit README to include a note about running SQLPlus in a task [#5](https://github.com/zabel-xyz/plsql-language/issues/5) 140 | 141 | ## 0.0.8 142 | * Ignoring names of methods in single-comments [#2](https://github.com/zabel-xyz/plsql-language/issues/2) 143 | 144 | ## 0.0.7 145 | * Colorize DECLARE keyword [#1](https://github.com/zabel-xyz/plsql-language/issues/1) 146 | * Ignoring names of methods in block-comments 147 | * Use new version of TypeScript, VSCode, vsce... 148 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 zabel-xyz 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PL/SQL (Oracle) for Visual Studio Code 2 | 3 | This extension adds support for the PL/SQL (Oracle) to Visual Studio Code. 4 | 5 | ## Colorization 6 | Full syntax highlight for PL/SQL files based on oracle-textmate-bundle 7 | 8 | An advanced customization can be done by using an **extensionDependencies**, [follow this exemple](plsql-language-custom/xyz.plsql-language-custom-0.0.1) 9 | 10 | ## Go to Symbol 11 | Navigate 12 | - to procedures, functions 13 | - to constants, variables, type, subtype, cursor (declared in spec/body part of a package) 14 | 15 | ![Image of Symbol](images/DocumentSymbol.gif) 16 | 17 | ## Go to Definition 18 | Navigate to methods (procedures and functions) with some limitations : 19 | - Go to a symbol (see Go to Symbol) in the same file 20 | - Go to a symbol (see Go to Symbol) in another file whose name includes the package or method name. 21 |
e.g.: *XXX_MyPackage.pkb or XXX_MyFunction.sql* 22 | 23 | ![Image of Definition](images/Definition.gif) 24 | 25 | ## Define search paths 26 | Use the settings `plsql-language.searchPaths` to specify where to search. 27 | - null (default) search in all workspaces. 28 | - ${workspaceFolder}: limit search in current workspace 29 | - ${workspaceFolder: _name_}: limit search by name of workspace 30 | - ${workspaceFolder: _index_}: limit search by index of workspace 31 | - _path_: limit search in this folder 32 | 33 | You can specify multiple locations by using an array. 34 |
e.g.: `["${workspaceFolder: pl}", "C:/Develop/MyProjects/Oracle"]` 35 | 36 | ## Documentation 37 | Generate detailed documentation automatically for procedures and functions. 38 | ![Image of Documentation](images/Documentation.gif) 39 | 40 | Use Ctrl+Space (like others snippets) when the caret is on an empty line, 41 | above a function or a procedure declaration, a 'special' snippet is generated. 42 | (with prefix __doc by default) 43 | 44 | The default template is [here](snippets/pldoc.json).
45 | (don't modify this file, it'll be overwritten with the update of the extension !)
46 | 47 | plsql-language.pldoc.path: to define your own snippet and specify its location 48 | plsql-language.pldoc.author: to define the author. 49 | plsql-language.pldoc.enable: to disabled this feature 50 | 51 | **To customize:**
52 | - Create your own file pldoc.json.
53 | Don't change the default file because it'll be overwritten the next time you update this extension 54 | - Define the path (folder only) to your custom file by using the setting *plsql-language.pldoc.path* 55 | 56 | **Note**
57 | If documentation begins with /** (double stars), it'll appear in Signature, Hover, Completion. 58 | 59 | ## Completion 60 | There is intelliSense for package members (autocompletion from package files). 61 | 62 | You can also define your own completion for tables/fields. 63 | An exemple is [here](snippets/plsql.completion.json).
64 | (don't use this file, it'll be overwritten with the update of the extension !)
65 | 66 | plsql-language.completion.path: to specify its location 67 | 68 | ## Snippets 69 | Some snippets available 70 | 71 | plsql-language.snippets.enable: to disabled snippets defined in this extension 72 | 73 | ## Note 74 | For this extension works with .sql files you must change your settings (user or workspace) like this: 75 | 76 | "files.associations": { 77 | "*.sql": "plsql" 78 | } 79 | 80 | ## Connection 81 | You can configure a list of connection in settings and use the active one in your tasks (see below). 82 | Use the command: `PLSQL - Activate connection` or click on status bar. 83 | 84 | ![Image of Connections](images/Connections.gif) 85 | 86 | The oracle db connection feature is still under constuction, this is a preview version. 87 | To activate it 88 | `plsql-language.oracleConnection.enable: true (false by default)` 89 | 90 | You can run the current SQL statement (CTRL+ENTER). 91 | If no selection, run current command delimited by character / 92 |
Warning: 93 | - Don't use or select character ; on a select statement. 94 | - Don't select character / 95 | 96 | ![Image of RunCmd](images/RunCmd.gif) 97 | 98 | You can also run an SQL statement from another extension like this: 99 | 100 | // To access db from another extension 101 | // Use active connection 102 | try { 103 | const result = await vscode.commands.executeCommand('plsql.executeCommand', {sql: 'SELECT LAST_NAME FROM EMPLOYEES WHERE FIRST_NAME = :NAME', params: ['John']}) 104 | vscode.window.showInformationMessage(JSON.stringify(result)); 105 | } catch(err) { 106 | vscode.window.showErrorMessage(JSON.stringify(err)); 107 | } 108 |
109 | 110 | // Or create a specific connection 111 | try { 112 | // with connection params 113 | // let _connection = await vscode.commands.executeCommand('plsql.createConnection', {user: 'hr', password: 'hr', connectString: 'localhost:1521/xe' }) 114 | // or with a tag to find in setting of connections 115 | let _connection = await vscode.commands.executeCommand('plsql.createConnection', {tag: 'hr'}) 116 | const result = await vscode.commands.executeCommand('plsql.executeCommand', {sql: 'SELECT LAST_NAME FROM EMPLOYEES', connection: _connection}) 117 | vscode.window.showInformationMessage(JSON.stringify(result)); 118 | await vscode.commands.executeCommand('plsql.removeConnection', {connection: _connection}); 119 | _connection = null; 120 | } catch(err) { 121 | vscode.window.showErrorMessage(JSON.stringify(err)); 122 | } 123 | 124 | Prerequiste: 125 | - Install Node.js from [nodejs.org](https://nodejs.org) 126 | 127 | Notes: 128 | - [node-oracledb](https://oracle.github.io/node-oracledb) is used externally to use pre-built [node-oracledb binary](https://github.com/oracle/node-oracledb/releases) 129 | - `npm install oracledb` is automatically executed on the first activation (when using connection) and installed here: 130 | `.../.vscode/extensions/xyz.plsql-language-_version_\server-oracle\node_modules\oracledb` 131 | - Please consult node-oracledb site to resolve issues when installation failed. 132 | - [vscode extensions](https://code.visualstudio.com/docs/extensionAPI/patterns-and-principles#_can-i-use-native-nodejs-modules-with-my-extension) 133 | don't supports binary node module. (This is the reason why command `npm install oracledb` is executed.) 134 | 135 | Roadmap: 136 | - [X] List of connections in settings. 137 | - [X] Complete fields list in settings (connect as...) 138 | - [X] Connect to DB 139 | - [X] Execute SQL 140 | - [ ] Run file as a script 141 | - [ ] Auto-complete 142 | 143 | ## Compile / Task 144 | You can compile a PLSQL package with sqlplus, create a task like this: 145 | 146 | { 147 | "version": "2.0.0", 148 | "tasks": [{ 149 | "label": "sqlplus", 150 | // Run sqlplus 151 | "command": "sqlplus", 152 | // Alternative (see below) 153 | // "command": "run_sqlplus.bat", 154 | 155 | "args": ["username/password@sid", "@\"${file}\""] 156 | // Alternative: use active connection defined in settings 157 | // "args": [${config:plsql-language.connection.activeInfos}, "@\"${file}\""] 158 | }] 159 | } 160 | 161 | To force sqlplus to complete, it is better to use a batch file like this: 162 | 163 | run_sqlplus.bat 164 | echo exit | echo show errors | sqlplus %1 %2 165 | 166 | This will run sqlplus, output any errors, and then exit cleanly back to VS Code.
167 | Thanks to @mortenbra (issue [#5](https://github.com/zabel-xyz/plsql-language/issues/5)) 168 | -------------------------------------------------------------------------------- /images/Connections.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zabel-xyz/plsql-language/28b3f624a88d96c9cc1d4f03653b658f1c234566/images/Connections.gif -------------------------------------------------------------------------------- /images/Definition.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zabel-xyz/plsql-language/28b3f624a88d96c9cc1d4f03653b658f1c234566/images/Definition.gif -------------------------------------------------------------------------------- /images/DocumentSymbol.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zabel-xyz/plsql-language/28b3f624a88d96c9cc1d4f03653b658f1c234566/images/DocumentSymbol.gif -------------------------------------------------------------------------------- /images/Documentation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zabel-xyz/plsql-language/28b3f624a88d96c9cc1d4f03653b658f1c234566/images/Documentation.gif -------------------------------------------------------------------------------- /images/RunCmd.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zabel-xyz/plsql-language/28b3f624a88d96c9cc1d4f03653b658f1c234566/images/RunCmd.gif -------------------------------------------------------------------------------- /images/plsql-language.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zabel-xyz/plsql-language/28b3f624a88d96c9cc1d4f03653b658f1c234566/images/plsql-language.png -------------------------------------------------------------------------------- /main/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "plsql-language-main", 3 | "displayName": "Language PL/SQL", 4 | "description": "PL/SQL language (Oracle) support", 5 | "version": "0.0.1", 6 | "publisher": "xyz", 7 | "engines": { 8 | "vscode": "^1.32.3" 9 | }, 10 | "scripts": { 11 | "update-vscode": "vscode-install", 12 | "postinstall": "vscode-install" 13 | }, 14 | "dependencies": { 15 | "glob": "7.1.3", 16 | "iconv-lite": "0.4.24", 17 | "json5": "2.1.0", 18 | "dateformat": "3.0.3", 19 | "vscode-languageclient": "5.2.1" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /main/src/client-oracle/oracle.server.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | import { 4 | LanguageClient, 5 | LanguageClientOptions, 6 | ServerOptions, 7 | TransportKind 8 | } from 'vscode-languageclient'; 9 | 10 | import * as path from 'path'; 11 | 12 | import PLSQLChannel from '../plsqlChannel'; 13 | 14 | export class OracleService { 15 | private static _oracleServer; 16 | 17 | public static activate(enable: Boolean, contextPath?: string, silent = false) { 18 | if (!this._oracleServer && enable) { 19 | this._oracleServer = new OracleServer( 20 | path.join(contextPath, 'server-oracle', 'out'), 'server.js'); 21 | } 22 | if (this._oracleServer) { 23 | if (enable) 24 | this._oracleServer.start() 25 | .then(msg => { 26 | if (!silent) { 27 | // PLSQLChannel.show(); 28 | PLSQLChannel.log(JSON.stringify(msg)); 29 | } 30 | }) 31 | .catch(err => { 32 | if (!silent) { 33 | vscode.window.showErrorMessage('Oracle start failed - See Output/PLSQL for more informations'); 34 | PLSQLChannel.show(); 35 | PLSQLChannel.log(JSON.stringify(err)); 36 | } 37 | }); 38 | else { 39 | this._oracleServer.stop() 40 | .catch((err) => { 41 | if (!silent) { 42 | vscode.window.showErrorMessage('Oracle stop failed - See Output/PLSQL for more informations'); 43 | PLSQLChannel.show(); 44 | PLSQLChannel.log(JSON.stringify(err)); 45 | } 46 | }); 47 | this._oracleServer = null; 48 | } 49 | } 50 | } 51 | 52 | public static execCommand(params): Promise { 53 | if (this._oracleServer) 54 | return this._oracleServer.execCommand(params); 55 | else 56 | return this.ErrorNoOracleServer(); 57 | } 58 | 59 | public static isConnected(): Boolean { 60 | if (this._oracleServer) 61 | return this._oracleServer.isConnected(); 62 | else 63 | return false; 64 | } 65 | 66 | public static connect(params): Promise { 67 | if (this._oracleServer) 68 | return this._oracleServer.connect(params); 69 | else 70 | return this.ErrorNoOracleServer(); 71 | } 72 | 73 | public static disconnect(params?): Promise { 74 | if (this._oracleServer) 75 | return this._oracleServer.disconnect(params); 76 | else 77 | return this.ErrorNoOracleServer(); 78 | } 79 | 80 | private static ErrorNoOracleServer(): Promise { 81 | return Promise.reject('No oracle server'); 82 | } 83 | } 84 | 85 | class OracleServer { 86 | 87 | private _client: LanguageClient; 88 | private _isClientReady = false; 89 | private _isClientInitialized = false; 90 | private _serverPath: string; 91 | private _serverModule: string; 92 | private _isConnected = false; 93 | 94 | constructor(path: string, module: string) { 95 | this._serverPath = path; 96 | this._serverModule = module; 97 | } 98 | 99 | public start(): Promise { 100 | if (this._client || !this._serverPath) 101 | return Promise.resolve(); 102 | 103 | return new Promise((resolve, reject) => { 104 | 105 | const serverOptions: ServerOptions = { 106 | runtime: 'node', // run node externally to avoid recompile oracleDB with electron version 107 | // module: path.join(this._serverPath, this._serverModule), 108 | module: this._serverModule, 109 | options: { 110 | cwd: this._serverPath 111 | }, 112 | transport: TransportKind.ipc 113 | }; 114 | // Options to control the language client 115 | const clientOptions: LanguageClientOptions = { 116 | }; 117 | 118 | // Create the language client and start the client. 119 | this._client = new LanguageClient( 120 | 'oracleServer', 121 | 'Oracle Server', 122 | serverOptions, 123 | clientOptions 124 | ); 125 | 126 | // Start the client. This will also launch the server 127 | this._client.start(); 128 | 129 | let msg: any; msg = {}; 130 | this._client.onReady() 131 | .then(() => { 132 | this._isClientReady = true; 133 | 134 | this._client.onNotification('Oracle/install', (data) => { 135 | PLSQLChannel.show(); 136 | PLSQLChannel.log(data); 137 | }); 138 | 139 | this._client.onNotification('Oracle/debug', (data) => { 140 | PLSQLChannel.show(); 141 | PLSQLChannel.log(data); 142 | }); 143 | 144 | return this.install(); 145 | }) 146 | .then(result => { 147 | if (result) 148 | msg.install = result; 149 | return this.init(); 150 | }) 151 | .then(result => { 152 | if (result) 153 | msg.init = result; 154 | this._isClientInitialized = true; 155 | return resolve(msg); 156 | }) 157 | .catch(err => { 158 | msg.error = err; 159 | return reject(msg); 160 | }); 161 | }); 162 | } 163 | 164 | public stop(): Promise { 165 | return new Promise((resolve, reject) => { 166 | 167 | this.disconnect() 168 | .then(() => promiseFinally()) 169 | .catch((err) => promiseFinally(err)); 170 | 171 | const promiseFinally = (dErr = false) => { 172 | if (!this._client) 173 | return resolve(); 174 | 175 | this._client.stop() 176 | .then(() => { 177 | if (dErr) 178 | return reject({disconnect: dErr}); 179 | return resolve(); 180 | }, 181 | (err) => reject({disconnect : dErr, error: err})); 182 | this._client = null; 183 | }; 184 | }); 185 | } 186 | 187 | public connect(params): Promise { 188 | return new Promise((resolve, reject) => { 189 | this.execRequest('Oracle/connect', params) 190 | .then(result => { 191 | if ((!params || !params.custom) && result && result.connected === true) 192 | this._isConnected = true; 193 | return resolve(result); 194 | }) 195 | .catch(err => reject(err)); 196 | }); 197 | } 198 | 199 | public disconnect(params?): Promise { 200 | return new Promise((resolve, reject) => { 201 | this.execRequest('Oracle/disconnect', params) 202 | .then(result => { 203 | if ((!params || !params.custom) && result === true) 204 | this._isConnected = false; 205 | return resolve(result); 206 | }) 207 | .catch(err => reject(err)); 208 | }); 209 | } 210 | 211 | public isConnected(): Boolean { 212 | return this._isConnected; 213 | } 214 | 215 | public execCommand(params): Promise { 216 | return this.execRequest('Oracle/execCommand', params); 217 | } 218 | 219 | private install(): Promise { 220 | return this.execRequest('Oracle/install', null, true); 221 | } 222 | 223 | private init(): Promise { 224 | return this.execRequest('Oracle/init', null, true); 225 | } 226 | 227 | private execRequest(name: string, params?: any, initRequest: Boolean = false): Promise { 228 | return new Promise((resolve, reject) => { 229 | if (!this._client || !this._isClientReady) 230 | return reject('Client is not ready'); 231 | 232 | if (!initRequest && !this._isClientInitialized) 233 | return reject('Client is not initialized'); 234 | 235 | this._client.sendRequest(name, params) 236 | .then((data: any) => { 237 | if (data && data.error) 238 | return reject(data); 239 | return resolve(data); 240 | }, error => 241 | reject(error)); 242 | }); 243 | } 244 | 245 | } 246 | -------------------------------------------------------------------------------- /main/src/connect/connect.controller.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | import { PLSQLSettings, PLSQLConnection } from '../plsql.settings'; 4 | 5 | import events = require('events'); 6 | 7 | export class ConnectController { 8 | 9 | public readonly eventEmitter; 10 | private active: PLSQLConnection; 11 | private connections: PLSQLConnection[]; 12 | private activeInfos: string; 13 | private patternActiveInfos: string; 14 | private patternName: string; 15 | private ID = 1; 16 | private _internalSave = false; 17 | 18 | constructor() { 19 | this.eventEmitter = new events.EventEmitter(); 20 | } 21 | 22 | public configurationChanged() { 23 | if (this.connections && !this._internalSave) 24 | this.getConnections(true); 25 | else 26 | this._internalSave = false; 27 | } 28 | 29 | public getConnections(refresh?: boolean): PLSQLConnection[] { 30 | if (refresh || !this.connections) { 31 | const pattern = PLSQLSettings.getConnectionPattern(); 32 | this.patternActiveInfos = pattern.patternActiveInfos; 33 | this.patternName = pattern.patternName; 34 | 35 | this.connections = PLSQLSettings.getConnections(); 36 | delete this.active; 37 | this.active = this.getActive(); 38 | // force only one connection active 39 | this.connections.forEach(item => { 40 | item.active = item === this.active; 41 | item.ID = ++this.ID; 42 | item.name = this.getName(item); 43 | }); 44 | 45 | // recalc activeInfos, according to active connection and pattern 46 | this.updateActiveInfos(this.active); 47 | this.notifyActive(this.active); 48 | 49 | this.saveConnections(); 50 | } 51 | return this.connections; 52 | } 53 | 54 | public getConnectionByID(ID: number): PLSQLConnection { 55 | const connections = this.getConnections(); 56 | if (connections) 57 | return connections.find(c => c.ID === ID); 58 | } 59 | 60 | public getConnectionIndexByID(ID: number): number { 61 | const connections = this.getConnections(); 62 | if (connections) 63 | return connections.findIndex(c => c.ID === ID); 64 | } 65 | 66 | public updateActiveInfos(connection: PLSQLConnection) { 67 | this.activeInfos = this.getTextInfos(connection); 68 | } 69 | 70 | public getTextInfos(connection: PLSQLConnection): string { 71 | if (this.patternActiveInfos && connection) 72 | return this.patternActiveInfos 73 | .replace('${database}', connection.database) 74 | .replace('${username}', connection.username) 75 | .replace('${password}', connection.password) 76 | .replace('${schema}', connection.schema); 77 | else 78 | return ''; 79 | } 80 | 81 | public getActive(): PLSQLConnection { 82 | if (!this.active && this.connections) 83 | this.active = this.connections.find(item => item.active); 84 | return this.active; 85 | } 86 | 87 | public setActive(connection: PLSQLConnection, active: boolean) { 88 | let actConnection; 89 | 90 | if (active) { 91 | const element = this.getActive(); 92 | if (element) 93 | element.active = false; 94 | connection.active = true; 95 | this.active = connection; 96 | actConnection = connection; 97 | } else { 98 | connection.active = false; 99 | actConnection = null; 100 | } 101 | 102 | this.updateActiveInfos(actConnection); 103 | this.notifyActive(actConnection); 104 | } 105 | 106 | public getByTag(tag: string): PLSQLConnection { 107 | if (this.connections) 108 | return this.connections.find(item => item.tag === tag); 109 | } 110 | 111 | public addConnection(connection: PLSQLConnection) { 112 | connection.ID = ++this.ID; 113 | connection.name = this.getName(connection); 114 | 115 | if (connection.active) 116 | this.setActive(connection, true); 117 | this.connections.push(connection); 118 | this.saveConnections(); 119 | } 120 | 121 | public updateConnection(connection: PLSQLConnection) { 122 | connection.name = this.getName(connection); 123 | const idx = this.getConnectionIndexByID(connection.ID); 124 | 125 | if (this.connections[idx].active !== connection.active) 126 | this.setActive(connection, connection.active); 127 | 128 | this.connections[idx] = connection; 129 | this.saveConnections(); 130 | } 131 | 132 | public removeConnection(connection: PLSQLConnection | number) { 133 | if (typeof connection !== 'number') 134 | connection = this.connections.indexOf(connection); 135 | if (connection < 0) 136 | return; 137 | 138 | if (this.connections[connection].active) 139 | this.setActive(this.connections[connection], false); 140 | this.connections.splice(connection, 1); 141 | this.saveConnections(); 142 | } 143 | 144 | public saveConnections() { 145 | if (!this.connections) 146 | return; 147 | 148 | if (!this.connections.length) { 149 | const settings = PLSQLSettings.getConnections(); 150 | if (!settings || !settings.length) 151 | return; 152 | } 153 | 154 | this._internalSave = true; 155 | const config = vscode.workspace.getConfiguration('plsql-language'); 156 | // TODO if no workspace !... 157 | config.update('connections', this.connections, false); 158 | config.update('connection.activeInfos', this.activeInfos, false); 159 | } 160 | 161 | private getName(connection: PLSQLConnection) { 162 | if (this.patternName) 163 | return this.patternName 164 | .replace('${database}', connection.database) 165 | .replace('${username}', connection.username) 166 | .replace('${password}', connection.password) 167 | .replace('${schema}', connection.schema); 168 | else 169 | return `unknown ${connection.ID}`; 170 | } 171 | 172 | private notifyActive(connection: PLSQLConnection) { 173 | this.eventEmitter.emit('setActive', connection); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /main/src/connect/connect.inputPannel.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | import * as fs from 'fs'; 4 | import * as path from 'path'; 5 | 6 | import { ConnectController } from './connect.controller'; 7 | import { PLSQLConnection } from '../plsql.settings'; 8 | 9 | /** 10 | * Manages connect input webview panels 11 | */ 12 | export default class ConnectInputPanel { 13 | /** 14 | * Track the currently panel. Only allow a single panel to exist at a time. 15 | */ 16 | public static currentPanel: ConnectInputPanel | undefined; 17 | 18 | private static readonly viewType = 'ConnectInput'; 19 | private readonly _panel: vscode.WebviewPanel; 20 | private readonly _extensionPath: string; 21 | private _htmlContent: string; 22 | private _disposables: vscode.Disposable[] = []; 23 | 24 | 25 | public static createOrShow(extensionPath: string, controller: ConnectController, showAll: boolean) { 26 | // If we already have a panel, show it. 27 | // Otherwise, create a new panel. 28 | if (ConnectInputPanel.currentPanel) { 29 | ConnectInputPanel.currentPanel._panel.reveal(); 30 | } else { 31 | ConnectInputPanel.currentPanel = new ConnectInputPanel(extensionPath, controller, showAll); 32 | } 33 | } 34 | 35 | public sendData(connection ?: PLSQLConnection | number) { 36 | // Send a message to the webview webview. 37 | // You can send any JSON serializable data. 38 | const connections = this._controller.getConnections(); 39 | 40 | if (this._showAll && connection && (typeof connection === 'number') ) 41 | connection = this._controller.getConnectionByID(connection); 42 | 43 | this._panel.webview.postMessage({ 44 | command: this._showAll ? 'settingsConnections' : 'newConnection', 45 | data: this._showAll ? { 46 | connection: connection, 47 | items: this._controller.getConnections() 48 | } : null 49 | }); 50 | } 51 | 52 | public dispose() { 53 | ConnectInputPanel.currentPanel = undefined; 54 | 55 | // Clean up our resources 56 | this._panel.dispose(); 57 | 58 | while (this._disposables.length) { 59 | const x = this._disposables.pop(); 60 | if (x) { 61 | x.dispose(); 62 | } 63 | } 64 | } 65 | 66 | private constructor(extensionPath: string, private _controller: ConnectController, private _showAll: boolean) { 67 | this._extensionPath = extensionPath; 68 | 69 | // Create and show a new webview panel 70 | this._panel = vscode.window.createWebviewPanel(ConnectInputPanel.viewType, 'Connection', vscode.ViewColumn.Active, { 71 | // Enable javascript in the webview 72 | enableScripts: true, 73 | 74 | // And restric the webview to only loading content from our extension's `media` directory. 75 | localResourceRoots: [ 76 | vscode.Uri.file(path.join(this._extensionPath, 'resources', 'webview')) 77 | ] 78 | }); 79 | 80 | // Set the webview's initial html content 81 | this._update(); 82 | 83 | // Listen for when the panel is disposed 84 | // This happens when the user closes the panel or when the panel is closed programatically 85 | this._panel.onDidDispose(() => this.dispose(), null, this._disposables); 86 | 87 | // Update the content based on view changes 88 | this._panel.onDidChangeViewState(e => { 89 | if (this._panel.visible) { 90 | this._update(); 91 | } else 92 | this.dispose(); 93 | }, null, this._disposables); 94 | 95 | // Handle messages from the webview 96 | this._panel.webview.onDidReceiveMessage(message => { 97 | switch (message.command) { 98 | case 'submitConnection': 99 | if (!message.data.ID) 100 | this._controller.addConnection(message.data); 101 | else 102 | this._controller.updateConnection(message.data); 103 | if (!this._showAll) 104 | this.dispose(); 105 | else 106 | this.sendData(message.data.ID); 107 | return; 108 | case 'cancelConnection': 109 | if (!this._showAll) 110 | this.dispose(); 111 | else 112 | this.sendData(message.data.ID); 113 | return; 114 | case 'deleteConnection': 115 | const connection = this._controller.getConnectionIndexByID(message.data.ID); 116 | if (connection >= 0) 117 | this._controller.removeConnection(connection); 118 | const connections = this._controller.getConnections(); 119 | let nextID; 120 | if (connection < connections.length) 121 | nextID = connections[connection].ID; 122 | else if (connection > 0) 123 | nextID = connections[connection - 1].ID; 124 | this.sendData(nextID); 125 | return; 126 | case 'showConnection': 127 | this.sendData(message.data.ID); 128 | return; 129 | case 'newConnection': 130 | this.sendData(); 131 | return; 132 | } 133 | }, null, this._disposables); 134 | } 135 | 136 | private _update() { 137 | 138 | if (this._htmlContent) { 139 | this._panel.webview.html = this._htmlContent; 140 | this.sendData(this._showAll ? this._controller.getActive() : null); 141 | } else 142 | this._getHtmlForWebview() 143 | .then(html => { 144 | this._htmlContent = html; 145 | this._panel.webview.html = html; 146 | this.sendData(this._showAll ? this._controller.getActive() : null); 147 | }); 148 | } 149 | 150 | 151 | private _getHtmlForWebview(): Promise { 152 | 153 | // html file 154 | const htmlPathOnDisk = path.join(this._extensionPath, 'resources','webview', 'connect.html'); 155 | 156 | // Local path to main script, css run in the webview 157 | const scriptPathOnDisk = vscode.Uri.file(path.join(this._extensionPath, 'resources', 'webview', 'connect.js')); 158 | const cssPathOnDisk = vscode.Uri.file(path.join(this._extensionPath, 'resources', 'webview', 'connect.css')); 159 | 160 | // And the uri we use to load this script in the webview 161 | const scriptUri = scriptPathOnDisk.with({ scheme: 'vscode-resource' }); 162 | const cssUri = cssPathOnDisk.with({ scheme: 'vscode-resource' }); 163 | 164 | // Use a nonce to whitelist which scripts can be run 165 | 166 | return this.readHTMLFile(htmlPathOnDisk, scriptUri, cssUri, this.getNonce()); 167 | } 168 | 169 | private readHTMLFile(file: string, scriptUri, cssUri, nonce): Promise { 170 | return new Promise((resolve, reject) => { 171 | fs.readFile(file, 'utf8', (err, data) => { 172 | if (err) 173 | return reject(err); 174 | const html = data.toString() 175 | .replace('${scriptUri}', scriptUri) 176 | .replace('${cssUri}', cssUri) 177 | .replace(/\${nonce}/g, nonce); 178 | return resolve(html); 179 | }); 180 | }); 181 | } 182 | 183 | private getNonce() { 184 | let text = ''; 185 | const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; 186 | for (let i = 0; i < 32; i++) { 187 | text += possible.charAt(Math.floor(Math.random() * possible.length)); 188 | } 189 | return text; 190 | } 191 | 192 | } 193 | -------------------------------------------------------------------------------- /main/src/connect/connect.statusBar.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { ConnectController } from './connect.controller'; 3 | import { PLSQLConnection } from '../plsql.settings'; 4 | 5 | export class ConnectStatusBar { 6 | 7 | private statusBar; 8 | private statusBarVisible; 9 | 10 | constructor(private controller: ConnectController) { 11 | const me = this; 12 | me.statusBar = vscode.window.createStatusBarItem(); 13 | controller.eventEmitter.on('setActive', (connection) => me.activeChange(connection)); 14 | controller.getConnections(); 15 | me.statusBar.command = 'plsql.activateConnection'; 16 | } 17 | 18 | private activeChange(connection: PLSQLConnection) { 19 | if (connection) { 20 | this.statusBar.text = `$(database) ${connection.name}`; 21 | // this.statusBar.tooltip =; 22 | } else { 23 | this.statusBar.text = `$(database) `; 24 | // this.statusBar.tooltip =; 25 | } 26 | 27 | if (!this.statusBarVisible && connection) { 28 | this.statusBar.show(); 29 | this.statusBarVisible = true; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /main/src/connect/connectUI.controller.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | import { ConnectController} from './connect.controller'; 4 | import { PLSQLConnection } from '../plsql.settings'; 5 | import ConnectInputPanel from './connect.inputPannel'; 6 | 7 | export default class ConnectUIController { 8 | 9 | constructor(private context: vscode.ExtensionContext, private controller: ConnectController) { 10 | } 11 | 12 | public activateConnectionsList() { 13 | const me = this; 14 | 15 | let connections: PLSQLConnection[]; 16 | const active = this.controller.getActive(); 17 | if (active) { 18 | connections = this.controller.getConnections().filter(item => item !== active); 19 | connections.unshift(active); 20 | } else 21 | connections = this.controller.getConnections(); 22 | 23 | const displayItems = connections 24 | .filter(item => item.active || !item.hidden) 25 | .map(item => { 26 | return { 27 | label: `${item.active ? '$(check) ' : ''} ${item.name}`, 28 | item: item, 29 | action: 'setActive' 30 | }; 31 | }); 32 | displayItems.push({ 33 | label: '', 34 | item: undefined, 35 | action: 'addConnection' 36 | }); 37 | displayItems.push({ 38 | label: '', 39 | item: undefined, 40 | action: 'showSettings' 41 | }); 42 | 43 | vscode.window.showQuickPick(displayItems) 44 | .then(val => { 45 | if (val) { 46 | me[val.action].apply(me, [val.item]); 47 | } 48 | }); 49 | } 50 | 51 | // used via displayItem.action 52 | /*private*/ setActive(connection: PLSQLConnection) { 53 | this.controller.setActive(connection, true); 54 | this.controller.saveConnections(); 55 | } 56 | /*private*/ addConnection() { 57 | ConnectInputPanel.createOrShow(this.context.extensionPath, this.controller, false); 58 | } 59 | /*private*/ showSettings() { 60 | // vscode.commands.executeCommand('workbench.action.openSettings'); 61 | ConnectInputPanel.createOrShow(this.context.extensionPath, this.controller, true); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /main/src/extension.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | import { PLSQLDefinitionProvider } from './provider/plsqlDefinition.provider'; 4 | import { PLSQLDocumentSymbolProvider } from './provider/plsqlDocumentSymbol.provider'; 5 | import { PLSQLCompletionItemProvider } from './provider/plsqlCompletionItem.provider'; 6 | import { PLSQLHoverProvider } from './provider/plsqlHover.provider'; 7 | import { PLSQLSignatureProvider } from './provider/plsqlSignature.provider'; 8 | 9 | import { PLSQLSettings } from './plsql.settings'; 10 | 11 | import { ConnectController } from './connect/connect.controller'; 12 | import ConnectUIController from './connect/connectUI.controller'; 13 | import { ConnectStatusBar } from './connect/connect.statusBar'; 14 | import { QueryController } from './query/query.controller'; 15 | import { OracleService } from './client-oracle/oracle.server'; 16 | 17 | export function activate(context: vscode.ExtensionContext) { 18 | 19 | // Default without $# redefinded here 20 | // because plsql.configuration.json don't work with getWordRangeAtPosition() according to issue #42649 21 | vscode.languages.setLanguageConfiguration('plsql', { 22 | wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\%\^\&\*\(\)\-\=\+\[\{\]\}\|\;\:\'\"\,\.\<\>\/\?\s]+)/ 23 | }); 24 | 25 | let hoverProvider, signatureHelpProvider; 26 | 27 | // language providers 28 | activateHover(); 29 | activateSignatureHelp(); 30 | 31 | // Oracle connection 32 | activateOracleConnection(); 33 | 34 | context.subscriptions.push(vscode.languages.registerCompletionItemProvider('plsql', new PLSQLCompletionItemProvider(), '.', '\"')); 35 | context.subscriptions.push(vscode.languages.registerDefinitionProvider('plsql', new PLSQLDefinitionProvider())); 36 | 37 | // context.subscriptions.push(vscode.languages.registerReferenceProvider('plsql', new PLSQLReferenceProvider())); 38 | // context.subscriptions.push(vscode.languages.registerDocumentFormattingEditProvider('plsql', new PLSQLDocumentFormattingEditProvider())); 39 | context.subscriptions.push(vscode.languages.registerDocumentSymbolProvider('plsql', new PLSQLDocumentSymbolProvider())); 40 | // context.subscriptions.push(vscode.languages.registerWorkspaceSymbolProvider(new PLSQLWorkspaceSymbolProvider())); 41 | // context.subscriptions.push(vscode.languages.registerRenameProvider('plsql', new PLSQLRenameProvider())); 42 | // context.subscriptions.push(vscode.languages.registerCodeActionsProvider('plsql', new PLSQLCodeActionProvider())); 43 | 44 | // Connection 45 | const connectController = new ConnectController(); 46 | const connectStatusBar = new ConnectStatusBar(connectController); 47 | const connectUIController = new ConnectUIController(context, connectController); 48 | context.subscriptions.push(vscode.commands.registerCommand('plsql.activateConnection', 49 | connectUIController.activateConnectionsList, connectUIController)); 50 | 51 | // Query 52 | const queryController = new QueryController(context, connectController); 53 | context.subscriptions.push(vscode.commands.registerCommand('plsql.executeCommand', 54 | queryController.executeCommand, queryController)); 55 | context.subscriptions.push(vscode.commands.registerCommand('plsql.createConnection', 56 | queryController.createConnection, queryController)); 57 | context.subscriptions.push(vscode.commands.registerCommand('plsql.removeConnection', 58 | queryController.removeConnection, queryController)); 59 | // context.subscriptions.push(vscode.commands.registerTextEditorCommand('plsql.runScript', 60 | // queryController.runScript, queryController)); 61 | context.subscriptions.push(vscode.commands.registerTextEditorCommand('plsql.runQuery', 62 | queryController.runQuery, queryController)); 63 | 64 | vscode.workspace.onDidChangeConfiguration(configChangedEvent => { 65 | if (!configChangedEvent.affectsConfiguration('plsql-language')) 66 | return; 67 | 68 | connectController.configurationChanged(); 69 | 70 | if (configChangedEvent.affectsConfiguration('plsql-language.signatureHelp')) 71 | activateSignatureHelp(); 72 | if (configChangedEvent.affectsConfiguration('plsql-language.hover')) 73 | activateHover(); 74 | if (configChangedEvent.affectsConfiguration('plsql-language.oracleConnection.enable')) 75 | activateOracleConnection(); 76 | }); 77 | 78 | function activateHover() { 79 | const enable = PLSQLSettings.getHoverEnable(); 80 | 81 | if (!hoverProvider && enable) { 82 | hoverProvider = new PLSQLHoverProvider(); 83 | context.subscriptions.push(vscode.languages.registerHoverProvider('plsql', hoverProvider)); 84 | } 85 | if (hoverProvider) 86 | hoverProvider.enable = enable; 87 | } 88 | 89 | function activateSignatureHelp() { 90 | const enable = PLSQLSettings.getSignatureEnable(); 91 | 92 | if (!signatureHelpProvider && enable) { 93 | signatureHelpProvider = new PLSQLSignatureProvider(); 94 | context.subscriptions.push(vscode.languages.registerSignatureHelpProvider('plsql', signatureHelpProvider, '(', ',')); 95 | } 96 | if (signatureHelpProvider) 97 | signatureHelpProvider.enable = enable; 98 | } 99 | 100 | function activateOracleConnection() { 101 | const enable = PLSQLSettings.getOracleConnectionEnable(); 102 | OracleService.activate(enable, context.asAbsolutePath('')); 103 | } 104 | } 105 | 106 | export function deactivate() { 107 | OracleService.activate(false, '', true); 108 | } 109 | -------------------------------------------------------------------------------- /main/src/lib/definition.ts: -------------------------------------------------------------------------------- 1 | interface PLSQLRoot { 2 | fileName: string; 3 | symbols: PLSQLSymbol[]; 4 | } 5 | 6 | interface PLSQLSymbol { 7 | name: string; 8 | definition?: string; 9 | documentation?: string; 10 | formatedDoc?: { 11 | isMarkdown: boolean, 12 | text: string 13 | }; 14 | offset?: number; 15 | offsetEnd?: number; 16 | kind: PLSQLSymbolKind; 17 | kindName: string; 18 | params?: PLSQLParam[]; 19 | symbols?: PLSQLSymbol[]; 20 | parent?: PLSQLSymbol; 21 | root?: PLSQLRoot; 22 | } 23 | 24 | interface PLSQLParam { 25 | text: string; 26 | name?: string; 27 | type: string; 28 | kind: PLSQLParamKind; 29 | } 30 | 31 | const enum PLSQLSymbolKind { 32 | packageSpec = 1, 33 | packageBody, 34 | function, 35 | functionSpec, 36 | procedure, 37 | procedureSpec, 38 | variable, 39 | constant, 40 | type, 41 | subtype, 42 | cursor, 43 | exception, 44 | trigger, 45 | view, 46 | table 47 | } 48 | 49 | const enum PLSQLParamKind { 50 | none = 0, 51 | return, 52 | in, 53 | out, 54 | inout 55 | } 56 | -------------------------------------------------------------------------------- /main/src/lib/docFormater.ts: -------------------------------------------------------------------------------- 1 | // import 2 | 3 | export default class DocFormater { 4 | 5 | public static format(doc: string, useJsDoc: boolean): string { 6 | if (!doc) 7 | return; 8 | 9 | if (useJsDoc) 10 | return this.formatToMarkdown(doc); 11 | else 12 | return this.formatToText(doc); 13 | } 14 | 15 | private static formatToText(doc: string): string { 16 | // remove /** */ and * at begin of line 17 | const regExpFormat = /\*\/|\/\*\*?|^[\t ]*\*[\t \/]?/gim; 18 | return doc.replace(regExpFormat, '').trim().replace(/^\s+$/gmi, ''); 19 | } 20 | 21 | private static formatToMarkdown(doc: string): string { 22 | doc = this.formatToText(doc); 23 | 24 | const regExpFormat = /([\r\n])?(@param|@return|@\w+)[\t ]*:?[\t ]*({[\w%$#]+})?[\t ]*(\w*)[\t ]*(\w*)/gi; 25 | return doc.replace(regExpFormat, (match, br, name, type, desc1, desc2) => { 26 | let result = `_${name}_ `; 27 | if (br) 28 | result = '\n\n'+result; //double \n to force new line 29 | if (type) 30 | result += ` **${type}**`; 31 | if (name && name.toLowerCase() === '@param') { 32 | if (desc1) // param name 33 | result += ` \`${desc1}\``; 34 | if (desc2) 35 | result += ` - ${desc2}`; 36 | } else { 37 | if (desc1) 38 | result += ` - ${desc1}`; 39 | if (desc2) 40 | result += ` ${desc2}`; 41 | } 42 | return result; 43 | }); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /main/src/lib/plsqlParser.ts: -------------------------------------------------------------------------------- 1 | import RegExpParser from './regEx/RegExParser'; 2 | import DocFormater from './docFormater'; 3 | import './definition'; 4 | 5 | // import AntlrParser from './Antlr/AntlrParser'; 6 | 7 | interface PLSQLInfos { 8 | packageName: string; 9 | spec?: PLSQLSymbol; 10 | body?: PLSQLSymbol; 11 | } 12 | 13 | interface PLSQLRange { 14 | start: number; 15 | end: number; 16 | } 17 | 18 | export default class PlSqlParser { 19 | 20 | public static initParser(symbolsComment?: boolean) { 21 | this.getParser().initParser(symbolsComment); 22 | } 23 | 24 | public static parseFile(fileName: string, content: string): PLSQLRoot { 25 | const root = this.getParser().getSymbols(content); 26 | if (root) 27 | root.fileName = fileName; 28 | return root; 29 | } 30 | 31 | public static parseParams(symbol: PLSQLSymbol): PLSQLParam[] { 32 | return this.getParser().parseParams(symbol); 33 | } 34 | 35 | public static findSymbolByNameOffset(symbols: PLSQLSymbol[], name: string, offset = 0, recursive = true): PLSQLSymbol { 36 | // find first symbol after given offset 37 | const lower = name.toLowerCase(); 38 | return this.findSymbol(symbols, symbol => (symbol.name.toLowerCase() === lower && symbol.offset >= offset), recursive); 39 | } 40 | 41 | public static findSymbolNearOffset(symbols: PLSQLSymbol[], offset: number, recursive = true): PLSQLSymbol { 42 | // find last symbol with offset smaller than given offset 43 | let nearSymbol; 44 | this.findSymbol(symbols, (symbol) => { 45 | const result = (symbol.offset > offset); 46 | if (!result) 47 | nearSymbol = symbol; 48 | return result; 49 | } , recursive); 50 | 51 | return nearSymbol; 52 | } 53 | 54 | public static findSymbolByNameKind(symbols: PLSQLSymbol[], name: string, kind: PLSQLSymbolKind|PLSQLSymbolKind[], recursive = true): PLSQLSymbol { 55 | const lower = name.toLowerCase(), 56 | kindArray = Array.isArray(kind) ? kind : [kind]; 57 | return this.findSymbol(symbols, symbol => (symbol.name.toLowerCase() === lower && kindArray.includes(symbol.kind)), recursive); 58 | } 59 | 60 | public static getSymbolsDeclaration(root: PLSQLRoot): PLSQLSymbol[] { 61 | const allSymbols: PLSQLSymbol[] = []; 62 | 63 | this.forEachSymbol(root.symbols, symbol => { 64 | if (![PLSQLSymbolKind.function, PLSQLSymbolKind.procedure, 65 | PLSQLSymbolKind.packageBody, PLSQLSymbolKind.packageSpec].includes(symbol.kind)) 66 | allSymbols.push(symbol); 67 | }); 68 | 69 | return allSymbols; 70 | } 71 | 72 | // Body to Spec and Spec to Body 73 | public static switchSymbol(symbol: PLSQLSymbol): PLSQLSymbol { 74 | let kind; 75 | if (symbol.kind === PLSQLSymbolKind.packageSpec) 76 | kind = PLSQLSymbolKind.packageBody; 77 | else if (symbol.kind === PLSQLSymbolKind.packageBody) 78 | kind = PLSQLSymbolKind.packageSpec; 79 | else 80 | return symbol; // not a package 81 | return this.findSymbolByNameKind(symbol.root.symbols, symbol.name, kind, false); 82 | } 83 | 84 | // Body to Spec and Spec to Body 85 | public static switchSymbolKind(symbolKind: PLSQLSymbolKind): PLSQLSymbolKind { 86 | if (symbolKind === PLSQLSymbolKind.functionSpec) 87 | return PLSQLSymbolKind.function; 88 | else if (symbolKind === PLSQLSymbolKind.function) 89 | return PLSQLSymbolKind.functionSpec; 90 | else if (symbolKind === PLSQLSymbolKind.procedureSpec) 91 | return PLSQLSymbolKind.procedure; 92 | else if (symbolKind === PLSQLSymbolKind.procedure) 93 | return PLSQLSymbolKind.procedureSpec; 94 | else if (symbolKind === PLSQLSymbolKind.packageSpec) 95 | return PLSQLSymbolKind.packageBody; 96 | else if (symbolKind === PLSQLSymbolKind.packageBody) 97 | return PLSQLSymbolKind.packageSpec; 98 | else 99 | return symbolKind; 100 | } 101 | 102 | public static isSymbolSpec(symbol: PLSQLSymbol): boolean { 103 | return [PLSQLSymbolKind.packageSpec, PLSQLSymbolKind.procedureSpec, PLSQLSymbolKind.functionSpec] 104 | .includes(symbol.kind); 105 | } 106 | 107 | public static getSymbols(fileName: string, content: string): PLSQLSymbol[] { 108 | return this.getSymbolsFromRoot(this.parseFile(fileName, content)); 109 | } 110 | 111 | public static getSymbolsFromRoot(root: PLSQLRoot): PLSQLSymbol[] { 112 | const allSymbols: PLSQLSymbol[] = []; 113 | 114 | this.forEachSymbol(root.symbols, symbol => { 115 | allSymbols.push(symbol); 116 | }); 117 | 118 | return allSymbols; 119 | } 120 | 121 | public static getSymbolFileName(symbol: PLSQLSymbol) { 122 | while (symbol.parent) 123 | symbol = symbol.parent; 124 | return symbol.root.fileName; 125 | } 126 | 127 | public static formatSymbolDocumentation(symbol: PLSQLSymbol, useJSDoc: boolean) { 128 | if (symbol.documentation && !symbol.formatedDoc) { 129 | symbol.formatedDoc = { 130 | text: DocFormater.format(symbol.documentation, useJSDoc), 131 | isMarkdown: useJSDoc 132 | }; 133 | } 134 | } 135 | 136 | private static forEachSymbol(symbols: PLSQLSymbol[], fn) { 137 | if (symbols) 138 | symbols.forEach(symbol => { 139 | fn.apply(this, [symbol]); 140 | this.forEachSymbol(symbol.symbols, fn); 141 | }); 142 | } 143 | 144 | private static findSymbol(symbols: PLSQLSymbol[], fn, recursive = true): PLSQLSymbol { 145 | if (!symbols) 146 | return; 147 | 148 | let result: PLSQLSymbol; 149 | for (let symbol of symbols) { 150 | if (fn.apply(this, [symbol])) 151 | return symbol; 152 | if (recursive) { 153 | result = this.findSymbol(symbol.symbols, fn); 154 | if (result) 155 | return result; 156 | } 157 | } 158 | } 159 | 160 | private static getParser() { 161 | return RegExpParser; 162 | // return AntlrParser; 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /main/src/pldoc.controller.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | import * as fs from 'fs'; 4 | import * as json5 from 'json5'; 5 | import * as dateFormat from 'dateformat'; 6 | 7 | import { PLSQLSettings } from './plsql.settings'; 8 | 9 | interface IPlDocObject { 10 | type: string; 11 | name: string; 12 | dataType?: string; 13 | params?: IPlDocParam[]; 14 | } 15 | 16 | interface IPlDocParam { 17 | name: string; 18 | dataType?: string; 19 | } 20 | 21 | interface IPlDocVariablesCustom { 22 | author: string; 23 | date: Date; 24 | } 25 | 26 | interface IPlDocVariables extends IPlDocVariablesCustom { 27 | type: string; 28 | object: string; 29 | } 30 | 31 | interface IPlDocVariablesDef { 32 | regFindVar: RegExp; 33 | values: IPlDocVariables; 34 | shift?: number; 35 | offset?: number; 36 | } 37 | 38 | interface IPlDocSnippet { 39 | prefix: string; 40 | body: string[]; 41 | description?: string; 42 | } 43 | 44 | interface IPlDocTemplate extends IPlDocSnippet { 45 | paramIndex?: number; 46 | paramMaxVar?: number; 47 | paramVarCount?: number; 48 | returnIndex?: number; 49 | } 50 | 51 | /** 52 | * Controller for handling PlDoc. 53 | */ 54 | export class PLDocController { 55 | 56 | private plDocTemplate: IPlDocTemplate; 57 | private plDocSnippets: IPlDocSnippet[]; 58 | private plDocAuthor: string; 59 | private plDocEnable: boolean; 60 | 61 | constructor() { 62 | } 63 | 64 | public getDocSnippet(document: vscode.TextDocument, text: string): IPlDocSnippet { 65 | this.init(document.uri); 66 | 67 | if (this.plDocEnable && this.plDocTemplate) { 68 | let plDocObj = this.getInfo(text); 69 | if (plDocObj) 70 | return this.buildTemplate(plDocObj, this.plDocTemplate); 71 | } 72 | } 73 | 74 | public getCustomSnippets(document: vscode.TextDocument): IPlDocSnippet[] { 75 | this.init(document.uri); 76 | return this.plDocSnippets; 77 | } 78 | 79 | private init(file: vscode.Uri) { 80 | // TODO: different plDoc for different workspaceFolders ? 81 | if (this.plDocEnable == null) { 82 | const {enable, author, location} = PLSQLSettings.getDocInfos(file); 83 | this.plDocEnable = enable; 84 | this.plDocAuthor = author; 85 | this.initTemplates(location); 86 | } 87 | } 88 | 89 | private getRegFindVar(): RegExp { 90 | return /\$(?:{)?(\d+)/gi; 91 | } 92 | private getRegFindVarParam(): RegExp { 93 | return new RegExp(`\\\${pldoc_${'param'}}`, 'i'); 94 | } 95 | private getRegFindVarParamType(): RegExp { 96 | return new RegExp(`\\\${pldoc_${'param_type'}}`, 'i'); 97 | } 98 | private getRegFindVarDoc(key: string): RegExp { 99 | return new RegExp(`\\\${pldoc_(${key})(?:(?:\\s*\\|\\s*)([^}]*))?}`, 'i'); 100 | } 101 | private getRegFindReturn(): RegExp { 102 | return /\breturn\b/i; // @return 103 | } 104 | 105 | private getInfo(text: string): IPlDocObject { 106 | let plDocObj: IPlDocObject; 107 | const regex = /(function|procedure)\s*(\w+)\s*(\([\s\S]*?\))?(?:\s*(return))?/i; 108 | let found = regex.exec(text); 109 | if (found && found.length > 0) { 110 | 111 | // Function or Procedure 112 | plDocObj = { 113 | type: found[1].toLowerCase(), 114 | name: found[2], 115 | params: [] 116 | }; 117 | 118 | // Params 119 | const params = found[3], 120 | regexParams = /(?:\(|,)\s*(\w+)\s*((?:in\s*out|in|out)?(?:\s*)?\w*)/g; 121 | if (params !== '') { 122 | while (found = regexParams.exec(params)) { 123 | if (found.length > 0) 124 | plDocObj.params.push({name: found[1], dataType: found[2]}); 125 | } 126 | } 127 | } 128 | 129 | return plDocObj; 130 | } 131 | 132 | private initTemplates(location) { 133 | 134 | let parsedJSON; 135 | try { 136 | parsedJSON = json5.parse(fs.readFileSync(location).toString()); // invalid JSON or permission issue can happen here 137 | } catch (error) { 138 | console.error(error); 139 | return; 140 | } 141 | 142 | if (parsedJSON) { 143 | 144 | const variables: IPlDocVariablesCustom = { 145 | author: this.plDocAuthor, 146 | date: new Date() 147 | }; 148 | 149 | Object.keys(parsedJSON).forEach(key => { 150 | // Doc 151 | if (key === 'pldoc') { 152 | if (this.plDocEnable && parsedJSON.pldoc.body) { 153 | this.plDocTemplate = parsedJSON.pldoc; 154 | this.addTemplateInfo(this.plDocTemplate); 155 | } 156 | else 157 | this.plDocTemplate = null; 158 | } else { // Other custom snippet 159 | const snippet = parsedJSON[key]; 160 | snippet.body.forEach( (text, index) => 161 | snippet.body[index] = this.replaceText(variables, text) 162 | ); 163 | if (!this.plDocSnippets) 164 | this.plDocSnippets = []; 165 | this.plDocSnippets.push(snippet); 166 | } 167 | }); 168 | } 169 | } 170 | 171 | private addTemplateInfo(template: IPlDocTemplate) { 172 | 173 | // Find index of params line 174 | const regFindParam = this.getRegFindVarParam(), 175 | regFindVar = this.getRegFindVar(), 176 | regFindReturn = this.getRegFindReturn(); 177 | 178 | let found; 179 | template.body.forEach( (text, index) => { 180 | if (template.paramIndex == null) { 181 | found = regFindParam.exec(text); 182 | if (found) { 183 | template.paramIndex = index; 184 | template.paramMaxVar = 0; 185 | template.paramVarCount = 0; 186 | let foundVar, numberVar = 0; 187 | while (foundVar = regFindVar.exec(text)) { 188 | ++template.paramVarCount; 189 | numberVar = Number(foundVar[1]); 190 | if (template.paramMaxVar < numberVar) 191 | template.paramMaxVar = numberVar; 192 | } 193 | } 194 | } 195 | if (template.returnIndex == null) { 196 | found = regFindReturn.exec(text); 197 | if (found) 198 | template.returnIndex = index; 199 | } 200 | }); 201 | } 202 | 203 | private buildTemplate(plDocObj: IPlDocObject, template: IPlDocTemplate): IPlDocTemplate { 204 | let body: string[] = []; 205 | 206 | const variables: IPlDocVariablesDef = { 207 | regFindVar: this.getRegFindVar(), 208 | values: { 209 | type: plDocObj.type, 210 | object: plDocObj.name, 211 | author: this.plDocAuthor, 212 | date: new Date() 213 | }, 214 | shift: plDocObj.params.length > 1 ? (plDocObj.params.length - 1)*template.paramVarCount : 0, 215 | offset: template.paramMaxVar 216 | }; 217 | 218 | template.body.forEach( (text, index) => { 219 | let lineText = text; 220 | if (index !== template.paramIndex) { 221 | if (index !== template.returnIndex || plDocObj.type === 'function') { 222 | lineText = this.replaceText(variables.values, lineText); 223 | lineText = this.shiftVariables(variables, lineText, template); 224 | body.push(lineText); 225 | } 226 | } else { 227 | plDocObj.params.forEach( (param, paramIndex) => { 228 | let paramText = lineText; 229 | paramText = this.replaceTextParam(param, paramText); 230 | if (paramIndex > 0) 231 | paramText = this.shiftParamVariables(variables, paramText); 232 | body.push(paramText); 233 | }); 234 | } 235 | }); 236 | 237 | if (body.length > 0) 238 | return { 239 | prefix: template.prefix, 240 | body: body, 241 | description: template.description 242 | }; 243 | } 244 | 245 | private replaceText(variables: IPlDocVariablesCustom, text: string): string { 246 | // replace special variables values 247 | Object.keys(variables).forEach(key => { 248 | text = text.replace(this.getRegFindVarDoc(key), (match, p1, p2) => { 249 | if (!p1 || (p1.toLowerCase() !== 'date')) 250 | return variables[key]; 251 | else { 252 | // replace date 253 | if (!p2 || (p2.trim() === '')) 254 | return variables.date.toLocaleDateString(); 255 | else 256 | return dateFormat(variables.date, p2); 257 | } 258 | }); 259 | }); 260 | return text; 261 | } 262 | 263 | private shiftVariables(variables: IPlDocVariablesDef, text: string, template: IPlDocTemplate): string { 264 | if (variables.shift > 0) { 265 | text = text.replace(variables.regFindVar, (match, p1) => { 266 | if (Number(p1) > template.paramMaxVar) { 267 | // Shift variables $n or ${n:xxx} 268 | if (match.startsWith('${')) 269 | return '${'+String(variables.shift+Number(p1)); 270 | else //if (match.startsWith('$')) 271 | return '$'+String(variables.shift+Number(p1)); 272 | } else 273 | return match; 274 | }); 275 | } 276 | return text; 277 | } 278 | 279 | private replaceTextParam(param: IPlDocParam, text: string): string { 280 | // replace special variables values 281 | return text.replace(this.getRegFindVarParam(), param.name) 282 | .replace(this.getRegFindVarParamType(), param.dataType); 283 | } 284 | 285 | private shiftParamVariables(variables: IPlDocVariablesDef, text: string): string { 286 | if (variables.offset != null) { 287 | text = text.replace(variables.regFindVar, (match, p1) => { 288 | // Shift variables $n or ${n:xxx} 289 | if (match.startsWith('${')) 290 | return '${'+String(++variables.offset); 291 | else //if (match.startsWith('$')) 292 | return '$'+String(++variables.offset); 293 | }); 294 | } 295 | return text; 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /main/src/plsql.settings.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as path from 'path'; 3 | 4 | interface PLSQLSynonym { 5 | replace: string; 6 | by?: string; 7 | } 8 | 9 | export interface PLSQLConnection { 10 | database: string; 11 | username: string; 12 | password?: string; 13 | privilege?: string; 14 | schema?: string; 15 | active?: boolean; 16 | hidden?: boolean; 17 | loginScript?: string; 18 | ID?: number; 19 | name?: string; 20 | tag?: string; 21 | } 22 | 23 | /** 24 | * Settings for plsql. 25 | */ 26 | export class PLSQLSettings { 27 | 28 | // constructor() { 29 | // } 30 | 31 | /** 32 | * SearchPaths: 33 | * Search in all ws null (default) 34 | * Search in same ws (${workspaceFolder}) 35 | * Search in specific folder (D:/Path...) 36 | * Search in specific ws (${workspaceFolder: name|index}) 37 | * Array => ['D:/Path', ${workspaceFolder}] 38 | */ 39 | public static getSearchInfos(file: vscode.Uri) { 40 | // ignore search.exclude settings 41 | let ignore; 42 | const searchExclude = vscode.workspace.getConfiguration('search', file).get('exclude'); 43 | if (searchExclude) { 44 | ignore = Object.keys(searchExclude).filter(key => searchExclude[key]); 45 | } 46 | 47 | const config = vscode.workspace.getConfiguration('plsql-language'); 48 | 49 | let searchFld = >config.get('searchPaths'); 50 | if (searchFld) { 51 | if (!Array.isArray(searchFld)) 52 | searchFld = [searchFld]; 53 | 54 | searchFld = searchFld.map(folder => { 55 | // start with 56 | // ${workspaceFolder} => current workspace 57 | // ${workspaceFolder: name} => workspace find by name 58 | // ${workspaceFolder: index} => workspace find by index 59 | const match = folder.match(/\${workspaceFolder(?:\s*:\s*(.*))?}/i); 60 | if (match) { 61 | if (vscode.workspace.workspaceFolders && match && match.index === 0) { 62 | const wsId = match[1]; 63 | if (wsId) { 64 | const find = vscode.workspace.workspaceFolders.find(ws => 65 | Number.isInteger(wsId - 1) ? ws.index === Number.parseInt(wsId, 10) : ws.name === wsId); 66 | if (find) 67 | return folder.replace(match[0], find.uri.fsPath); 68 | } else { 69 | const wsFolder = vscode.workspace.getWorkspaceFolder(file); 70 | if (wsFolder) 71 | return folder.replace('${workspaceFolder}', wsFolder.uri.fsPath); 72 | } 73 | } 74 | return ''; 75 | }; 76 | return folder; 77 | }).filter(folder => folder !== ''); 78 | } else if (vscode.workspace.workspaceFolders) // search in all workspaces 79 | searchFld = vscode.workspace.workspaceFolders.map(ws => ws.uri.fsPath); 80 | else 81 | searchFld = ['']; 82 | 83 | return {ignore, searchFld}; 84 | } 85 | 86 | 87 | public static translatePackageName(packageName: string): string { 88 | const config = vscode.workspace.getConfiguration('plsql-language'); 89 | 90 | // packageName using synonym => real packageName 91 | let name = packageName; 92 | const synonym = config.get('synonym'); 93 | if (synonym) { 94 | const regExp = new RegExp(synonym.replace, 'i'); 95 | name = name.replace(regExp, synonym.by || ''); 96 | } 97 | return name; 98 | } 99 | 100 | public static getCommentInSymbols(): boolean { 101 | const config = vscode.workspace.getConfiguration('plsql-language'); 102 | return config.get('commentInSymbols'); 103 | } 104 | 105 | public static getHoverEnable(): boolean { 106 | const config = vscode.workspace.getConfiguration('plsql-language'); 107 | return config.get('hover.enable'); 108 | } 109 | public static getSignatureEnable(): boolean { 110 | const config = vscode.workspace.getConfiguration('plsql-language'); 111 | return config.get('signatureHelp.enable'); 112 | } 113 | public static getOracleConnectionEnable(): boolean { 114 | const config = vscode.workspace.getConfiguration('plsql-language'); 115 | return config.get('oracleConnection.enable'); 116 | } 117 | 118 | public static getSearchExt(searchExt: string[]) { 119 | 120 | // copy of package.json 121 | const DEFAULT_EXT = 122 | ['sql','ddl','dml','pkh','pks','pkb','pck','pls','plb', 123 | 'bdy','fnc','idx','mv','prc','prg','sch','seq','spc','syn','tab','tbl','tbp','tps','trg','typ','vw']; 124 | 125 | let allExt = [...new Set([...searchExt, ...DEFAULT_EXT])]; // (merge and remove duplicate) 126 | 127 | const config = vscode.workspace.getConfiguration('files', null), 128 | assoc = config.get('associations'); 129 | 130 | if (assoc) { 131 | const assocExt = [], otherExt = []; 132 | Object.keys(assoc).forEach(key => 133 | (assoc[key] === 'plsql' ? assocExt : otherExt).push(key.replace(/^\*./,'').toLowerCase()) 134 | ); 135 | // Remove ext associated with another language 136 | if (otherExt.length) 137 | allExt = allExt.filter(item => otherExt.indexOf(item) === -1); 138 | // Add ext associated with plsql (remove duplicate) 139 | if (assocExt.length) 140 | allExt = [...new Set([...allExt, ...assocExt])]; 141 | } 142 | 143 | return allExt; 144 | } 145 | 146 | public static getDocInfos(file: vscode.Uri) { 147 | const config = vscode.workspace.getConfiguration('plsql-language'), 148 | enable = config.get('pldoc.enable'), 149 | author = config.get('pldoc.author'); 150 | 151 | let location = config.get('pldoc.path'); 152 | if (!location) 153 | location = path.join(__dirname, '../../../snippets/pldoc.json'); 154 | else { 155 | // const wsFolder = vscode.workspace.getWorkspaceFolder(file); 156 | // temporary code to resolve bug https://github.com/Microsoft/vscode/issues/36221 157 | const wsFolder = vscode.workspace.getWorkspaceFolder(vscode.Uri.file(file.fsPath)); 158 | const cwd = wsFolder ? wsFolder.uri.fsPath : ''; 159 | location = location.replace('${workspaceRoot}', cwd); // deprecated 160 | location = location.replace('${workspaceFolder}', cwd); 161 | location = path.join(location, 'pldoc.json'); 162 | } 163 | 164 | return {enable, author, location}; 165 | } 166 | 167 | public static getCompletionPath(wsFolder: vscode.Uri): string { 168 | const config = vscode.workspace.getConfiguration('plsql-language'); 169 | 170 | let location = config.get('completion.path'); 171 | if (location) { 172 | const cwd = wsFolder ? wsFolder.fsPath : ''; 173 | // location = location.replace('${workspaceRoot}', cwd); // deprecated 174 | location = location.replace('${workspaceFolder}', cwd); 175 | if (location) 176 | location = path.join(location, 'plsql.completion.json'); 177 | } 178 | 179 | return location; 180 | } 181 | 182 | // global config 183 | public static getConnections(): PLSQLConnection[] { 184 | const config = vscode.workspace.getConfiguration('plsql-language'); 185 | return config.get('connections'); 186 | } 187 | 188 | public static getConnectionPattern(): any { 189 | const config = vscode.workspace.getConfiguration('plsql-language'); 190 | return { 191 | patternName: config.get('connection.patternName'), 192 | patternActiveInfos: config.get('connection.patternActiveInfos') 193 | }; 194 | } 195 | 196 | public static getEncoding(file: vscode.Uri) { 197 | const config = vscode.workspace.getConfiguration('files', file); 198 | return config.get('encoding', 'utf8'); 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /main/src/plsqlChannel.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | export default class PLSQLChannel { 4 | private static _channel: vscode.OutputChannel; 5 | 6 | public static show() { 7 | if (this._channel) 8 | this._channel.show(); 9 | } 10 | 11 | public static log(text: string) { 12 | if (!this._channel) 13 | this._channel = vscode.window.createOutputChannel('PLSQL'); 14 | this._channel.appendLine(text); 15 | } 16 | 17 | public static dispose() { 18 | if (this._channel) 19 | this._channel.dispose(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /main/src/plsqlCompletionCustom.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | import * as fs from 'fs'; 4 | import * as json5 from 'json5'; 5 | 6 | import { PLSQLSettings } from './plsql.settings'; 7 | 8 | interface PLSQLCompletionItem { 9 | label: string; 10 | kind: vscode.CompletionItemKind; 11 | documentation?: string; 12 | } 13 | 14 | interface PLSQLCompletionDefinition { 15 | folder: vscode.Uri; 16 | members: any; // object of PLSQLCompletionItem[] 17 | objects?: PLSQLCompletionItem[]; 18 | } 19 | 20 | /** 21 | * Controller for handling PLSQLCompletionCustom 22 | */ 23 | export default class PLSQLCompletionCustom { 24 | 25 | private plsqlCompletionDefs: PLSQLCompletionDefinition[] = []; 26 | 27 | constructor() { 28 | } 29 | 30 | public getCompletion(document: vscode.TextDocument, text?: string): PLSQLCompletionItem[] { 31 | 32 | const completion = this.init(document.uri); 33 | if (completion) 34 | if (text) 35 | return completion.members[text.toLowerCase()]; 36 | else 37 | return completion.objects; 38 | } 39 | 40 | private init(file: vscode.Uri): PLSQLCompletionDefinition { 41 | 42 | // Find workspaceFolder corresponding to file 43 | let folder; 44 | // const wsFolder = vscode.workspace.getWorkspaceFolder(file); 45 | // temporary code to resolve bug https://github.com/Microsoft/vscode/issues/36221 46 | const wsFolder = vscode.workspace.getWorkspaceFolder(vscode.Uri.file(file.fsPath)); 47 | if (wsFolder) 48 | folder = wsFolder.uri; 49 | 50 | let completionDefs = this.plsqlCompletionDefs.find((value, index, obj) => { 51 | if (folder && value.folder) 52 | return (value.folder.fsPath === folder.fsPath); 53 | else 54 | return (folder === value.folder); 55 | }); 56 | if (!completionDefs) { 57 | completionDefs = this.readJSONFile(folder); 58 | if (completionDefs) 59 | this.plsqlCompletionDefs.push(completionDefs); 60 | else 61 | this.plsqlCompletionDefs.push({folder: folder, members: {}}); 62 | } 63 | return completionDefs; 64 | } 65 | 66 | private readJSONFile(workspacefolder: vscode.Uri): PLSQLCompletionDefinition { 67 | const location = PLSQLSettings.getCompletionPath(workspacefolder); 68 | if (!location) 69 | return; 70 | 71 | let parsedJSON; 72 | try { 73 | parsedJSON = json5.parse(fs.readFileSync(location).toString()); // invalid JSON or permission issue can happen here 74 | } catch (error) { 75 | console.error(error); 76 | return; 77 | } 78 | 79 | const members = {}; 80 | const objects = []; 81 | Object.keys(parsedJSON).forEach(item => { 82 | if (parsedJSON[item].members) 83 | members[item.toLowerCase()] = parsedJSON[item].members.map( 84 | member => ({label: member.label, kind: this.convertToCompletionKind(member.kind), documentation: member.documentation})); 85 | objects.push({label: item, kind: this.convertToCompletionKind(parsedJSON[item].kind), documentation: parsedJSON[item].documentation}); 86 | }); 87 | 88 | return { 89 | folder: workspacefolder, 90 | objects: objects, 91 | members: members 92 | }; 93 | } 94 | 95 | private convertToCompletionKind(kind: string): vscode.CompletionItemKind { 96 | if (kind) 97 | kind = kind.toLowerCase(); 98 | switch (kind) { 99 | case 'class': return vscode.CompletionItemKind.Class; 100 | case 'color': return vscode.CompletionItemKind.Color; 101 | case 'constant': return vscode.CompletionItemKind.Constant; 102 | case 'constructor': return vscode.CompletionItemKind.Constructor; 103 | case 'enum': return vscode.CompletionItemKind.Enum; 104 | case 'enumMember': return vscode.CompletionItemKind.EnumMember; 105 | case 'event': return vscode.CompletionItemKind.Event; 106 | case 'field': return vscode.CompletionItemKind.Field; 107 | case 'file': return vscode.CompletionItemKind.File; 108 | case 'folder': return vscode.CompletionItemKind.Folder; 109 | case 'function': return vscode.CompletionItemKind.Function; 110 | case 'interface': return vscode.CompletionItemKind.Interface; 111 | case 'keyword': return vscode.CompletionItemKind.Keyword; 112 | case 'method': return vscode.CompletionItemKind.Method; 113 | case 'module': return vscode.CompletionItemKind.Module; 114 | case 'operator': return vscode.CompletionItemKind.Operator; 115 | case 'property': return vscode.CompletionItemKind.Property; 116 | case 'reference': return vscode.CompletionItemKind.Reference; 117 | case 'snippet': return vscode.CompletionItemKind.Snippet; 118 | case 'struct': return vscode.CompletionItemKind.Struct; 119 | case 'text': return vscode.CompletionItemKind.Text; 120 | case 'typeParameter': return vscode.CompletionItemKind.TypeParameter; 121 | case 'unit': return vscode.CompletionItemKind.Unit; 122 | case 'value': return vscode.CompletionItemKind.Value; 123 | case 'variable': return vscode.CompletionItemKind.Variable; 124 | default: return vscode.CompletionItemKind.Text; 125 | } 126 | } 127 | 128 | } 129 | -------------------------------------------------------------------------------- /main/src/plsqlNavigator.vscode.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as path from 'path'; 3 | 4 | import { PLSQLSettings } from './plsql.settings'; 5 | import PlSqlParser from './plsqlParser.vscode'; 6 | import { PlSqlNavigator } from './lib/plsqlNavigator'; 7 | import { PLSQLCursorInfos } from './lib/plsqlNavigator'; 8 | 9 | 10 | export interface PLSQLCursorInfosVSC extends PLSQLCursorInfos { 11 | line: vscode.TextLine; 12 | } 13 | 14 | export class PlSqlNavigatorVSC /*extends PlSqlNavigator*/ { 15 | 16 | public static goto(document: vscode.TextDocument, position: vscode.Position): Promise { 17 | 18 | PlSqlParser.initParser(PLSQLSettings.getCommentInSymbols()); 19 | 20 | const cursorInfos = this.getCursorInfos(document, position), 21 | parserRoot = PlSqlParser.parseDocument(document); 22 | 23 | PlSqlNavigator.setEncoding(PLSQLSettings.getEncoding(document.uri)); 24 | return PlSqlNavigator.goto(cursorInfos, document.offsetAt(cursorInfos.line.range.start), parserRoot, 25 | this.translatePackageName.bind(this, document), this.getGlobCmdEx.bind(this, document)); 26 | } 27 | 28 | public static getDeclaration(document: vscode.TextDocument, position: vscode.Position): Promise { 29 | 30 | PlSqlParser.initParser(PLSQLSettings.getCommentInSymbols()); 31 | 32 | const cursorInfos = this.getCursorInfos(document, position), 33 | parserRoot = PlSqlParser.parseDocument(document); 34 | 35 | PlSqlNavigator.setEncoding(PLSQLSettings.getEncoding(document.uri)); 36 | return PlSqlNavigator.goto(cursorInfos, document.offsetAt(cursorInfos.line.range.start), parserRoot, 37 | this.translatePackageName.bind(this, document), this.getGlobCmdEx.bind(this, document), true); 38 | } 39 | 40 | public static complete(document: vscode.TextDocument, position: vscode.Position, cursorInfos: PLSQLCursorInfos): Promise { 41 | 42 | if (!cursorInfos.previousWord) 43 | return Promise.resolve(null); 44 | 45 | PlSqlParser.initParser(PLSQLSettings.getCommentInSymbols()); 46 | PlSqlNavigator.setEncoding(PLSQLSettings.getEncoding(document.uri)); 47 | return PlSqlNavigator.complete(cursorInfos, 48 | this.translatePackageName.bind(this, document), this.getGlobCmdEx.bind(this, document)); 49 | } 50 | 51 | public static getCursorInfos(document: vscode.TextDocument, position: vscode.Position): PLSQLCursorInfosVSC { 52 | 53 | const line = document.lineAt(position), 54 | lineText = line.text, 55 | range = document.getWordRangeAtPosition(position), 56 | endChar = range ? range.end.character : position.character, 57 | currentWord = range ? document.getText(range) : '', // 'pkg.' 58 | cursorInfo = PlSqlNavigator.getCursorInfos(currentWord, endChar, lineText); 59 | 60 | return { ...cursorInfo, line}; 61 | } 62 | 63 | private static translatePackageName(document, packageName: string): string { 64 | return PLSQLSettings.translatePackageName(packageName); 65 | } 66 | 67 | private static getGlobCmdEx(document, search) { 68 | 69 | const {searchFld, ignore} = PLSQLSettings.getSearchInfos(document.uri); 70 | 71 | return { 72 | files: search.files, 73 | ext: PLSQLSettings.getSearchExt(search.ext), 74 | searchFld: searchFld, 75 | current: document.uri.fsPath, 76 | params: { 77 | nocase: true, 78 | ignore: ignore 79 | } 80 | }; 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /main/src/plsqlParser.vscode.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | import './lib/definition'; 4 | import PlSqlParser from './lib/plsqlParser'; 5 | 6 | export default class PlSqlParserVSC extends PlSqlParser { 7 | 8 | public static parseDocument(document: vscode.TextDocument): PLSQLRoot { 9 | return this.parseFile(document.fileName, document.getText()); 10 | } 11 | 12 | public static parseParams(symbol: PLSQLSymbol): PLSQLParam[] { 13 | return PlSqlParser.parseParams(symbol); 14 | } 15 | 16 | public static getAllSymbols(document: vscode.TextDocument): vscode.DocumentSymbol[] { 17 | const root = this.parseFile(document.fileName, document.getText()); 18 | return root.symbols.map(symbol => this.getSymbolInformation(document, symbol)); 19 | } 20 | 21 | public static getAllDeclaration(document: vscode.TextDocument): PLSQLSymbol[] { 22 | const root = this.parseFile(document.fileName, document.getText()); 23 | return PlSqlParser.getSymbolsDeclaration(root); 24 | } 25 | 26 | public static getSymbolsCompletion(symbol: PLSQLSymbol): any { 27 | return { 28 | label: symbol.name, 29 | documentation: this.getFormatSymbolDocumentation(symbol), 30 | kind: this.convertToCompletionKind(symbol.kind), 31 | detail: symbol.definition 32 | }; 33 | } 34 | 35 | public static getFormatSymbolDocumentation(symbol: PLSQLSymbol): string | vscode.MarkdownString { 36 | if (!symbol.documentation) 37 | return ''; 38 | 39 | const useJsDoc = symbol.documentation.indexOf('@') !== -1; 40 | PlSqlParser.formatSymbolDocumentation(symbol, useJsDoc); 41 | 42 | let symbolDoc: string | vscode.MarkdownString; 43 | if (symbol.formatedDoc.isMarkdown) 44 | symbolDoc = new vscode.MarkdownString(symbol.formatedDoc.text); 45 | else 46 | symbolDoc = symbol.formatedDoc.text; 47 | return symbolDoc; 48 | } 49 | 50 | private static getSymbolInformation(document: vscode.TextDocument, symbol: PLSQLSymbol) { 51 | const line = symbol.offset != null ? document.lineAt(document.positionAt(symbol.offset)) : document.lineAt(0)/*document.lineAt(symbol.line)*/; 52 | const lineEnd = symbol.offsetEnd != null ? document.lineAt(document.positionAt(symbol.offsetEnd)) : line; 53 | 54 | const result = new vscode.DocumentSymbol( 55 | symbol.kindName+' '+symbol.name, 56 | '', 57 | this.convertToSymbolKind(symbol.kind), 58 | // symbol.parent ? symbol.parent.kindName+' '+symbol.parent.name : '', 59 | // new vscode.Location(document.uri, new vscode.Range(line.range.start, line.range.end)) 60 | new vscode.Range(line.range.start, lineEnd.range.end), 61 | new vscode.Range(line.range.start, lineEnd.range.end) 62 | ); 63 | if (symbol.symbols) 64 | result.children = symbol.symbols.map(item => this.getSymbolInformation(document, item)); 65 | 66 | return result; 67 | } 68 | 69 | private static convertToSymbolKind(kind: PLSQLSymbolKind): vscode.SymbolKind { 70 | switch (kind) { 71 | case PLSQLSymbolKind.packageSpec: 72 | return vscode.SymbolKind.Package; 73 | case PLSQLSymbolKind.packageBody: 74 | return vscode.SymbolKind.Package; 75 | case PLSQLSymbolKind.function: 76 | return vscode.SymbolKind.Function; 77 | case PLSQLSymbolKind.functionSpec: 78 | // return vscode.SymbolKind.Function; 79 | return vscode.SymbolKind.Interface; 80 | case PLSQLSymbolKind.procedure: 81 | return vscode.SymbolKind.Method; 82 | case PLSQLSymbolKind.procedureSpec: 83 | // return vscode.SymbolKind.Method; 84 | return vscode.SymbolKind.Interface; 85 | case PLSQLSymbolKind.variable: 86 | return vscode.SymbolKind.Variable; 87 | case PLSQLSymbolKind.constant: 88 | return vscode.SymbolKind.Constant; 89 | case PLSQLSymbolKind.type: 90 | case PLSQLSymbolKind.subtype: 91 | case PLSQLSymbolKind.cursor: 92 | case PLSQLSymbolKind.exception: 93 | case PLSQLSymbolKind.table: 94 | case PLSQLSymbolKind.view: 95 | case PLSQLSymbolKind.trigger: 96 | return vscode.SymbolKind.Struct; 97 | } 98 | } 99 | 100 | private static convertToCompletionKind(kind: PLSQLSymbolKind): vscode.CompletionItemKind { 101 | switch (kind) { 102 | case PLSQLSymbolKind.packageSpec: 103 | return vscode.CompletionItemKind.Unit; // Package; 104 | case PLSQLSymbolKind.packageBody: 105 | return vscode.CompletionItemKind.Unit; // Package; 106 | case PLSQLSymbolKind.function: 107 | return vscode.CompletionItemKind.Function; 108 | case PLSQLSymbolKind.functionSpec: 109 | return vscode.CompletionItemKind.Function; 110 | // return vscode.CompletionItemKind.Interface; 111 | case PLSQLSymbolKind.procedure: 112 | return vscode.CompletionItemKind.Method; 113 | case PLSQLSymbolKind.procedureSpec: 114 | return vscode.CompletionItemKind.Method; 115 | // return vscode.CompletionItemKind.Interface; 116 | case PLSQLSymbolKind.variable: 117 | return vscode.CompletionItemKind.Variable; 118 | case PLSQLSymbolKind.constant: 119 | return vscode.CompletionItemKind.Constant; 120 | case PLSQLSymbolKind.type: 121 | case PLSQLSymbolKind.subtype: 122 | case PLSQLSymbolKind.cursor: 123 | case PLSQLSymbolKind.exception: 124 | case PLSQLSymbolKind.table: 125 | case PLSQLSymbolKind.view: 126 | case PLSQLSymbolKind.trigger: 127 | return vscode.CompletionItemKind.Struct; 128 | } 129 | } 130 | 131 | } 132 | -------------------------------------------------------------------------------- /main/src/provider/plsqlCompletionItem.provider.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { PLDocController } from '../pldoc.controller'; 3 | 4 | import { PlSqlNavigatorVSC as PlSqlNavigator } from '../plsqlNavigator.vscode'; 5 | import { PLSQLCursorInfosVSC as PLSQLCursorInfos } from '../plsqlNavigator.vscode'; 6 | import PLSQLCompletionCustom from '../plsqlCompletionCustom'; 7 | import PlSqlParser from '../plsqlParser.vscode'; 8 | 9 | export class PLSQLCompletionItemProvider implements vscode.CompletionItemProvider { 10 | 11 | private plDocController = new PLDocController(); 12 | private plsqlCompletionCustom = new PLSQLCompletionCustom(); 13 | private plDocCustomItems: vscode.CompletionItem[]; 14 | private plsqlSnippets: vscode.CompletionItem[]; 15 | 16 | public provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, 17 | token: vscode.CancellationToken): Thenable { 18 | 19 | return new Promise((resolve, reject) => { 20 | 21 | const completeItems: vscode.CompletionItem[] = []; 22 | 23 | const lineText = document.lineAt(position.line).text, 24 | text = document.getText(), 25 | wordRange = document.getWordRangeAtPosition(position), 26 | word = wordRange && document.getText(wordRange), 27 | cursorInfos = PlSqlNavigator.getCursorInfos(document, position); 28 | 29 | if (!cursorInfos.previousDot) { 30 | // PLDOC 31 | const plDocItem = this.getPlDocItem(document, position, lineText, text); 32 | if (plDocItem) 33 | completeItems.push(plDocItem); 34 | 35 | // PLDOC - custom items 36 | if (!this.plDocCustomItems) 37 | this.plDocCustomItems = this.getPlDocCustomItems(document); 38 | Array.prototype.push.apply(completeItems, this.filterCompletion(this.plDocCustomItems, word)); 39 | 40 | // PLSQL - snippets 41 | if (!this.plsqlSnippets) 42 | this.plsqlSnippets = this.getSnippets(); 43 | Array.prototype.push.apply(completeItems, this.filterCompletion(this.plsqlSnippets, word)); 44 | 45 | // Custom completion 46 | const objects = this.getCompletionCustomItems(document); 47 | Array.prototype.push.apply(completeItems, this.filterCompletion(objects, word)); 48 | 49 | // Current package completion 50 | const items = PlSqlParser.getAllDeclaration(document); 51 | if (items) { 52 | Array.prototype.push.apply(completeItems, 53 | this.filterCompletion( 54 | items.map(symbol => this.createSymbolItem(symbol)), 55 | word)); 56 | } 57 | 58 | // TODO symbol in workspace 59 | 60 | return resolve(this.processCompleteItems(completeItems)); 61 | } else { 62 | if (cursorInfos.previousWord) { 63 | // 1. Use plsql.completion.json 64 | const members = this.getCompletionCustomItems(document, cursorInfos.previousWord); 65 | if (members && members.length) { 66 | Array.prototype.push.apply(completeItems, members); 67 | return resolve(this.processCompleteItems(completeItems)); 68 | } 69 | 70 | // 2. Use Package member completion (spec) 71 | this.getPackageItems(document, position, cursorInfos) 72 | .then(items => { 73 | Array.prototype.push.apply(completeItems, items); 74 | return resolve(this.processCompleteItems(completeItems)); 75 | }) 76 | .catch(err => { 77 | console.log(err); 78 | return resolve(undefined); 79 | }); 80 | } 81 | } 82 | }); 83 | } 84 | 85 | private processCompleteItems(completeItems) { 86 | // completionItems must be filtered and if empty return undefined 87 | // otherwise word suggestion are lost ! (https://github.com/Microsoft/vscode/issues/21611) 88 | if (completeItems.length > 0) 89 | return completeItems; 90 | } 91 | 92 | private filterCompletion(items: vscode.CompletionItem[], word: string) { 93 | // completionItems must be filtered and if empty return undefined 94 | // otherwise word suggestion are lost ! (https://github.com/Microsoft/vscode/issues/21611) 95 | if (items && word) { 96 | const wordL = word.toLowerCase(); 97 | return items.filter(item => item.label.toLowerCase().startsWith(wordL)); 98 | } else if (items) 99 | return items; 100 | else return []; 101 | } 102 | 103 | private createSnippetItem(snippet, origin = ''): vscode.CompletionItem { 104 | return this.createCompleteItem(vscode.CompletionItemKind.Snippet, 105 | snippet.prefix, snippet.description, snippet.body.join('\n'), origin); 106 | } 107 | 108 | private createSymbolItem(symbol: PLSQLSymbol): vscode.CompletionItem { 109 | const symbolInfo = PlSqlParser.getSymbolsCompletion(symbol); 110 | return this.createCompleteItem(symbolInfo.kind, 111 | symbolInfo.label, symbolInfo.documentation, symbolInfo.label, symbolInfo.detail); 112 | } 113 | 114 | private createCompleteItem(type: vscode.CompletionItemKind, label: string, doc = '', text = label, origin = ''): vscode.CompletionItem { 115 | const item = new vscode.CompletionItem(label, type); 116 | if (type === vscode.CompletionItemKind.Snippet) { 117 | item.insertText = new vscode.SnippetString(text); 118 | } else 119 | item.insertText = text; 120 | item.documentation = doc; 121 | item.detail = origin; 122 | return item; 123 | } 124 | 125 | private getPlDocItem(document: vscode.TextDocument, position: vscode.Position, lineText: string, text: string): vscode.CompletionItem { 126 | // Empty line, above a function or procedure 127 | if ((text !== '') && (lineText.trim() === '') && (document.lineCount > position.line + 1)) { 128 | 129 | const nextPos = new vscode.Position(position.line + 1, 0), 130 | nextText = text.substr(document.offsetAt(nextPos)); 131 | 132 | const snippet = this.plDocController.getDocSnippet(document, nextText); 133 | if (snippet) 134 | return this.createSnippetItem(snippet, 'pldoc'); 135 | } 136 | } 137 | 138 | private getPlDocCustomItems(document: vscode.TextDocument): vscode.CompletionItem[] { 139 | const snippets = this.plDocController.getCustomSnippets(document); 140 | if (snippets) 141 | return snippets.map(snippet => this.createSnippetItem(snippet)); 142 | return []; 143 | } 144 | 145 | private getSnippets(): vscode.CompletionItem[] { 146 | if (vscode.workspace.getConfiguration('plsql-language').get('snippets.enable')) { 147 | const parsedJSON = require('../../../../snippets/plsql.snippets.json'); 148 | return Object.keys(parsedJSON).map(key => this.createSnippetItem(parsedJSON[key], 'plsql.snippets')); 149 | } 150 | return []; 151 | } 152 | 153 | private getPackageItems(document: vscode.TextDocument, position: vscode.Position, cursorInfos: PLSQLCursorInfos): Promise { 154 | 155 | return new Promise((resolve, reject) => { 156 | 157 | PlSqlNavigator.complete(document, position, cursorInfos) 158 | .then(symbols => { 159 | if (symbols) 160 | return resolve(symbols.map(symbol => this.createSymbolItem(symbol))); 161 | else 162 | return resolve([]); 163 | }) 164 | .catch(err => resolve([])); 165 | }); 166 | } 167 | 168 | private getCompletionCustomItems(document: vscode.TextDocument, text?: string): vscode.CompletionItem[] { 169 | const items = this.plsqlCompletionCustom.getCompletion(document, text); 170 | if (items) 171 | return items.map(item => this.createCompleteItem(item.kind, item.label, item.documentation, item.label, 'plsql.completion')); 172 | return []; 173 | } 174 | 175 | } 176 | -------------------------------------------------------------------------------- /main/src/provider/plsqlDefinition.provider.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import '../lib/definition'; 3 | 4 | import PlSqlParser from '../plsqlParser.vscode'; 5 | 6 | import { PlSqlNavigatorVSC as PlSqlNavigator } from '../plsqlNavigator.vscode'; 7 | 8 | export class PLSQLDefinitionProvider implements vscode.DefinitionProvider { 9 | 10 | public provideDefinition(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Thenable { 11 | 12 | return new Promise((resolve, reject) => { 13 | 14 | PlSqlNavigator.goto(document, position) 15 | .then(symbol => { 16 | return this.getFileLocation(symbol); 17 | }) 18 | .then(location => { 19 | return resolve(location); 20 | }) 21 | .catch(err => { 22 | reject(err); 23 | }); 24 | }); 25 | } 26 | 27 | private getFileLocation(navigateSymbol: PLSQLSymbol): Promise { 28 | return new Promise((resolve, reject) => { 29 | if (navigateSymbol) 30 | vscode.workspace.openTextDocument(PlSqlParser.getSymbolFileName(navigateSymbol)) 31 | .then(document => { 32 | resolve(this.getLocation(document, navigateSymbol)); 33 | }); 34 | else 35 | resolve(); 36 | }); 37 | } 38 | 39 | private getLocation(document: vscode.TextDocument, navigateSymbol: PLSQLSymbol): vscode.Location { 40 | if (navigateSymbol) 41 | return new vscode.Location(vscode.Uri.file(document.fileName), document.positionAt(navigateSymbol.offset)); 42 | else 43 | return null; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /main/src/provider/plsqlDocumentSymbol.provider.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import PlSqlParser from '../plsqlParser.vscode'; 3 | import { PLSQLSettings } from '../plsql.settings'; 4 | 5 | export class PLSQLDocumentSymbolProvider implements vscode.DocumentSymbolProvider { 6 | 7 | public provideDocumentSymbols(document: vscode.TextDocument, token: vscode.CancellationToken): vscode.DocumentSymbol[] { 8 | PlSqlParser.initParser(PLSQLSettings.getCommentInSymbols()); 9 | return PlSqlParser.getAllSymbols(document); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /main/src/provider/plsqlHover.provider.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | import { PlSqlNavigatorVSC as PlSqlNavigator } from '../plsqlNavigator.vscode'; 4 | import PlSqlParser from '../plsqlParser.vscode'; 5 | 6 | export class PLSQLHoverProvider implements vscode.HoverProvider { 7 | public enable: boolean; 8 | 9 | public provideHover(document: vscode.TextDocument, position: vscode.Position, 10 | token: vscode.CancellationToken): vscode.ProviderResult { 11 | return new Promise((resolve, reject) => { 12 | if (!this.enable) 13 | resolve(); 14 | 15 | // TODO use cache 16 | PlSqlNavigator.getDeclaration(document, position) 17 | .then(symbol => { 18 | if (symbol) { 19 | const hoverText = []; 20 | let value; 21 | if (symbol.definition) 22 | value = symbol.definition; 23 | else 24 | value = symbol.kindName; 25 | hoverText.push({language: 'plsql', value: value}); 26 | if (symbol.documentation) { 27 | const symbolDoc = PlSqlParser.getFormatSymbolDocumentation(symbol); 28 | hoverText.push(symbolDoc); 29 | } 30 | resolve(new vscode.Hover(hoverText)); 31 | } else 32 | resolve(); 33 | }) 34 | .catch(err => { 35 | reject(err); 36 | }); 37 | }); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /main/src/provider/plsqlSignature.provider.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | import { PlSqlNavigatorVSC as PlSqlNavigator } from '../plsqlNavigator.vscode'; 4 | import PlSqlParser from '../plsqlParser.vscode'; 5 | 6 | export class PLSQLSignatureProvider implements vscode.SignatureHelpProvider { 7 | 8 | public enable: boolean; 9 | 10 | public provideSignatureHelp(document: vscode.TextDocument, position: vscode.Position, 11 | token: vscode.CancellationToken): vscode.ProviderResult { 12 | return new Promise((resolve, reject) => { 13 | 14 | if (!this.enable) 15 | resolve(); 16 | 17 | let theCall = this.walkBackwardsToBeginningOfCall(document, position); 18 | if (theCall == null) 19 | return resolve(null); 20 | let callerPos = this.previousTokenPosition(document, theCall.openParen); 21 | 22 | // TODO use cache 23 | PlSqlNavigator.getDeclaration(document, callerPos) 24 | .then(symbol => { 25 | if (symbol) { 26 | let result = new vscode.SignatureHelp(), 27 | si: vscode.SignatureInformation; 28 | 29 | const symbolDoc = PlSqlParser.getFormatSymbolDocumentation(symbol); 30 | 31 | si = new vscode.SignatureInformation(symbol.definition, symbolDoc); 32 | si.parameters = PlSqlParser.parseParams(symbol) 33 | .filter(p => p.kind !== PLSQLParamKind.return) 34 | .map(p => new vscode.ParameterInformation(p.text)); 35 | result.signatures = [si]; 36 | result.activeSignature = 0; 37 | result.activeParameter = Math.min(theCall.commas.length, si.parameters.length - 1); 38 | return resolve(result); 39 | } else 40 | return resolve(null); 41 | }) 42 | .catch(err => { 43 | reject(err); 44 | }); 45 | }); 46 | } 47 | 48 | private walkBackwardsToBeginningOfCall(document: vscode.TextDocument, position: vscode.Position): { 49 | openParen: vscode.Position, commas: vscode.Position[] } { 50 | let parenBalance = 0; 51 | let commas = []; 52 | let maxLookupLines = 30; 53 | 54 | for (let line = position.line; line >= 0 && maxLookupLines >= 0; line--, maxLookupLines--) { 55 | let currentLine = document.lineAt(line).text; 56 | let characterPosition = document.lineAt(line).text.length - 1; 57 | 58 | if (line === position.line) { 59 | characterPosition = position.character; 60 | currentLine = currentLine.substring(0, position.character); 61 | } 62 | 63 | for (let char = characterPosition; char >= 0; char--) { 64 | switch (currentLine[char]) { 65 | case '(': 66 | parenBalance--; 67 | if (parenBalance < 0) { 68 | return { 69 | openParen: new vscode.Position(line, char), 70 | commas: commas 71 | }; 72 | } 73 | break; 74 | case ')': 75 | parenBalance++; 76 | break; 77 | case ',': 78 | if (parenBalance === 0) { 79 | commas.push(new vscode.Position(line, char)); 80 | } 81 | } 82 | } 83 | } 84 | return null; 85 | } 86 | 87 | private previousTokenPosition(document: vscode.TextDocument, position: vscode.Position): vscode.Position { 88 | while (position.character > 0) { 89 | let word = document.getWordRangeAtPosition(position); 90 | if (word) { 91 | return word.start; 92 | } 93 | position = position.translate(0, -1); 94 | } 95 | return null; 96 | } 97 | } 98 | 99 | // /** 100 | // * @desription 101 | // * My description multi-lines 102 | // * second line 103 | // * @param {string} Texte Description 104 | // * @param {number} Texte Description 105 | // * @return {number} Description 106 | // * 107 | // */ 108 | 109 | // _@description_ - 110 | // My description multi-lines 111 | // second line 112 | //
_@param_ **string** `Texte` - description 113 | //
_@param_ **number** `Texte` - description 114 | //
_@return_ **number** - description 115 | -------------------------------------------------------------------------------- /main/src/query/query.controller.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | import QueryGridView from './query.gridview'; 4 | import { OracleService } from '../client-oracle/oracle.server'; 5 | import { ConnectController } from '../connect/connect.controller'; 6 | import { PLSQLConnection } from '../plsql.settings'; 7 | import PLSQLChannel from '../plsqlChannel'; 8 | 9 | export class QueryController { 10 | 11 | private _activeConnection: PLSQLConnection; 12 | 13 | constructor(private context: vscode.ExtensionContext, 14 | private connectionCtrl: ConnectController) { 15 | connectionCtrl.eventEmitter.on('setActive', 16 | connection => this.doConnectChange(connection)); 17 | 18 | // Init first connection 19 | this.doConnectChange(connectionCtrl.getActive()); 20 | } 21 | 22 | public executeCommand(param) { 23 | if (!param) 24 | return; 25 | 26 | if (typeof param === 'string') 27 | param = {sql: param}; 28 | else if (param.connection && param.connection.connection) 29 | param.connection = param.connection.connection; 30 | 31 | param.silent = true; 32 | // if (param.script) 33 | // return this.internalRunScript(param); 34 | // else 35 | return this.internalRunQuery(param); 36 | } 37 | 38 | public createConnection(param) { 39 | if (!param) 40 | return; 41 | 42 | if (param.tag) { 43 | const connection = this.connectionCtrl.getByTag(param.tag); 44 | if (connection) 45 | param = connection; 46 | else 47 | return Promise.reject(`Cannot connect tag ${param.tag} not found`); 48 | } 49 | param.silent = true; 50 | param.custom = true; 51 | 52 | return OracleService.connect(param); 53 | } 54 | 55 | public removeConnection(param) { 56 | if (!param) 57 | return; 58 | 59 | if (param.connection && param.connection.connection) 60 | param.connection = param.connection.connection; 61 | 62 | param.silent = true; 63 | param.custom = true; 64 | return OracleService.disconnect(param); 65 | } 66 | 67 | public async runQuery(editor: vscode.TextEditor) { 68 | const text = this.getTextFromRange(editor, this.getSelectionRange(editor)); 69 | return this.internalRunQuery({sql: text}); 70 | } 71 | 72 | // public runScript(editor: vscode.TextEditor) { 73 | // const text = editor.document.getText(); 74 | // return this.internalRunScript(sql: text}); 75 | // } 76 | 77 | private internalRunQuery(param) { 78 | return new Promise((resolve, reject) => { 79 | let p; 80 | 81 | if (!param.connection && !OracleService.isConnected()) 82 | p = OracleService.connect(this._activeConnection); 83 | else 84 | p = Promise.resolve(); 85 | p.then(data => { 86 | if (!param.silent && data && data.loginScript) { 87 | PLSQLChannel.show(); 88 | PLSQLChannel.log(JSON.stringify(data.loginScript)); 89 | } 90 | return OracleService.execCommand(param); // TODO input params 91 | }) 92 | .then((data) => { 93 | if (!param.silent) 94 | this.showQueryResult(data); 95 | return resolve(data); 96 | }) 97 | .catch((err) => { 98 | if (!param.silent) { 99 | vscode.window.showErrorMessage(JSON.stringify(err)); 100 | } 101 | return reject(err); 102 | }); 103 | }); 104 | } 105 | 106 | // TODO 107 | // Actuatlly oracleDB doesn't run script ! 108 | // private internalRunScript(param) { 109 | // return new Promise((resolve, reject) => { 110 | 111 | // let p; 112 | 113 | // if (OracleService.isConnected()) 114 | // p = OracleService.connect(this._activeConnection); // TODO no connection error 115 | // else 116 | // p = Promise.resolve(); 117 | // p.then(() => { 118 | // return OracleService.execCommand(param.sql); 119 | // }) 120 | // .then((data) => { 121 | // if (!param.silent) { 122 | // PLSQLChannel.show(); 123 | // PLSQLChannel.log(data); 124 | // PLSQLChannel.log('script terminated with success'); 125 | // } 126 | // return resolve(data); 127 | // }) 128 | // .catch((err) => { 129 | // if (!param.silent) { 130 | // vscode.window.showErrorMessage(err); 131 | // } 132 | // return reject(err); 133 | // }); 134 | // }); 135 | // } 136 | 137 | private showQueryResult(data) { 138 | if (data.data.metaData || data.data.rows) 139 | QueryGridView.createOrShow(this.context.extensionPath, data); 140 | else { 141 | PLSQLChannel.show(); 142 | PLSQLChannel.log(JSON.stringify(data.data)); 143 | } 144 | } 145 | 146 | private doConnectChange(connection: PLSQLConnection) { 147 | OracleService.disconnect() 148 | .catch((err) => { 149 | if (err.disconnect) 150 | vscode.window.showErrorMessage(err.disconnect); 151 | if (err.error) 152 | vscode.window.showErrorMessage(err.error); 153 | }); 154 | this._activeConnection = connection; 155 | } 156 | 157 | private getSelectionRange(editor: vscode.TextEditor): vscode.Range { 158 | 159 | if (!editor.selection.isEmpty) { 160 | return new vscode.Range(editor.selection.start, editor.selection.end); 161 | } 162 | 163 | if (editor.document.lineCount === 0) 164 | return; 165 | 166 | // Get text delimited by / 167 | 168 | let lineStart; 169 | if (editor.selection) 170 | lineStart = editor.selection.start.line; 171 | else 172 | lineStart = 0; 173 | 174 | let lineEnd = lineStart; 175 | 176 | -- lineStart; 177 | let chrStart = 0, found = false; 178 | while (lineStart >= 0) { 179 | let text = editor.document.lineAt(lineStart).text; 180 | if ((chrStart = text.search(/\//i)) > -1) { 181 | found = true; 182 | break; 183 | } 184 | --lineStart; 185 | } 186 | if (!found) 187 | lineStart = new vscode.Position(0, 0); 188 | else 189 | lineStart = editor.document.positionAt( 190 | editor.document.offsetAt(new vscode.Position(lineStart, chrStart)) + 1); 191 | 192 | found = false; 193 | let chrEnd = 0; 194 | while (lineEnd < editor.document.lineCount) { 195 | let text = editor.document.lineAt(lineEnd).text; 196 | if ((chrEnd = text.search(/\//i)) > -1) { 197 | found = true; 198 | break; 199 | } 200 | ++lineEnd; 201 | } 202 | if (!found) 203 | lineEnd = editor.document.lineAt(editor.document.lineCount - 1).range.end; 204 | else 205 | lineEnd = editor.document.positionAt( 206 | editor.document.offsetAt(new vscode.Position(lineEnd, chrEnd)) - 1); 207 | 208 | return new vscode.Range(lineStart, lineEnd); 209 | } 210 | 211 | private getTextFromRange(editor: vscode.TextEditor, range: vscode.Range): string { 212 | if (range !== undefined) { 213 | return editor.document.getText(range); 214 | } 215 | return ''; 216 | } 217 | 218 | } 219 | -------------------------------------------------------------------------------- /main/src/query/query.gridview.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | import * as fs from 'fs'; 4 | import * as path from 'path'; 5 | 6 | /** 7 | * Manages connect input webview panels 8 | */ 9 | export default class QueryGridView { 10 | /** 11 | * Track the currently panel. Only allow a single panel to exist at a time. 12 | */ 13 | public static currentPanel: QueryGridView | undefined; 14 | 15 | private static readonly viewType = 'GridView'; 16 | private readonly _panel: vscode.WebviewPanel; 17 | private readonly _extensionPath: string; 18 | private _htmlContent: string; 19 | private _currentData: string; 20 | private _disposables: vscode.Disposable[] = []; 21 | 22 | 23 | public static createOrShow(extensionPath: string, data: any) { 24 | // If we already have a panel, show it. 25 | // Otherwise, create a new panel. 26 | if (QueryGridView.currentPanel) { 27 | QueryGridView.currentPanel._currentData = data; 28 | QueryGridView.currentPanel._panel.reveal(QueryGridView.currentPanel._panel.viewColumn/*vscode.ViewColumn.Beside*/); 29 | } else { 30 | QueryGridView.currentPanel = new QueryGridView(extensionPath, data); 31 | } 32 | } 33 | 34 | public sendData(data) { 35 | // Send a message to the webview webview. 36 | // You can send any JSON serializable data. 37 | this._panel.webview.postMessage({ 38 | command: 'showGridView', 39 | data: data 40 | }); 41 | } 42 | 43 | public dispose() { 44 | QueryGridView.currentPanel = undefined; 45 | 46 | // Clean up our resources 47 | this._panel.dispose(); 48 | 49 | while (this._disposables.length) { 50 | const x = this._disposables.pop(); 51 | if (x) { 52 | x.dispose(); 53 | } 54 | } 55 | } 56 | 57 | private constructor(extensionPath: string, data: any) { 58 | this._extensionPath = extensionPath; 59 | 60 | // Create and show a new webview panel 61 | this._currentData = data; 62 | this._panel = vscode.window.createWebviewPanel(QueryGridView.viewType, 'Oracle result', vscode.ViewColumn.Beside, { 63 | // Enable javascript in the webview 64 | enableScripts: true, 65 | 66 | // And restric the webview to only loading content from our extension's `media` directory. 67 | localResourceRoots: [ 68 | vscode.Uri.file(path.join(this._extensionPath, 'resources', 'webview')), 69 | vscode.Uri.file(path.join(this._extensionPath, 'node_modules')) 70 | ] 71 | }); 72 | 73 | // Set the webview's initial html content 74 | this._update(); 75 | 76 | // Listen for when the panel is disposed 77 | // This happens when the user closes the panel or when the panel is closed programatically 78 | this._panel.onDidDispose(() => this.dispose(), null, this._disposables); 79 | 80 | // Update the content based on view changes 81 | this._panel.onDidChangeViewState(e => { 82 | if (this._panel.visible) { 83 | this._update(); 84 | } //else 85 | // this.dispose(); 86 | }, this, this._disposables); 87 | 88 | // Handle messages from the webview 89 | this._panel.webview.onDidReceiveMessage(message => { 90 | switch (message.command) { 91 | // case 'newGridView': 92 | // this._controller.addConnection(message.data); 93 | // this.dispose(); 94 | // return; 95 | case 'cancelGridView': 96 | this.dispose(); 97 | return; 98 | } 99 | }, this, this._disposables); 100 | } 101 | 102 | private _update() { 103 | 104 | if (this._htmlContent) { 105 | this._panel.webview.html = this._htmlContent; 106 | this.sendData(this._currentData); 107 | } else 108 | this._getHtmlForWebview() 109 | .then(html => { 110 | this._htmlContent = html; 111 | this._panel.webview.html = html; 112 | this.sendData(this._currentData); 113 | }); 114 | } 115 | 116 | private _getHtmlForWebview(): Promise { 117 | 118 | // html file 119 | const htmlPathOnDisk = path.join(this._extensionPath, 'resources','webview', 'gridView.html'); 120 | 121 | // Local path to main script, css run in the webview 122 | const scriptPathOnDisk = vscode.Uri.file(path.join(this._extensionPath, 'resources', 'webview', 'gridView.js')); 123 | const cssPathOnDisk = vscode.Uri.file(path.join(this._extensionPath, 'resources', 'webview', 'gridView.css')); 124 | const scriptAgGrid = vscode.Uri.file(path.join(this._extensionPath, 'node_modules', 'ag-grid-community', 'dist', 'ag-grid-community.min.noStyle.js')); 125 | const cssAgGrid = vscode.Uri.file(path.join(this._extensionPath, 'node_modules', 'ag-grid-community', 'dist', 'styles', 'ag-grid.css')); 126 | const cssAgTheme = vscode.Uri.file(path.join(this._extensionPath, 'node_modules', 'ag-grid-community', 'dist', 'styles', 'ag-theme-balham.css')); 127 | 128 | // And the uri we use to load this script in the webview 129 | const scriptUri = scriptPathOnDisk.with({ scheme: 'vscode-resource' }); 130 | const cssUri = cssPathOnDisk.with({ scheme: 'vscode-resource' }); 131 | const scriptAgGridUri = scriptAgGrid.with({ scheme: 'vscode-resource' }); 132 | const cssAgGridUri = cssAgGrid.with({ scheme: 'vscode-resource' }); 133 | const cssAgThemeUri = cssAgTheme.with({ scheme: 'vscode-resource' }); 134 | 135 | // Use a nonce to whitelist which scripts can be run 136 | 137 | return this.readHTMLFile(htmlPathOnDisk, { 138 | scriptUri, cssUri, scriptAgGridUri, cssAgGridUri, cssAgThemeUri, 139 | }, this.getNonce()); 140 | } 141 | 142 | private readHTMLFile(file: string, uris, nonce): Promise { 143 | return new Promise((resolve, reject) => { 144 | fs.readFile(file, 'utf8', (err, data) => { 145 | if (err) 146 | return reject(err); 147 | let html = data.toString(); 148 | Object.keys(uris).forEach(uri => { 149 | html = html.replace('${'+uri+'}', uris[uri]); 150 | }); 151 | html = html.replace(/\${nonce}/g, nonce); 152 | return resolve(html); 153 | }); 154 | }); 155 | } 156 | 157 | private getNonce() { 158 | let text = ''; 159 | const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; 160 | for (let i = 0; i < 32; i++) { 161 | text += possible.charAt(Math.floor(Math.random() * possible.length)); 162 | } 163 | return text; 164 | } 165 | 166 | } 167 | -------------------------------------------------------------------------------- /main/test/index.ts: -------------------------------------------------------------------------------- 1 | // 2 | // PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING 3 | // 4 | // This file is providing the test runner to use when running extension tests. 5 | // By default the test runner in use is Mocha based. 6 | // 7 | // You can provide your own test runner if you want to override it by exporting 8 | // a function run(testRoot: string, clb: (error:Error) => void) that the extension 9 | // host can call to run the tests. The test runner is expected to use console.log 10 | // to report the results back to the caller. When the tests are finished, return 11 | // a possible error to the callback or null if none. 12 | 13 | import testRunner = require('vscode/lib/testrunner'); 14 | 15 | // You can directly control Mocha options by uncommenting the following lines 16 | // See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info 17 | testRunner.configure({ 18 | timeout: 5000, 19 | ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.) 20 | useColors: true // colored output from test results 21 | }); 22 | 23 | module.exports = testRunner; 24 | -------------------------------------------------------------------------------- /main/test/plsqlCompletionItem.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | import * as vscode from 'vscode'; 3 | 4 | import * as path from 'path'; 5 | 6 | import { PLSQLCompletionItemProvider } from '../src/provider/plsqlCompletionItem.provider'; 7 | 8 | interface ICase { 9 | curTextLine: string; 10 | atBeginning: boolean; 11 | expCompletion: any[]; 12 | } 13 | 14 | suite('PLSQL Completion', () => { 15 | 16 | const provider = new PLSQLCompletionItemProvider(); 17 | 18 | function runTest(file: string, cases: ICase[], done) { 19 | 20 | const uri = vscode.Uri.file(path.join(vscode.workspace.rootPath, file)); 21 | vscode.workspace.openTextDocument(uri) 22 | .then(textDocument => { 23 | return Promise.all(cases.map( (test, index) => { 24 | // found line index 25 | const regExp = new RegExp(test.curTextLine, 'gi'); 26 | const found = regExp.exec(textDocument.getText()); 27 | assert.notEqual(found, null, `curText: ${test.curTextLine} not found`); 28 | const curPos = textDocument.positionAt(test.atBeginning ? found.index + 2 : regExp.lastIndex); 29 | 30 | // run test 31 | return provider.provideCompletionItems(textDocument, curPos, null) 32 | .then(completion => { 33 | if (completion) { 34 | test.expCompletion.forEach((expItem) => { 35 | const foundItem = completion.find(item => item.label.toLowerCase() === expItem.text.toLowerCase()); 36 | if (!foundItem) 37 | assert.ok(false, `not Found ${expItem.text}`); 38 | assert.equal(foundItem.kind, expItem.kind, `wrong kind ${expItem.text}`); 39 | }); 40 | } else 41 | // no completion found 42 | assert.ok(false, `Found no completion ${test.curTextLine}`); 43 | }); 44 | })); 45 | }, err => assert.ok(false, `error in OpenTextDocument ${err}`)) 46 | .then(() => done(), done); 47 | } 48 | 49 | function buildCase(curTextLine: string, atBeginning: boolean, expCompletion: any[]): ICase { 50 | return { 51 | atBeginning: atBeginning, 52 | curTextLine: curTextLine, 53 | expCompletion: expCompletion 54 | }; 55 | } 56 | 57 | test('Package', (done) => { 58 | let testCases: ICase[] = [ 59 | // Object. 60 | buildCase( 61 | '-- complete\\s*MyPackage2.', false, [ 62 | {text: 'txyz_myType', kind: vscode.CompletionItemKind.Struct}, 63 | {text: 'ttxyz_myType', kind: vscode.CompletionItemKind.Struct}, 64 | {text: 'myConst', kind: vscode.CompletionItemKind.Constant}, 65 | {text: 'myGlobalVar', kind: vscode.CompletionItemKind.Variable}, 66 | {text: 'get_myValue', kind: vscode.CompletionItemKind.Function}, 67 | {text: 'set_myValue', kind: vscode.CompletionItemKind.Method}, 68 | {text: 'myCall', kind: vscode.CompletionItemKind.Method} 69 | ]), 70 | // Object.Partial member 71 | buildCase( 72 | '-- complete\\s*return MyPackage2.myV', false, [ 73 | {text: 'myGlobalVar', kind: vscode.CompletionItemKind.Variable}, 74 | {text: 'get_myValue', kind: vscode.CompletionItemKind.Function}, 75 | {text: 'set_myValue', kind: vscode.CompletionItemKind.Method}, 76 | ]), 77 | // PL_DOC 78 | buildCase( 79 | '\\s*procedure set_myValue\\(param1 in varchar2\\)\\s*is', true, [ 80 | {text: '__doc', kind: vscode.CompletionItemKind.Snippet}, 81 | {text: 'begin', kind: vscode.CompletionItemKind.Snippet} 82 | ]) 83 | ]; 84 | runTest('xyz_myPackage.sql', testCases, done); 85 | }); 86 | 87 | test('Separate package body', (done) => { 88 | let testCases: ICase[] = [ 89 | // Object. 90 | buildCase( 91 | '-- complete\\s*MyPackage.', false, 92 | [ 93 | {text: 'txyz_myType', kind: vscode.CompletionItemKind.Struct}, 94 | {text: 'ttxyz_myType', kind: vscode.CompletionItemKind.Struct}, 95 | {text: 'myConst', kind: vscode.CompletionItemKind.Constant}, 96 | {text: 'myGlobalVar', kind: vscode.CompletionItemKind.Variable}, 97 | {text: 'get_myValue', kind: vscode.CompletionItemKind.Function}, 98 | {text: 'set_myValue', kind: vscode.CompletionItemKind.Method}, 99 | {text: 'myCall', kind: vscode.CompletionItemKind.Method} 100 | ]), 101 | // Object.Partial member 102 | buildCase( 103 | '-- complete\\s*MyPackage.myV', false, 104 | [ 105 | {text: 'myGlobalVar', kind: vscode.CompletionItemKind.Variable}, 106 | {text: 'get_myValue', kind: vscode.CompletionItemKind.Function}, 107 | {text: 'set_myValue', kind: vscode.CompletionItemKind.Method}, 108 | ]), 109 | // PL_DOC 110 | buildCase( 111 | '\\s*procedure set_myValue\\(param1 in varchar2\\)\\s*is', true, [ 112 | {text: '__doc', kind: vscode.CompletionItemKind.Snippet}, 113 | {text: 'begin', kind: vscode.CompletionItemKind.Snippet} 114 | ]) 115 | ]; 116 | runTest('xyz_myPackage2.pkb', testCases, done); 117 | }); 118 | }); 119 | -------------------------------------------------------------------------------- /main/test/plsqlDocumentSymbol.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | import * as vscode from 'vscode'; 3 | 4 | import * as path from 'path'; 5 | 6 | import { PLSQLDocumentSymbolProvider } from '../src/provider/plsqlDocumentSymbol.provider'; 7 | 8 | suite('PLSQL Symbols', () => { 9 | 10 | const provider = new PLSQLDocumentSymbolProvider(); 11 | 12 | function checkSymbol(symbols, expSymbols) { 13 | assert.equal(symbols.length, expSymbols.length, '(number of symbols)'); 14 | symbols.forEach((symbol, index) => { 15 | assert.equal(symbol.name.toLowerCase(), expSymbols[index].name.toLowerCase()); 16 | assert.equal(symbol.kind, expSymbols[index].kind, `(${index}) ${symbol.name.toLowerCase()}`); 17 | if (symbol.children && expSymbols[index].children) 18 | checkSymbol(symbol.children, expSymbols[index].children); 19 | else if (!(!(symbol.children && symbol.children.length) && !expSymbols[index].children)) 20 | assert.ok(false, 'no children symbols'); 21 | // else 22 | // it's ok, there is no children in both list 23 | }); 24 | } 25 | 26 | function runTest(file: string, expSymbols, done) { 27 | const uri = vscode.Uri.file(path.join(vscode.workspace.rootPath, file)); 28 | vscode.workspace.openTextDocument(uri) 29 | .then(textDocument => { 30 | const symbols = provider.provideDocumentSymbols(textDocument, null); 31 | if (symbols) { 32 | checkSymbol(symbols, expSymbols); 33 | } else 34 | // no symbols found 35 | assert.ok(false, 'Found no symbols'); 36 | }, err => assert.ok(false, `error in OpenTextDocument ${err}`)) 37 | .then(() => done(), done); 38 | } 39 | 40 | test('Package', (done) => { 41 | runTest('xyz_myPackage.sql', [ 42 | {name: 'package MyPackage', kind: vscode.SymbolKind.Package, 43 | children: [ 44 | {name: 'exception bulk_error', kind: vscode.SymbolKind.Struct}, 45 | {name: 'type txyz_myType', kind: vscode.SymbolKind.Struct}, 46 | {name: 'type ttxyz_myType', kind: vscode.SymbolKind.Struct}, 47 | {name: 'constant myConst', kind: vscode.SymbolKind.Constant}, 48 | {name: 'variable myGlobalVar', kind: vscode.SymbolKind.Variable}, 49 | {name: 'procedure conditional', kind: vscode.SymbolKind.Interface}, 50 | {name: 'function get_myValue', kind: vscode.SymbolKind.Interface}, 51 | {name: 'function myCallJava', kind: vscode.SymbolKind.Interface}, 52 | {name: 'procedure set_myValue', kind: vscode.SymbolKind.Interface}, 53 | {name: 'procedure myCall', kind: vscode.SymbolKind.Interface} 54 | ]}, 55 | {name: 'package body MyPackage', kind: vscode.SymbolKind.Package, 56 | children: [ 57 | {name: 'exception bulk_error', kind: vscode.SymbolKind.Struct}, 58 | {name: 'type txyz_myType2', kind: vscode.SymbolKind.Struct}, 59 | {name: 'type ttxyz_myType2', kind: vscode.SymbolKind.Struct}, 60 | {name: 'constant myConst2', kind: vscode.SymbolKind.Constant}, 61 | {name: 'variable myGlobalVar2', kind: vscode.SymbolKind.Variable}, 62 | {name: 'function pForward', kind: vscode.SymbolKind.Interface}, 63 | {name: 'function get_myValue', kind: vscode.SymbolKind.Function}, 64 | {name: 'function myCallJava', kind: vscode.SymbolKind.Function}, 65 | {name: 'function insideConditional', kind: vscode.SymbolKind.Function}, 66 | {name: 'procedure conditional', kind: vscode.SymbolKind.Interface}, 67 | {name: 'procedure conditional', kind: vscode.SymbolKind.Method}, 68 | {name: 'procedure quote', kind: vscode.SymbolKind.Method}, 69 | {name: 'function get_token_count', kind: vscode.SymbolKind.Function}, 70 | {name: 'procedure set_myValue', kind: vscode.SymbolKind.Method}, 71 | {name: 'procedure myCall', kind: vscode.SymbolKind.Method}, 72 | {name: 'procedure pCallInternal', kind: vscode.SymbolKind.Method}, 73 | {name: 'procedure pMainProcedure', kind: vscode.SymbolKind.Method, 74 | children: [ 75 | {name: 'function pSubFunction', kind: vscode.SymbolKind.Function} 76 | ]}, 77 | {name: 'function pForward', kind: vscode.SymbolKind.Function} 78 | ]} 79 | ], done); 80 | }); 81 | 82 | test('Separate package spec', (done) => { 83 | runTest('xyz_myPackage2.pks', [ 84 | {name: 'PACKAGE MyPackage2', kind: vscode.SymbolKind.Package, 85 | children: [ 86 | {name: 'type txyz_myType', kind: vscode.SymbolKind.Struct}, 87 | {name: 'type ttxyz_myType', kind: vscode.SymbolKind.Struct}, 88 | {name: 'constant myConst', kind: vscode.SymbolKind.Constant}, 89 | {name: 'variable myGlobalVar', kind: vscode.SymbolKind.Variable}, 90 | {name: 'function get_myValue', kind: vscode.SymbolKind.Interface}, 91 | {name: 'procedure set_myValue', kind: vscode.SymbolKind.Interface}, 92 | {name: 'procedure "do"', kind: vscode.SymbolKind.Interface}, 93 | {name: 'procedure myCall', kind: vscode.SymbolKind.Interface} 94 | ]}, 95 | ], done); 96 | }); 97 | 98 | test('Separate package body', (done) => { 99 | runTest('xyz_myPackage2.pkb', [ 100 | {name: 'package body MyPackage2', kind: vscode.SymbolKind.Package, 101 | children: [ 102 | {name: 'function get_myValue', kind: vscode.SymbolKind.Function}, 103 | {name: 'procedure set_myValue', kind: vscode.SymbolKind.Method}, 104 | {name: 'procedure "do"', kind: vscode.SymbolKind.Method}, 105 | {name: 'procedure myCall', kind: vscode.SymbolKind.Method}, 106 | {name: 'function myCallJava', kind: vscode.SymbolKind.Function}, 107 | {name: 'procedure pCallInternal', kind: vscode.SymbolKind.Method} 108 | ]}, 109 | ], done); 110 | }); 111 | 112 | test('Function', (done) => { 113 | runTest('xyz_myFunc.sql', [ 114 | {name: 'FUNCTION MyFunc', kind: vscode.SymbolKind.Function, 115 | children: [ 116 | {name: 'function myNestedFunc', kind: vscode.SymbolKind.Function}, 117 | {name: 'variable xyz', kind: vscode.SymbolKind.Variable}, 118 | {name: 'variable abc', kind: vscode.SymbolKind.Variable} 119 | ]}, 120 | ], done); 121 | }); 122 | 123 | test('Procedure', (done) => { 124 | runTest('xyz_myProc.sql', [ 125 | {name: 'procedure MyProc', kind: vscode.SymbolKind.Method, 126 | children: [ 127 | {name: 'function myNestedProc', kind: vscode.SymbolKind.Function}, 128 | {name: 'variable x', kind: vscode.SymbolKind.Variable} 129 | ]}, 130 | ], done); 131 | }); 132 | 133 | test('Dml', (done) => { 134 | runTest('xyz_myDml.sql', [], done); 135 | }); 136 | 137 | }); 138 | -------------------------------------------------------------------------------- /main/test/sql/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "plsql-language.connections": [ 3 | { 4 | "active": false, 5 | "database": "abcd", 6 | "id": 3, 7 | "username": "test", 8 | "ID": 2, 9 | "name": "test@abcd" 10 | }, 11 | { 12 | "ID": 3, 13 | "active": false, 14 | "database": "localhost:1521/xe", 15 | "loginScript": "begin\n login_pkg.setActive('HELLO');\nend;", 16 | "password": "ZABEL", 17 | "username": "ZABEL", 18 | "name": "ZABEL@localhost:1521/xe" 19 | }, 20 | { 21 | "ID": 4, 22 | "active": true, 23 | "database": "localhost:1521/xe", 24 | "password": "hr", 25 | "tag": "hr", 26 | "username": "hr", 27 | "name": "hr@localhost:1521/xe" 28 | } 29 | ], 30 | "plsql-language.connection.activeInfos": "hr/hr@localhost:1521/xe", 31 | // "plsql-language.oracleConnection.enable": true 32 | } -------------------------------------------------------------------------------- /main/test/sql/plsql.completion.json: -------------------------------------------------------------------------------- 1 | { 2 | /* 3 | This is the default definition for your tables structure 4 | This list is used for auto-completion. 5 | You must create your own file and specify his location with the setting "plsql-language.completion.path" 6 | 7 | e.g.: 8 | 9 | "myTableName1": { 10 | "kind": "struct", // https://code.visualstudio.com/docs/extensionAPI/vscode-api#CompletionItemKind 11 | "documentation": "no comment", 12 | "members": [ 13 | {"label": "myFieldName1", "kind": "field", "documentation": "no comment"}, 14 | {"label": "myFieldName2", "kind": "field"}, 15 | {"label": "myFieldName3", "kind": "field"} 16 | ] 17 | }, 18 | "myTableNameN": { 19 | "kind": "struct", 20 | "members": [ 21 | {"label": "myFieldNameA", "kind": "field"}, 22 | {"label": "myFieldNameB", "kind": "field"}, 23 | {"label": "myFieldNameC", "kind": "field"} 24 | ] 25 | } 26 | 27 | */ 28 | 29 | "myTableName1": { 30 | "kind": "struct", 31 | "documentation": "no comment", 32 | "members": [ 33 | {"label": "myFieldName1", "kind": "field", "documentation": "no comment"}, 34 | {"label": "myFieldName2", "kind": "field"}, 35 | {"label": "myFieldName3", "kind": "field"} 36 | ] 37 | }, 38 | "myTableNameN": { 39 | "kind": "struct", 40 | "members": [ 41 | {"label": "myFieldNameA", "kind": "field"}, 42 | {"label": "myFieldNameB", "kind": "field"}, 43 | {"label": "myFieldNameC", "kind": "field"} 44 | ] 45 | }, 46 | "myFuncForTest": {}, 47 | "myFuncForTest2": { 48 | "kind": "function", 49 | "documentation": "None" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /main/test/sql/query.sql: -------------------------------------------------------------------------------- 1 | insert into EMPLOYEES (EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, HIRE_DATE) 2 | values (456, 'XYZ', 'TEST', 'xyz@test.com', sysdate) 3 | / 4 | 5 | commit 6 | / 7 | 8 | rollback 9 | / 10 | 11 | select * from EMPLOYEES 12 | / 13 | 14 | begin 15 | employee_pkg.setActive('Khoo'); 16 | end; 17 | / 18 | 19 | select employee_pkg.getActive from dual 20 | / 21 | 22 | select login_pkg.getActive from dual 23 | / -------------------------------------------------------------------------------- /main/test/sql/src/PACKAGES/PCK$1.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE PACKAGE "schema"."PCK$1" 2 | as 3 | procedure my$Procedure(param1 in varchar2) 4 | end; 5 | / -------------------------------------------------------------------------------- /main/test/sql/src/PACKAGES/PCK_2.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE PACKAGE "schema"."PCK_2" 2 | as 3 | procedure myProcedure(param1 in varchar2); 4 | function myFunction(param1 in varchar2) return number; 5 | end; 6 | / -------------------------------------------------------------------------------- /main/test/sql/src/PACKAGES_BODIES/PCK$1.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE PACKAGE BODY "schema"."PCK$1" 2 | as 3 | 4 | procedure my$Procedure(param1 in varchar2) 5 | is 6 | lnumber number; 7 | begin 8 | -- some code to execute 9 | lnumber = PCK_2.myFunction('toto'); 10 | PCK_2.myProcedure('toto'); 11 | return; 12 | end; 13 | 14 | end; 15 | / 16 | -------------------------------------------------------------------------------- /main/test/sql/src/PACKAGES_BODIES/PCK_2.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE PACKAGE BODY "schema"."PCK_2" 2 | as 3 | 4 | procedure myProcedure(param1 in varchar2) 5 | is 6 | begin 7 | -- some code to execute 8 | return; 9 | end; 10 | 11 | procedure myFunction(param1 in varchar2) return number 12 | is 13 | begin 14 | -- some code to execute 15 | PCK$1.my$procedure('test'); 16 | return 4; 17 | end; 18 | 19 | end; 20 | / 21 | -------------------------------------------------------------------------------- /main/test/sql/xyz_myDml.sql: -------------------------------------------------------------------------------- 1 | update myTable 2 | set myField = MyPackage.set_myValue('toto') 3 | / 4 | 5 | update myTable2 6 | set myField2 = pck_2.myfunction('toto') 7 | / 8 | 9 | declare 10 | xyz MyPackage2.txyz_myType; 11 | abc MyPackage.ttxyz_myType; 12 | begin 13 | if x = schema.MyPackage.myConst 14 | if y = MyPackage2.myGlobalVar 15 | end 16 | / 17 | 18 | Select 1 from dual; -------------------------------------------------------------------------------- /main/test/sql/xyz_myFunc.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION MyFunc(param1 varchar2) return varchar2 2 | is 3 | 4 | /** 5 | * function myNestedFunc 6 | */ 7 | -- function myNestedFunc 8 | function myNestedFunc(param1 varchar2) 9 | return varchar2 10 | is 11 | begin 12 | return param1||'_TEST'; 13 | end; 14 | 15 | xyz MyPackage2.txyz_myType; 16 | abc MyPackage.ttxyz_myType; 17 | 18 | begin 19 | myPackage.myCall(param1); 20 | schema.MyProc(param1); 21 | 22 | if x = schema.MyPackage.myConst 23 | if y = MyPackage2.myGlobalVar 24 | 25 | return myNestedFunc(param1); 26 | end; 27 | -------------------------------------------------------------------------------- /main/test/sql/xyz_myPackage.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE PACKAGE schema."MyPackage" 2 | as 3 | 4 | bulk_error exception; 5 | pragma exception_init(bulk_error, -24381); 6 | 7 | type txyz_myType is record( 8 | myChar varchar2 9 | , myNumber number 10 | , myField myTable.myField%type 11 | ); 12 | type ttxyz_myType is table of txyz_myType; 13 | 14 | myConst constant char(2) := '10'; 15 | myGlobalVar number := 10; 16 | 17 | -- Test conditional compilation 18 | $IF $$opt $THEN 19 | procedure conditional; 20 | $END 21 | 22 | /** 23 | * Comment 24 | */ 25 | function get_myValue(param1 in varchar2) 26 | return varchar2; 27 | 28 | function myCallJava(param1 in varchar2) return varchar2; 29 | 30 | /** 31 | * Comment 32 | * 33 | * procedure set_myValue 34 | * 35 | */ 36 | procedure set_myValue(param1 in varchar2); 37 | 38 | procedure myCall(param1 in varchar2); 39 | 40 | end; 41 | / 42 | 43 | create or replace package body schema.MyPackage 44 | as 45 | 46 | bulk_error exception; 47 | pragma exception_init(bulk_error, -24381); 48 | 49 | type txyz_myType2 is record( 50 | myChar varchar2 51 | , myNumber number 52 | , myField myTable.myField%type 53 | ); 54 | type ttxyz_myType2 is table of txyz_myType2; 55 | 56 | myConst2 constant char(2) := '10'; 57 | myGlobalVar2 number := 10; 58 | 59 | -- forward declaration 60 | function pForward(param1 in varchar2) 61 | return varchar2; 62 | 63 | -- function get_myValue 64 | function get_myValue(param1 in varchar2) 65 | return varchar2 66 | is 67 | begin 68 | return param1 || ' TEST'; 69 | end; 70 | 71 | /** 72 | Doc wrapper 73 | */ 74 | function myCallJava(param1 in varchar2) return varchar2 75 | as language java 76 | name 'myJavaFunc'; 77 | 78 | function insideConditional return number 79 | is 80 | begin 81 | while false loop 82 | begin 83 | $if true $then 84 | null; 85 | $elsif $$no_op $then 86 | null; 87 | $end 88 | exception when others then 89 | null; 90 | end; 91 | end loop; 92 | return null; 93 | end; 94 | 95 | $IF $$opt $THEN 96 | procedure conditional; 97 | $END 98 | 99 | $IF $$opt $THEN 100 | procedure conditional is 101 | begin 102 | null; 103 | end; 104 | $END 105 | 106 | procedure quote is 107 | begin 108 | l := '"'; 109 | logger.log('end'); 110 | -- end 111 | return null; 112 | end; 113 | 114 | function get_token_count(pi_text in varchar2 115 | , pi_pattern in varchar2 := g_std_sep 116 | , pi_case in varchar2 := 'i' 117 | ) 118 | return number 119 | as 120 | l_return number; 121 | begin 122 | if pi_text is null then 123 | l_return := 0; 124 | else 125 | l_return := regexp_count(pi_text, pi_pattern, 1, pi_case); 126 | end if; 127 | 128 | return l_return; 129 | end get_token_count; 130 | 131 | procedure set_myValue(param1 in varchar2) 132 | is 133 | begin 134 | MyPackage.myCall('test'); 135 | MyPackage2."do"('test1'); 136 | schema.MyPackage.myCall('test2'); 137 | pCallInternal('test3'); 138 | case 139 | end 140 | -- some other code to execute 141 | return; 142 | end; 143 | 144 | procedure myCall(param1 in varchar2) 145 | is 146 | begin 147 | -- some code to execute 148 | return; 149 | end; 150 | 151 | procedure pCallInternal(param1 in varchar2) 152 | is 153 | xyz MyPackage2.txyz_myType; 154 | abc ttxyz_myType; 155 | begin 156 | -- some code to execute 157 | MyPackage2.myCall('Test'); 158 | MyFunc('Test'); 159 | MyProc('Test'); 160 | schema.MyProc('Test'); 161 | 162 | pForward('Test3'); 163 | 164 | if x = schema.MyPackage.myConst 165 | if y = myGlobalVar 166 | 167 | -- complete 168 | MyPackage2. 169 | -- complete 170 | return MyPackage2.myV 171 | 172 | return; 173 | end; 174 | 175 | -- test with subfunction 176 | procedure pMainProcedure(param1 in varchar2) 177 | is 178 | x number; 179 | 180 | function pSubFunction(param1 in number) 181 | is 182 | begin 183 | -- some code to execute 184 | return 3; 185 | end pSubFunction; 186 | begin 187 | -- some code to execute 188 | 189 | -- call to subFunction 190 | x = pSubFunction(2); 191 | 192 | case 193 | case 194 | end 195 | end 196 | end pMainProcedure; 197 | 198 | function pForward(param1 in varchar2) 199 | return varchar2 200 | is 201 | xyz txyz_myType2; 202 | abc ttxyz_myType2; 203 | begin 204 | -- some code to execute 205 | if x = myConst2 206 | if y = myGlobalVar2 207 | 208 | return 'test'; 209 | end; 210 | 211 | end; 212 | / 213 | -------------------------------------------------------------------------------- /main/test/sql/xyz_myPackage2.pkb: -------------------------------------------------------------------------------- 1 | create or replace package body MyPackage2 2 | /* test */ 3 | -- test2 4 | as 5 | 6 | function get_myValue(param1 in varchar2) 7 | return varchar2 8 | is 9 | begin 10 | return param1 || ' TEST'; 11 | end; 12 | 13 | procedure set_myValue(param1 in varchar2) 14 | is 15 | begin 16 | MyPackage2.myCall('test'); 17 | pCallInternal('test2'); 18 | -- some other code to execute 19 | return; 20 | end; 21 | 22 | procedure "do"(param1 in varchar2) 23 | is 24 | begin 25 | l := "l'abc"; 26 | return; 27 | end; 28 | 29 | procedure myCall(param1 in varchar2) 30 | is 31 | begin 32 | -- some code to execute 33 | return; 34 | end; 35 | 36 | function myCallJava(param1 in varchar2) return varchar2 37 | as language java 38 | name '' 39 | ; 40 | 41 | procedure pCallInternal(param1 in varchar2) 42 | is 43 | xyz MyPackage.txyz_myType; 44 | abc ttxyz_myType; 45 | begin 46 | -- some code to execute 47 | MyPackage.myCall('Test'); 48 | MyFunc('Test'); 49 | MyProc('Test'); 50 | schema.MyProc('Test'); 51 | 52 | if x = schema.MyPackage2.myConst 53 | if y = myGlobalVar 54 | 55 | -- complete 56 | MyPackage. 57 | -- complete 58 | MyPackage.myV 59 | 60 | return; 61 | end; 62 | 63 | end; 64 | / 65 | -------------------------------------------------------------------------------- /main/test/sql/xyz_myPackage2.pks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zabel-xyz/plsql-language/28b3f624a88d96c9cc1d4f03653b658f1c234566/main/test/sql/xyz_myPackage2.pks -------------------------------------------------------------------------------- /main/test/sql/xyz_myProc.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE PROCEDURE "schema"."MyProc"(param1 varchar2) 2 | is 3 | 4 | /** 5 | * procedure myNestedProc 6 | */ 7 | function myNestedProc(param1 varchar2) 8 | return varchar2 9 | is 10 | begin 11 | return param1||'_TEST'; 12 | end; 13 | 14 | x varchar2(10); 15 | begin 16 | myPackage.myCall(param1); 17 | x := schema.MyFunc(param1); 18 | myNestedProc(param1); 19 | end; 20 | -------------------------------------------------------------------------------- /main/test/sql/xyz_myTable.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE schema.myTable 2 | AS SELECT * FROM hr.emp; -------------------------------------------------------------------------------- /main/test/sql/xyz_myTable2.sql: -------------------------------------------------------------------------------- 1 | CREATE GLOBAL TEMPORARY TABLE schema.myTable2 2 | AS SELECT * FROM myTable; -------------------------------------------------------------------------------- /main/test/sql/xyz_myTrigger.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE TRIGGER schema.myTrigger 2 | BEFORE INSERT OR UPDATE OF salary, job_id ON myTable 3 | FOR EACH ROW 4 | WHEN (new.job_id <> 'AD_VP') 5 | myPackage.get_myValue() 6 | -- pl/sql_block 7 | -------------------------------------------------------------------------------- /main/test/sql/xyz_myView.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE NO FORCE VIEW myView ( 2 | CustomerName, ContactName 3 | ) 4 | AS 5 | SELECT CustomerName, ContactName 6 | FROM myTable 7 | WHERE Country = "Brazil"; -------------------------------------------------------------------------------- /main/test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out", 5 | "rootDir": "../", 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /main/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "out/src" 5 | }, 6 | "exclude": [ 7 | "test" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /plsql-language-custom/.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": "Launch Extension", 7 | "type": "extensionHost", 8 | "request": "launch", 9 | "runtimeExecutable": "${execPath}", 10 | "args": ["--extensionDevelopmentPath=${workspaceRoot}" ] 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /plsql-language-custom/.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | xyz.plsql-language-custom-0.0.1/** -------------------------------------------------------------------------------- /plsql-language-custom/xyz.plsql-language-custom-0.0.1/.vsixmanifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Language PL/SQL (custom) 6 | Some customization for Language PL/SQL 7 | plsql,sql 8 | 9 | Public 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | extension/plsql-language-custom.png 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /plsql-language-custom/xyz.plsql-language-custom-0.0.1/README.md: -------------------------------------------------------------------------------- 1 | # PL/SQL Custom colorization 2 | 3 | This is an exemple for some custom colorization. 4 | 5 | 1. Modify **syntaxes\plsql-custom.tmlanguage** with some custom definitions 6 | 2. Copy folder **xyz.plsql-language-custom-0.0.1** in extensions, according to your platform: 7 | - **Windows** %USERPROFILE%\\.vscode\extensions 8 | - **Mac** ~/.vscode/extensions 9 | - **Linux** ~/.vscode/extensions 10 | -------------------------------------------------------------------------------- /plsql-language-custom/xyz.plsql-language-custom-0.0.1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "plsql-language-custom", 3 | "displayName": "Language PL/SQL (custom)", 4 | "description": "Some customization for Language PL/SQL", 5 | "version": "0.0.1", 6 | "publisher": "xyz", 7 | "engines": { 8 | "vscode": "^1.7.0" 9 | }, 10 | "icon": "plsql-language-custom.png", 11 | "extensionDependencies": [ 12 | "xyz.plsql-language" 13 | ], 14 | "contributes": { 15 | "grammars": [ 16 | { 17 | "language": "plsql", 18 | "scopeName": "source.plsql.oracle.custom", 19 | "path": "./syntaxes/plsql-custom.tmLanguage" 20 | } 21 | ] 22 | }, 23 | "__metadata": null 24 | } -------------------------------------------------------------------------------- /plsql-language-custom/xyz.plsql-language-custom-0.0.1/plsql-language-custom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zabel-xyz/plsql-language/28b3f624a88d96c9cc1d4f03653b658f1c234566/plsql-language-custom/xyz.plsql-language-custom-0.0.1/plsql-language-custom.png -------------------------------------------------------------------------------- /plsql-language-custom/xyz.plsql-language-custom-0.0.1/syntaxes/plsql-custom.tmLanguage: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | fileTypes 6 | 7 | sql 8 | ddl 9 | dml 10 | pks 11 | pkb 12 | 13 | name 14 | PL/SQL Custom (Oracle) 15 | patterns 16 | 17 | 18 | 32 | 33 | 34 | include 35 | source.plsql.oracle 36 | 37 | 38 | scopeName 39 | source.plsql.oracle.custom 40 | uuid 41 | B0F1D9EB-BB16-4999-8A67-21400A86471C 42 | 43 | 44 | -------------------------------------------------------------------------------- /resources/webview/connect.css: -------------------------------------------------------------------------------- 1 | /* debug */ 2 | 3 | /* 4 | :root { 5 | --vscode-button-background: #4b74cc; 6 | --vscode-button-foreground: white; 7 | --vscode-button-hoverBackground: #3a62b8; 8 | 9 | --vscode-inputValidation-errorBorder: #cf1010; 10 | --vscode-inputValidation-infoBorder: #4b74cc; 11 | --vscode-input-placeholderForeground: #c6c6c6; 12 | 13 | --vscode-foreground: black; 14 | } 15 | */ 16 | 17 | :root { 18 | --pl-text-foreground: var(--vscode-foreground); 19 | --pl-label-focus: var(--vscode-inputValidation-infoBorder); 20 | --pl-label-error: var(--vscode-inputValidation-errorBorder); 21 | /* --pl-label: var(--vscode-input-placeholderForeground); */ 22 | --pl-label: var(--vscode-foreground); 23 | --pl-title: var(--vscode-foreground); 24 | --pl-button-background: var(--vscode-button-background); 25 | --pl-button-foreground: var(--vscode-button-foreground); 26 | --pl-button-hover: var(--vscode-button-hoverBackground); 27 | --pl-tooltip-background: var(--vscode-inputValidation-errorBackground); 28 | --pl-tooltip-foregound: var(--vscode-input-foreground); 29 | } 30 | 31 | h1, input, textarea, select, button, label, .tooltip { 32 | font-family: sans-serif; 33 | font-size: 12px; 34 | } 35 | 36 | label { 37 | color: var(--pl-label); 38 | } 39 | 40 | h1 { 41 | font-size: 18px; 42 | margin: 0px; 43 | padding-bottom: 5px; 44 | border-bottom: solid 1px var(--pl-title); 45 | } 46 | 47 | form { 48 | margin: 30px 30px 0; 49 | } 50 | 51 | button { 52 | border: none; 53 | background: var(--pl-button-background); 54 | cursor: pointer; 55 | border-radius: 3px; 56 | padding: 6px; 57 | width: 100px; 58 | color: var(--pl-button-foreground); 59 | } 60 | 61 | button:hover { 62 | /* box-shadow: 0 3px 6px 0 */ 63 | background: var(--pl-button-hover); 64 | } 65 | 66 | .group { 67 | position: relative; 68 | margin: 20px 0; 69 | } 70 | 71 | .group-title { 72 | padding-bottom: 10px; 73 | } 74 | 75 | .group-check { 76 | padding-bottom: 20px; 77 | } 78 | 79 | /* textarea { 80 | resize: none; 81 | } */ 82 | 83 | .group input, .group textarea, .group select { 84 | background: none; 85 | color: var(--pl-text-foreground); 86 | padding: 4px 0px; 87 | display: block; 88 | width: 320px; 89 | border: none; 90 | border-radius: 0; 91 | border-bottom: 1px solid var(--pl-label); 92 | } 93 | 94 | .group input:focus, .group textarea:focus, .group select:focus { 95 | outline: none; 96 | } 97 | 98 | .group .notEmpty~label, 99 | .group input:focus~label, .group textarea:focus~label, .group select:focus~label { 100 | top: -12px; 101 | font-size: 10px; 102 | color: var(--pl-label-focus); 103 | } 104 | 105 | .group .notEmpty~label { 106 | color: var(--pl-label) 107 | } 108 | 109 | .group .error~label { 110 | color: var(--pl-label-error) !important; 111 | } 112 | 113 | .group label { 114 | color: var(--pl-label); 115 | font-weight: normal; 116 | position: absolute; 117 | pointer-events: none; 118 | left: 0px; 119 | top: 6px; 120 | transition: 300ms ease all; 121 | } 122 | 123 | .group-check label span { 124 | top: -3px; 125 | position: relative; 126 | } 127 | 128 | .group .error~.bar:before { 129 | background: var(--pl-label-error); 130 | } 131 | 132 | .group input:focus~.bar:before, 133 | .group textarea:focus~.bar:before, 134 | .group select:focus~.bar:before { 135 | width: 320px; 136 | } 137 | 138 | .bar { 139 | position: relative; 140 | display: block; 141 | width: 320px; 142 | } 143 | 144 | .bar:before { 145 | content: ''; 146 | height: 2px; 147 | width: 0; 148 | bottom: 0px; 149 | position: absolute; 150 | background: var(--pl-label-focus); 151 | transition: 300ms ease all; 152 | left: 0%; 153 | } 154 | 155 | .tooltip { 156 | display: none; 157 | } 158 | 159 | .group .error~.tooltip { 160 | padding: 4px !important; 161 | border-radius: 3px !important; 162 | position: relative; 163 | display: inline-block !important; 164 | /* box-shadow: 4px 4px 4px #aaaaaa; */ 165 | margin-top: 4px; 166 | border: 1px solid var(--pl-label-error); 167 | color: var(--pl-tooltip-foregound); 168 | background-color: var(--pl-tooltip-background) 169 | } 170 | 171 | .group .error~.tooltip:before { 172 | content: ''; 173 | width: 0; 174 | height: 0; 175 | border-left: 8px solid transparent; 176 | border-right: 8px solid transparent; 177 | border-bottom: 8px solid var(--pl-label-error); 178 | position: absolute; 179 | top: -8px; 180 | } 181 | 182 | html { 183 | height: 100%; 184 | font-family: sans-serif; 185 | } 186 | 187 | body { 188 | height: 100%; 189 | overflow: hidden; 190 | margin: 0px; 191 | display: flex; 192 | } 193 | 194 | .column { 195 | height: 100%; 196 | display: flex; 197 | flex-direction: column; 198 | flex-grow: 1; 199 | overflow-y: auto; 200 | } 201 | 202 | .left { 203 | flex-shrink: 0; 204 | } 205 | 206 | .navigItem { 207 | cursor: pointer; 208 | padding: 6px 0px; 209 | } 210 | 211 | ul { 212 | list-style-type: none; 213 | padding-left: 0px; 214 | } 215 | 216 | .active { 217 | font-weight: bold 218 | } 219 | 220 | #new { 221 | margin-top: 10px; 222 | } 223 | 224 | -------------------------------------------------------------------------------- /resources/webview/connect.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 14 | 15 | 16 | 17 | PLSQL - Connection 18 | 19 | 20 | 21 | 27 | 28 |
29 |
30 |
31 |

New connection

32 |
33 |
34 | 35 | 36 | 37 |
Please enter the database (connectString)
38 |
39 |
40 | 41 | 42 | 43 |
Please enter the user
44 |
45 |
46 | 47 | 48 | 49 |
50 |
51 | 61 | 62 | 63 |
64 |
65 | 66 | 67 | 68 |
69 |
70 | 71 | 72 | 73 |
74 |
75 | 76 | 77 | 78 |
79 |
80 | 84 | 88 |
89 | 90 | 91 | 92 |
93 |
94 | 95 | 96 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /resources/webview/connect.js: -------------------------------------------------------------------------------- 1 | // This script will be run within the webview itself 2 | // It cannot access the main VS Code APIs directly. 3 | 4 | (function () { 5 | const vscode = typeof acquireVsCodeApi != 'undefined' ? acquireVsCodeApi() : null; 6 | let _currentID = null; 7 | let _oldID = null; 8 | 9 | // add events 10 | document.getElementById('connection').addEventListener('submit', submitConnection); 11 | document.getElementById('cancel').addEventListener('click', cancelConnection); 12 | document.getElementById('delete').addEventListener('click', deleteConnection); 13 | document.getElementById('new').addEventListener('click', newConnection); 14 | 15 | window.addEventListener('message', sendMessage); 16 | 17 | document.querySelectorAll('input, textarea') 18 | .forEach(input => { 19 | input.addEventListener('invalid', event => 20 | input.classList.add('error') 21 | ); 22 | input.addEventListener('input', event => { 23 | input.value !== '' ? input.classList.add('notEmpty') : input.classList.remove('notEmpty'); 24 | input.classList.remove('error'); 25 | }); 26 | }); 27 | 28 | document.querySelectorAll('.checkbox') 29 | .forEach(input => { 30 | input.addEventListener('click', event => { 31 | if (input.checked) 32 | if (input.id === 'active') 33 | document.querySelector('.checkbox#hidden').checked = false 34 | else if (input.id === 'hidden') 35 | document.querySelector('.checkbox#active').checked = false 36 | }); 37 | }); 38 | 39 | document.querySelectorAll('select') 40 | .forEach(input => { 41 | input.addEventListener('input', event => { 42 | input.value !== '' ? input.classList.add('notEmpty') : input.classList.remove('notEmpty'); 43 | }); 44 | }); 45 | 46 | function submitConnection(event) { 47 | _oldID = null; 48 | const data = { 49 | database: document.connection.database.value, 50 | username: document.connection.user.value, 51 | password: document.connection.password.value, 52 | privilege: document.connection.privilege.value, 53 | schema: document.connection.schema.value, 54 | loginScript: document.connection.loginScript.value, 55 | tag: document.connection.tag.value, 56 | active: document.connection.active.checked ? true : false, 57 | hidden: document.connection.hidden.checked ? true : false, 58 | ID: _currentID 59 | }; 60 | const sendData = {}; 61 | Object.keys(data).forEach(key => { 62 | if (data[key] !== '') 63 | sendData[key] = data[key]; 64 | }); 65 | vscode.postMessage({ 66 | command: 'submitConnection', 67 | data: sendData 68 | }); 69 | } 70 | 71 | function cancelConnection(event) { 72 | vscode.postMessage({ 73 | command: 'cancelConnection', 74 | data: { 75 | ID: (_showAll ? (_currentID == null ? _oldID : _currentID) : null) 76 | } 77 | }); 78 | _oldID = null; 79 | } 80 | 81 | function deleteConnection(event) { 82 | vscode.postMessage({ 83 | command: 'deleteConnection', 84 | data: { 85 | ID: _currentID 86 | } 87 | }); 88 | } 89 | 90 | function showConnection(ID) { 91 | vscode.postMessage({ 92 | command: 'showConnection', 93 | data: { 94 | ID: ID 95 | } 96 | }); 97 | } 98 | 99 | function newConnection(event) { 100 | _oldID = _currentID; 101 | vscode.postMessage({ 102 | command: 'newConnection' 103 | }); 104 | } 105 | 106 | // Handle messages sent from the extension to the webview 107 | function sendMessage(event) { 108 | const message = event.data; // The json data that the extension sent 109 | _showAll = true; 110 | if (message.command === 'newConnection') { 111 | _showAll = false; 112 | } else if (message.command === 'settingsConnections') { 113 | updateForm(message.data.connection); 114 | updateList(message.data.items); 115 | } 116 | document.getElementById('navigator').hidden = !_showAll; 117 | document.connection.delete.hidden = _currentID == null; 118 | document.getElementById('new').hidden = _currentID == null; 119 | if (_currentID == null) { 120 | document.connection.create.textContent = 'Create'; 121 | document.getElementById('title').textContent = 'New connection'; 122 | } else { 123 | document.connection.create.textContent = 'Update'; 124 | document.getElementById('title').textContent = `Connection ${message.data.connection.name}`; 125 | } 126 | } 127 | 128 | function updateForm(data) { 129 | if (data && data.database) { 130 | document.connection.database.value = data.database; 131 | document.connection.database.classList.add('notEmpty'); 132 | } else { 133 | document.connection.database.value = ''; 134 | document.connection.database.classList.remove('notEmpty'); 135 | } 136 | if (data && data.username) { 137 | document.connection.user.value = data.username; 138 | document.connection.user.classList.add('notEmpty'); 139 | } else { 140 | document.connection.user.value = ''; 141 | document.connection.user.classList.remove('notEmpty'); 142 | } 143 | if (data && data.password) { 144 | document.connection.password.value = data.password; 145 | document.connection.password.classList.add('notEmpty'); 146 | } else { 147 | document.connection.password.value = ''; 148 | document.connection.password.classList.remove('notEmpty'); 149 | } 150 | if (data && data.privilege) { 151 | document.connection.privilege.value = data.privilege; 152 | document.connection.privilege.classList.add('notEmpty'); 153 | } else { 154 | document.connection.privilege.value = ''; 155 | document.connection.privilege.classList.remove('notEmpty'); 156 | } 157 | if (data && data.schema) { 158 | document.connection.schema.value = data.schema; 159 | document.connection.schema.classList.add('notEmpty'); 160 | } else { 161 | document.connection.schema.value = ''; 162 | document.connection.schema.classList.remove('notEmpty'); 163 | } 164 | if (data && data.loginScript) { 165 | document.connection.loginScript.value = data.loginScript; 166 | document.connection.loginScript.classList.add('notEmpty'); 167 | } else { 168 | document.connection.loginScript.value = ''; 169 | document.connection.loginScript.classList.remove('notEmpty'); 170 | } 171 | if (data && data.tag) { 172 | document.connection.tag.value = data.tag; 173 | document.connection.tag.classList.add('notEmpty'); 174 | } else { 175 | document.connection.tag.value = ''; 176 | document.connection.tag.classList.remove('notEmpty'); 177 | } 178 | 179 | 180 | document.querySelectorAll('input, textarea, select') 181 | .forEach(input => { 182 | input.classList.remove('error'); 183 | }); 184 | 185 | if (data) 186 | _currentID = data.ID; 187 | else 188 | _currentID = null; 189 | if (!data) { 190 | document.connection.active.checked = true; 191 | document.connection.hidden.checked = false; 192 | } else { 193 | document.connection.active.checked = data.active; 194 | document.connection.hidden.checked = !data.active && data.hidden; 195 | } 196 | } 197 | 198 | function updateList(items) { 199 | const navig = document.getElementById('navigatorItems'); 200 | const ul = document.createElement('ul'); 201 | if (items) 202 | items.forEach(item => { 203 | const li = document.createElement('li'); 204 | li.className = 'navigItem'; 205 | li.addEventListener('click', () => showConnection(item.ID)); 206 | if (item.ID === _currentID) 207 | li.classList.add('active'); 208 | li.innerHTML = item.name; 209 | ul.appendChild(li); 210 | }); 211 | navig.removeChild(navig.lastChild); 212 | navig.appendChild(ul); 213 | } 214 | 215 | }()); -------------------------------------------------------------------------------- /resources/webview/gridView.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zabel-xyz/plsql-language/28b3f624a88d96c9cc1d4f03653b658f1c234566/resources/webview/gridView.css -------------------------------------------------------------------------------- /resources/webview/gridView.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | PLSQL - Results 23 | 24 | 25 |
26 |
27 |
28 |
29 | 30 | 31 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /resources/webview/gridView.js: -------------------------------------------------------------------------------- 1 | // This script will be run within the webview itself 2 | // It cannot access the main VS Code APIs directly. 3 | 4 | (function () { 5 | const vscode = typeof acquireVsCodeApi != 'undefined' ? acquireVsCodeApi() : null; 6 | let gridOpt; 7 | 8 | // add events 9 | window.addEventListener('message', sendMessage); 10 | 11 | function cancelConnection(event) { 12 | vscode.postMessage({ 13 | command: 'cancelGridView', 14 | // data: { 15 | // } 16 | }); 17 | } 18 | 19 | // Handle messages sent from the extension to the webview 20 | function sendMessage(event) { 21 | const message = event.data; // The json data that the extension sent 22 | if (message.command === 'showGridView') { 23 | displayGridView(message.data); 24 | } 25 | } 26 | 27 | function displayGridView(data) { 28 | if (!data) 29 | return; 30 | 31 | console.log(data); 32 | 33 | const result = data.data; 34 | 35 | if (!result || !result.metaData || !result.rows) 36 | return; 37 | 38 | 39 | const columnDefs = result.metaData.map(col => ({headerName: col.name, field: col.name})); 40 | 41 | const rowData = result.rows.map(record => { 42 | let row = {}; 43 | record.forEach((value, index) => row[result.metaData[index].name] = value); 44 | return row; 45 | }); 46 | 47 | // lookup the container we want the Grid to use 48 | const eGridDiv = document.querySelector('#gridView'); 49 | 50 | // create the grid passing in the div to use together with the columns & data we want to use 51 | if (!gridOpt) { 52 | gridOpt = {enableColResize: true, columnDefs, rowData}; 53 | new agGrid.Grid(eGridDiv, gridOpt) 54 | } else { 55 | gridOpt.api.setColumnDefs(columnDefs); 56 | gridOpt.api.setRowData(rowData); 57 | } 58 | } 59 | 60 | }()); -------------------------------------------------------------------------------- /server-oracle/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "oracle-connect", 3 | "description": "Server to connect to Oracle DB", 4 | "publisher": "xyz", 5 | "version": "0.0.1", 6 | "engines": { 7 | "node": "*" 8 | }, 9 | "dependencies": { 10 | "vscode-languageserver": "5.2.1" 11 | }, 12 | "scripts": {} 13 | } 14 | -------------------------------------------------------------------------------- /server-oracle/src/server.ts: -------------------------------------------------------------------------------- 1 | import { createConnection, IConnection } from 'vscode-languageserver'; 2 | import * as fs from 'fs'; 3 | 4 | export class OracleConnection { 5 | 6 | // Create a connection for the server. The connection uses Node's IPC as a transport. 7 | // Also include all preview / proposed LSP features. 8 | private static _connection: IConnection; 9 | private static _oracleDB; 10 | private static _oracleConnection; 11 | 12 | private static _customID = 0; 13 | private static _customConnections = {}; 14 | 15 | private static ORACLE_DB_VERSION = 'oracledb@"^3.0.0"'; 16 | 17 | public static init() { 18 | this._connection = createConnection(/*ProposedFeatures.all*/); 19 | [ 20 | {name: 'Oracle/install', fn: this.installOracle}, 21 | {name: 'Oracle/init', fn: this.initOracle}, 22 | {name: 'Oracle/connect', fn: this.connect}, 23 | {name: 'Oracle/disconnect', fn: this.disconnect}, 24 | {name: 'Oracle/execCommand', fn: this.execCommand} 25 | ].forEach(request => { 26 | this._connection.onRequest(request.name, (params) => request.fn.call(this, params)); 27 | }); 28 | this._connection.listen(); 29 | } 30 | 31 | private static createOracleDirectory() { 32 | return new Promise((resolve, reject) => { 33 | const directory = '../node_modules/oracledb'; 34 | 35 | fs.stat(directory, (err, stats) => { 36 | if (!err) 37 | return resolve(false); 38 | 39 | // Check if error defined and the error code is "not exists" 40 | if (err.code === 'ENOENT') { 41 | // Create the directory, call the callback. 42 | fs.mkdir(directory, error => { 43 | if (error) 44 | return reject('OracleDB failed create directory '+error); 45 | return resolve(true); 46 | }); 47 | } else 48 | return reject('OracleDB failed check directory '+err); 49 | }); 50 | }); 51 | } 52 | 53 | // private static installOracle(): Promise { 54 | // return new Promise((resolve, reject) => { 55 | // this.createOracleDirectory() 56 | // .then((install) => { 57 | // if (!install) 58 | // return resolve('OracleDB already installed'); 59 | 60 | // // No warranty (http://abulletproofidea.com/questions/5157/puis-je-installer-un-paquet-npm-a-partir-de-javascript-execute-dans-node-js) 61 | // // directory npm is deleted, why ??? 62 | // const npm = require('npm'); 63 | // npm.load({}, err => { 64 | // if (err) 65 | // return resolve({error: 'OracleDB - npm load failed ' + err}); 66 | 67 | // npm.commands.install([ORACLE_DB_VERSION], (err, data) => { 68 | // if (err) 69 | // return resolve({error: 'OracleDB - install failed '+ err}); 70 | // return resolve('OracleDB - install work '+JSON.stringify(data)); 71 | // }) 72 | // }) 73 | // }) 74 | // .catch(err => resolve({error: err})); 75 | // }); 76 | // } 77 | 78 | private static installOracle(): Promise { 79 | return new Promise((resolve, reject) => { 80 | this.createOracleDirectory() 81 | .then(install => { 82 | if (!install) 83 | return resolve('OracleDB already installed'); 84 | this._connection.sendNotification('Oracle/install', `Start install ${OracleConnection.ORACLE_DB_VERSION}...`); 85 | require('child_process').exec(`npm install ${OracleConnection.ORACLE_DB_VERSION}`, 86 | (err, stdout, stderr) => { 87 | this._connection.sendNotification('Oracle/install', `...End install ${OracleConnection.ORACLE_DB_VERSION}`); 88 | if (err) 89 | resolve({error: err}); 90 | if (stderr) 91 | resolve({stderror: stderr}); 92 | resolve({install: stdout}); 93 | }); 94 | }); 95 | 96 | }); 97 | } 98 | 99 | private static initOracle() { 100 | return new Promise((resolve, reject) => { 101 | try { 102 | this._oracleDB = require('oracledb'); 103 | this._oracleDB.fetchAsString = [this._oracleDB.CLOB]; 104 | return resolve('oracleDB work'); 105 | } catch (e) { 106 | return resolve({error : 'oracleDB failed: '+e + 'ENV: '+process.env}); 107 | } 108 | }); 109 | } 110 | 111 | private static execCommand(params): Promise { 112 | return new Promise((resolve, reject) => { 113 | if (!this._oracleDB) 114 | return resolve({error: 'OracleDB is missing'}); 115 | if ( !(params && params.connection) && !this._oracleConnection) 116 | return resolve({error: 'Connection is missing'}); 117 | 118 | this.internalExecCommand(params) 119 | .then(data => resolve(data)) 120 | .catch(err => resolve(err)); 121 | }); 122 | } 123 | 124 | private static internalExecCommand(params): Promise { 125 | return new Promise((resolve, reject) => { 126 | 127 | const cmd = params.sql.trim().toLowerCase(); 128 | 129 | let internalConnection; 130 | if (params.connection) { 131 | internalConnection = this._customConnections[params.connection.customID]; 132 | if (!internalConnection) 133 | return reject({ params: params, error: 'connection not found ', list: this._customConnections, connection: internalConnection }); 134 | } else 135 | internalConnection = this._oracleConnection; 136 | 137 | if (cmd === 'commit' || cmd === 'rollback') 138 | internalConnection[cmd]() 139 | .then(() => resolve({ data: cmd })) 140 | .catch(err => reject({ error: this.formatOracleError(err) })); 141 | else { 142 | // this._connection.sendNotification('Oracle/debug', 'ExecCmd'); 143 | // if no params, don't add an arguments null ! 144 | params.params = params.params || {}; 145 | params.opt = params.opt || {}; 146 | internalConnection.execute(params.sql, params.params, params.opt) 147 | .then(result => resolve({ params: params, data: result })) 148 | .catch(err => reject({ params: params, error: this.formatOracleError(err) })); 149 | } 150 | }); 151 | } 152 | 153 | private static connect(params): Promise { 154 | return new Promise((resolve, reject) => { 155 | if (!this._oracleDB) 156 | return resolve({error: 'OracleDB is missing'}); 157 | 158 | const result: any = {}; 159 | 160 | Promise.resolve() 161 | .then(() => { 162 | if (params && !params.custom && this._oracleConnection) 163 | return this.disconnect(); 164 | return; 165 | }) 166 | .then((data) => { 167 | // disconnect error 168 | if (data && data.error) 169 | result.disconnect = data; 170 | return this.internalConnect(params); 171 | }) 172 | .then(connection => { 173 | if (params.custom) { 174 | connection.customID = ++this._customID; 175 | this._customConnections[connection.customID] = connection; 176 | } else 177 | this._oracleConnection = connection; 178 | 179 | result.connection = connection; 180 | result.connected = true; 181 | 182 | if (params.schema) 183 | result.schema = params.schema; 184 | if (!params.loginScript) 185 | return; 186 | else 187 | return this.internalExecConnectCommand(params); 188 | }) 189 | .then(data => { 190 | if (data) 191 | result.loginScript = data; 192 | return resolve(result); 193 | }) 194 | .catch(err => { 195 | result.error = err; 196 | return resolve(result); 197 | }); 198 | }); 199 | } 200 | 201 | private static internalConnect(params): Promise { 202 | return new Promise((resolve, reject) => { 203 | if (!params) 204 | return reject('No params !'); 205 | 206 | const connectParams = { 207 | user: params.user || params.username, 208 | password: params.password || params.username, 209 | connectString: params.connectString || params.database, 210 | privilege: this._oracleDB[params.privilege] 211 | }; 212 | this._oracleDB.getConnection(connectParams, (err, connection) => { 213 | if (err) 214 | return reject({ error: this.formatOracleError(err), params: connectParams }); 215 | return resolve(connection); 216 | }); 217 | }); 218 | } 219 | 220 | private static internalExecConnectCommand(params): Promise { 221 | return new Promise((resolve, reject) => { 222 | this.internalExecCommand({sql: params.loginScript}) 223 | .then(data => resolve(data)) 224 | .catch(err => resolve(err)); // Not considered as connection error 225 | }); 226 | } 227 | 228 | private static disconnect(params?): Promise { 229 | return new Promise((resolve, reject) => { 230 | 231 | let internalConnection; 232 | if (params && params.connection) { 233 | internalConnection = this._customConnections[params.connection.customID]; 234 | if (!internalConnection) 235 | return resolve({ params: params, error: 'connection not found' }); 236 | } else { 237 | if (!this._oracleConnection) 238 | return resolve(true); 239 | internalConnection = this._oracleConnection; 240 | } 241 | 242 | internalConnection.close(err => { 243 | if (err) 244 | return resolve({error: err}); 245 | 246 | if (params && params.connection) 247 | delete this._customConnections[params.connection.customID]; 248 | else 249 | delete this._oracleConnection; 250 | return resolve(true); 251 | }); 252 | }); 253 | } 254 | 255 | private static formatOracleError(error) { 256 | // re-format oracle error to be able to stringify 257 | return { 258 | message: error.message, 259 | num: error.errorNum, 260 | offset: error.offset 261 | }; 262 | } 263 | } 264 | 265 | { 266 | OracleConnection.init(); 267 | } 268 | -------------------------------------------------------------------------------- /server-oracle/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "out", 5 | "rootDir": "src", 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /snippets/pldoc.json: -------------------------------------------------------------------------------- 1 | { 2 | /* 3 | This is the default definition for your function or procedure. 4 | You can create your own file and specify his location with the setting "plsql-language.pldoc.path" 5 | (don't modify this file, it'll be overwritten with the update of the extension !) 6 | The definition is like snippets with some specific variables: 7 | ${PLDOC_TYPE}: function or procedure 8 | ${PLDOC_OBJECT}: name of function or procedure 9 | ${PLDOC_PARAM}: name of parameter 10 | ${PLDOC_PARAM_TYPE}: type of parameter 11 | ${PLDOC_AUTHOR}: author (see setting) 12 | ${PLDOC_DATE}: current date 13 | ${PLDOC_DATE|dd/mm/yyyy}: current date with format according to https://www.npmjs.com/package/dateformat 14 | */ 15 | "pldoc": { 16 | "prefix": "__doc", // '__' for first position in snippet list 17 | "body": [ 18 | "/**", 19 | " * Description: ${0:Description}", 20 | " *", 21 | " * Author: ${PLDOC_AUTHOR}", 22 | " * Created: ${PLDOC_DATE}", 23 | " *", 24 | " * Param: ${PLDOC_PARAM} ${1:Description}", 25 | " * Return: ${2:Description}", 26 | " */" 27 | ], 28 | "description": "Documentation for your function or procedure" 29 | } 30 | 31 | /* 32 | You can also define custom snippet (warning only in pldoc.json, not in regular snippet plsql.json) 33 | The available variables are 34 | ${PLDOC_AUTHOR}: author (see setting) 35 | ${PLDOC_DATE}: current date 36 | ${PLDOC_DATE|dd/mm/yyyy}: current date with format according to https://www.npmjs.com/package/dateformat 37 | */ 38 | /* 39 | "pllastUpdate": { 40 | "prefix": "__last", 41 | "body": [ 42 | "${PLDOC_AUTHOR} ${PLDOC_DATE}" 43 | ], 44 | "description": "Author and date" 45 | } 46 | */ 47 | } -------------------------------------------------------------------------------- /snippets/plsql.completion.json: -------------------------------------------------------------------------------- 1 | { 2 | /* 3 | This is the default definition for your tables structure 4 | This list is used for auto-completion. 5 | You must create your own file and specify his location with the setting "plsql-language.completion.path" 6 | (don't use this file, it'll be overwritten with the update of the extension !) 7 | e.g.: 8 | 9 | "myTableName1": { 10 | "kind": "struct", 11 | "documentation": "no comment", 12 | "members": [ 13 | {"label": "myFieldName1", "kind": "field", "documentation": "no comment"}, 14 | {"label": "myFieldName2", "kind": "field"}, 15 | {"label": "myFieldName3", "kind": "field"} 16 | ] 17 | }, 18 | "myTableNameN": { 19 | "kind": "struct", 20 | "members": [ 21 | {"label": "myFieldNameA", "kind": "field"}, 22 | {"label": "myFieldNameB", "kind": "field"}, 23 | {"label": "myFieldNameC", "kind": "field"} 24 | ] 25 | } 26 | 27 | */ 28 | } 29 | -------------------------------------------------------------------------------- /snippets/plsql.snippets.json: -------------------------------------------------------------------------------- 1 | { 2 | "package..spec": { 3 | "prefix": "pkg", 4 | "body": [ 5 | "create or replace package ${1:name}", 6 | "as", 7 | " $0", 8 | "end $1;" 9 | ], 10 | "description": "package spec" 11 | }, 12 | "package..body": { 13 | "prefix": "pkg", 14 | "body": [ 15 | "create or replace package body ${1:name}", 16 | "as", 17 | " $0", 18 | "end $1;" 19 | ], 20 | "description": "package body" 21 | }, 22 | 23 | "func..spec": { 24 | "prefix": "func", 25 | "body": [ 26 | "function ${1:name}(", 27 | " ${2:param} in ${3:param_type}", 28 | ") return ${4:return_type};" 29 | ], 30 | "description": "function spec" 31 | }, 32 | "func": { 33 | "prefix": "func", 34 | "body": [ 35 | "function ${1:name}(", 36 | " ${2:param} in ${3:param_type}", 37 | ") return ${4:return_type}", 38 | "is", 39 | "begin", 40 | " $0", 41 | "end $1;" 42 | ], 43 | "description": "function" 44 | }, 45 | 46 | "proc..spec": { 47 | "prefix": "proc", 48 | "body": [ 49 | "procedure ${1:name}(", 50 | " ${2:param} in ${3:param_type}", 51 | ");" 52 | ], 53 | "description": "procedure spec" 54 | }, 55 | "proc": { 56 | "prefix": "proc", 57 | "body": [ 58 | "procedure ${1:name}(", 59 | " ${2:param} in ${3:param_type}", 60 | ")", 61 | "is", 62 | "begin", 63 | " $0", 64 | "end $1;" 65 | ], 66 | "description": "procedure" 67 | }, 68 | 69 | "begin..end": { 70 | "prefix": "begin", 71 | "body": [ 72 | "begin", 73 | " $0", 74 | "end;" 75 | ], 76 | "description": "begin..end" 77 | }, 78 | 79 | "if..end": { 80 | "prefix": "ifthen", 81 | "body": [ 82 | "if ${1:condition} then", 83 | " $0", 84 | "end if;" 85 | ], 86 | "description": "if..end" 87 | }, 88 | "if..else..end": { 89 | "prefix": "ifelse", 90 | "body": [ 91 | "if ${1:condition} then", 92 | " $2", 93 | "else", 94 | " $3", 95 | "end if;" 96 | ], 97 | "description": "if..else..end" 98 | }, 99 | "elsif..": { 100 | "prefix": "elsif", 101 | "body": [ 102 | "elsif ${1:condition} then", 103 | " $0" 104 | ], 105 | "description": "elsif.." 106 | }, 107 | 108 | "case..when..end": { 109 | "prefix": "case", 110 | "body": [ 111 | "case $1", 112 | " when ${2:condition} then $3", 113 | " else $4", 114 | "end;" 115 | ], 116 | "description": "case..when..end" 117 | }, 118 | "when..then": { 119 | "prefix": "when", 120 | "body": [ 121 | "when ${1:condition} then $2" 122 | ], 123 | "description": "when..then" 124 | }, 125 | 126 | "loop..end": { 127 | "prefix": "loop", 128 | "body": [ 129 | "loop", 130 | " $0", 131 | "end loop" 132 | ], 133 | "description": "loop..end" 134 | }, 135 | 136 | "for..in": { 137 | "prefix": "for", 138 | "body": [ 139 | "for $1 in $2 loop", 140 | " $0", 141 | "end loop" 142 | ], 143 | "description": "for..in" 144 | }, 145 | 146 | "for..in..collection": { 147 | "prefix": "for", 148 | "body": [ 149 | "for i in 1..${1:collection}.count loop", 150 | " $0", 151 | "end loop" 152 | ], 153 | "description": "for..in..collection" 154 | }, 155 | 156 | "exception_no_data": { 157 | "prefix": "exception", 158 | "body": [ 159 | "exception", 160 | " when no_data_found then", 161 | " $1;" 162 | ], 163 | "description": "exception no_data_found" 164 | }, 165 | "exception_too_many": { 166 | "prefix": "exception", 167 | "body": [ 168 | "exception", 169 | " when too_many_rows then", 170 | " $1;" 171 | ], 172 | "description": "exception too_many_rows" 173 | }, 174 | "exception_others": { 175 | "prefix": "exception", 176 | "body": [ 177 | "exception", 178 | " when ${1:others} then", 179 | " ${2:null};" 180 | ], 181 | "description": "exception others" 182 | }, 183 | 184 | "raise_application_error": { 185 | "prefix": "raise", 186 | "body": [ 187 | "raise_application_error(${1:-20000}, '${2:message}');" 188 | ], 189 | "description": "raise application error" 190 | }, 191 | "dbms_output.put_line": { 192 | "prefix": "output", 193 | "body": [ 194 | "dbms_output.put_line('${1:message}');" 195 | ], 196 | "description": "dbms_output.put_line" 197 | }, 198 | 199 | "select": { 200 | "prefix": "select", 201 | "body": [ 202 | "select ${1:field}", 203 | " ${2:into ${3:variable}}", 204 | " from ${4:table}", 205 | " where ${5:condition}" 206 | ], 207 | "description": "select" 208 | }, 209 | "insert": { 210 | "prefix": "insert", 211 | "body": [ 212 | "insert into ${1:table} ${2:(${3:column})}", 213 | "values ${4:(${5:values})}" 214 | ], 215 | "description": "insert" 216 | }, 217 | "update": { 218 | "prefix": "update", 219 | "body": [ 220 | "update ${1:table}", 221 | " set ${2:field}=${3:value}", 222 | " where ${4:condition}" 223 | ], 224 | "description": "update" 225 | }, 226 | "delete": { 227 | "prefix": "delete", 228 | "body": [ 229 | "delete from ${1:table}", 230 | " where ${2:condition}" 231 | ], 232 | "description": "delete" 233 | } 234 | } -------------------------------------------------------------------------------- /syntaxes/plsql.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 | ["if", "end if"], 13 | ["loop", "end loop"], 14 | ["case", "end case"] 15 | // ["begin", "end"] 16 | ], 17 | "autoClosingPairs": [ 18 | ["(", ")"], 19 | ["{", "}"], 20 | ["/*", "*/"], 21 | ["\"", "\""], 22 | ["'", "'"] 23 | ], 24 | "surroundingPairs": [ 25 | ["(", ")"], 26 | ["{", "}"], 27 | ["\"", "\""], 28 | ["'", "'"] 29 | ], 30 | // Default without $# (dont' work with getWordRangeAtPosition() according to issue #42649) 31 | "wordPattern": 32 | "(-?\\d*\\.\\d\\w*)|([^\\`\\~\\!\\@\\%\\^\\&\\*\\(\\)\\-\\=\\+\\[\\{\\]\\}\\\\\\|\\;\\:\\'\\\"\\,\\.\\<\\>\\/\\?\\s]+)" 33 | } 34 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es2017", 5 | "lib": [ 6 | "es2017" 7 | ], 8 | "sourceMap": true, 9 | "watch": false 10 | }, 11 | "exclude": [ 12 | "node_modules", 13 | ".history" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "class-name": true, 4 | // // "comment-format": [true, "check-space"], 5 | // "curly": true, 6 | "eofline": true, 7 | "forin": true, 8 | "indent": [true, "spaces"], 9 | "label-position": true, 10 | // "max-line-length": [true, 140], 11 | // "member-access": true, 12 | "member-ordering": [ 13 | true, { 14 | "order": [ 15 | "public-static-field", 16 | "private-static-field", 17 | "public-instance-field", 18 | "private-instance-field", 19 | "public-constructor", 20 | "public-instance-method", 21 | "private-constructor", 22 | "private-instance-method" 23 | ]} 24 | ], 25 | "no-arg": true, 26 | "no-bitwise": true, 27 | // "no-console": [true, 28 | // "debug", 29 | // "info", 30 | // "time", 31 | // "timeEnd", 32 | // "trace" 33 | // ], 34 | "no-construct": true, 35 | "no-debugger": true, 36 | "no-duplicate-variable": true, 37 | //"no-empty": true, 38 | "no-eval": true, 39 | "no-inferrable-types": true, 40 | "no-shadowed-variable": true, 41 | "no-string-literal": true, 42 | "no-switch-case-fall-through": true, 43 | "no-trailing-whitespace": true, 44 | "no-unused-expression": true, 45 | "no-unused-variable": true, 46 | // "no-use-before-declare": true, 47 | "no-var-keyword": true, 48 | // "object-literal-sort-keys": true, 49 | "one-line": [true, 50 | "check-open-brace", 51 | // "check-catch", 52 | // "check-else", 53 | // "check-finally", 54 | "check-whitespace" 55 | ], 56 | "quotemark": [true, "single", "avoid-escape"], 57 | "radix": true, 58 | "semicolon": [true, "always"], 59 | // "trailing-comma": [true, { 60 | // "singleline": "never", 61 | // "multiline": "always" 62 | // }], 63 | "triple-equals": [true, "allow-null-check"], 64 | "typedef-whitespace": [true, { 65 | "call-signature": "nospace", 66 | "index-signature": "nospace", 67 | "parameter": "nospace", 68 | "property-declaration": "nospace", 69 | "variable-declaration": "nospace" 70 | }], 71 | "variable-name": false, 72 | "whitespace": [true, 73 | "check-branch", 74 | "check-decl", 75 | // "check-operator", 76 | // "check-separator", 77 | "check-type" 78 | ] 79 | } 80 | } 81 | --------------------------------------------------------------------------------