├── .no-sublime-package ├── util ├── .gitignore ├── test_expansion.py └── qvvars.py ├── bin ├── dart.exe ├── inqlik.snapshot └── inqlik.bat ├── .gitignore ├── Snippets ├── DROP FIELD.sane.sublime-snippet ├── DROP TABLE.sane.sublime-snippet ├── RENAME FIELD.sane.sublime-snippet ├── RENAME TABLE.sane.sublime-snippet └── NEW_QLIKVIEW_VARIABLE.sane.sublime-snippet ├── QlikView-vars.sublime-build ├── QlikView variables.sublime-build ├── Settings ├── FormatNum.sublime-macro ├── QlikView variables file.sublime-settings ├── QlikView script.sublime-settings ├── QlikView variables.sublime-completions ├── Default (Windows).sublime-keymap ├── QlikView GUI only.sublime-completions ├── QlikView script only.sublime-completions ├── Main.sublime-menu ├── test.json └── QlikView script.sublime-completions ├── Syntax ├── ExpressionSection.tmPreferences ├── TabSymbol.tmPreferences ├── TableSymbol.tmPreferences ├── SoubroutineSymbol.tmPreferences ├── VariableSymbol.tmPreferences ├── ExpressionSymbol.tmPreferences ├── QlikView-reload-log.YAML-tmLanguage ├── Comments (QlikView).tmPreferences ├── QlikView-reload-log.tmLanguage ├── QlikView variables file.YAML-tmLanguage ├── QlikView variables file.tmLanguage ├── QlikView script.YAML-tmLanguage └── QlikView script.tmLanguage ├── qvw_log_viewer.py ├── new_etl_module.py ├── qvw_open_log.py ├── qlikview_goto_file.py ├── qlikview_goto_definition.py ├── qvw_load.py ├── CHANGELOG.md ├── qvd_viewer.py ├── README.md └── qlickview_vars.py /.no-sublime-package: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /util/.gitignore: -------------------------------------------------------------------------------- 1 | /*.pyc 2 | -------------------------------------------------------------------------------- /bin/dart.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inqlik/inqlik-tools/HEAD/bin/dart.exe -------------------------------------------------------------------------------- /bin/inqlik.snapshot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inqlik/inqlik-tools/HEAD/bin/inqlik.snapshot -------------------------------------------------------------------------------- /bin/inqlik.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | SET BINDIR=%~dp0 3 | "%BINDIR%dart.exe" "%BINDIR%inqlik.snapshot" %* 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /*.tmPreferences.cache 2 | /*.tmLanguage.cache 3 | /*.sane-snippet 4 | /*.pyc 5 | /package-metadata.json -------------------------------------------------------------------------------- /Snippets/DROP FIELD.sane.sublime-snippet: -------------------------------------------------------------------------------- 1 | DROP FIELDdrop fieldsource.qvs 2 | 3 | -------------------------------------------------------------------------------- /Snippets/DROP TABLE.sane.sublime-snippet: -------------------------------------------------------------------------------- 1 | DROP TABLEdrop tablesource.qvs 2 | 3 | -------------------------------------------------------------------------------- /Snippets/RENAME FIELD.sane.sublime-snippet: -------------------------------------------------------------------------------- 1 | RENAME FIELDrename fieldsource.qvs 2 | 3 | -------------------------------------------------------------------------------- /Snippets/RENAME TABLE.sane.sublime-snippet: -------------------------------------------------------------------------------- 1 | RENAME TABLErename tablesource.qvs 2 | 3 | -------------------------------------------------------------------------------- /QlikView-vars.sublime-build: -------------------------------------------------------------------------------- 1 | { 2 | "target": "qlikview_variables_export", 3 | "selector": "source.qlikview-vars", 4 | "variants": [ 5 | { 6 | "name": "Run", 7 | "target": "qlikview_variables_export", 8 | "commandVariant": "simple" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /Snippets/NEW_QLIKVIEW_VARIABLE.sane.sublime-snippet: -------------------------------------------------------------------------------- 1 | New QlikView Expression (YAML)---source.qlikview-vars 2 | 8 | -------------------------------------------------------------------------------- /QlikView variables.sublime-build: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": ["inqlik.bat", "exp","$file"], 3 | "encoding": "utf8", 4 | "file_regex": "^>+ Parse error. File: \"(...*?)\", line: ([0-9]*)", 5 | "selector": "source.qlikview-vars", 6 | "variants": [ 7 | { 8 | "name": "Run", 9 | "cmd": ["inqlik.bat", "exp","$file"], 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /Settings/FormatNum.sublime-macro: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "args": null, 4 | "command": "copy" 5 | }, 6 | { 7 | "args": 8 | { 9 | "characters": "Money" 10 | }, 11 | "command": "insert" 12 | }, 13 | { 14 | "args": 15 | { 16 | "contents": "($0)" 17 | }, 18 | "command": "insert_snippet" 19 | }, 20 | { 21 | "args": null, 22 | "command": "paste" 23 | }, 24 | { 25 | "args": 26 | { 27 | "characters": ",'# ##0,00'" 28 | }, 29 | "command": "insert" 30 | } 31 | ] 32 | -------------------------------------------------------------------------------- /Syntax/ExpressionSection.tmPreferences: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | name 6 | Symbol List: Expression Section 7 | scope 8 | source.qlikview-vars meta.name.section.qlikview-vars 9 | settings 10 | 11 | showInSymbolList 12 | 1 13 | 14 | 15 | uuid 16 | c32ba3e2-4f2f-4ecf-922a-5b92ba7f3201 17 | 18 | -------------------------------------------------------------------------------- /Syntax/TabSymbol.tmPreferences: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | name 6 | Symbol List: Tab 7 | scope 8 | source.qvs meta.tab.qvs 9 | settings 10 | 11 | showInSymbolList 12 | 1 13 | symbolTransformation 14 | s/.*/$0/ 15 | 16 | uuid 17 | c32ba3e2-4f2f-4ecf-922a-5b92ba7f3203 18 | 19 | -------------------------------------------------------------------------------- /Syntax/TableSymbol.tmPreferences: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | name 6 | Symbol List: Tables 7 | scope 8 | source.qvs entity.name.type.table.qvs 9 | settings 10 | 11 | showInSymbolList 12 | 1 13 | symbolTransformation 14 | s/.*/ $0 \(Table\)/ 15 | 16 | uuid 17 | c32ba3e2-4f2f-4ecf-922a-5b92ba7f3201 18 | 19 | -------------------------------------------------------------------------------- /Syntax/SoubroutineSymbol.tmPreferences: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | name 6 | Symbol List: Subroutine 7 | scope 8 | source.qvs entity.name.type.subroutine.qvs 9 | settings 10 | 11 | showInSymbolList 12 | 1 13 | symbolTransformation 14 | s/.*/$0 \(SUB\)/ 15 | 16 | uuid 17 | c32ba3e2-4f2f-4ecf-922a-5b92ba7f3202 18 | 19 | -------------------------------------------------------------------------------- /Syntax/VariableSymbol.tmPreferences: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | name 6 | Symbol List: Variable 7 | scope 8 | source.qvs entity.name.type.variable.qvs 9 | settings 10 | 11 | showInSymbolList 12 | 1 13 | symbolTransformation 14 | s/.*/ $0 \(Var\)/ 15 | 16 | uuid 17 | c32ba3e2-4f2f-4ecf-922a-5b92ba7f320f 18 | 19 | -------------------------------------------------------------------------------- /Syntax/ExpressionSymbol.tmPreferences: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | name 6 | Symbol List: Expression 7 | scope 8 | source.qlikview-vars entity.name.type.expression.qlikview-vars 9 | settings 10 | 11 | showInSymbolList 12 | 1 13 | symbolTransformation 14 | s/.*/ $0/ 15 | 16 | uuid 17 | c32ba3e2-4f2f-4ecf-922a-5b92ba7f3200 18 | 19 | -------------------------------------------------------------------------------- /Settings/QlikView variables file.sublime-settings: -------------------------------------------------------------------------------- 1 | { 2 | "separator":".", 3 | "output_mode": "CSV", 4 | "expand_variables": false, 5 | "mappings": { 6 | "set": "SET", 7 | "let": "LET", 8 | "label": "Label", 9 | "comment": "Comment", 10 | "backgroundColor": "backgroundColor", 11 | "fontColor": "FontColor", 12 | "textFormat": "TextFormat", 13 | "enableCondition":"EnableCondition", 14 | "showCondition":"ShowCondition", 15 | "sortBy":"SortBy", 16 | "visualCueUpper":"VisualCueUpper", 17 | "visualCueLower":"VisualCueLower", 18 | "symbol":"Symbol", 19 | "thousandSymbol":"ThousandSymbol", 20 | "millionSymbol":"MillionSymbol", 21 | "billionSymbol":"BillionSymbol" 22 | } 23 | } -------------------------------------------------------------------------------- /util/test_expansion.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from qvvars import * 3 | class TestExpand(unittest.TestCase): 4 | 5 | # def setUp(self): 6 | # self.seq = range(10) 7 | 8 | def test_readerDummmy(self): 9 | reader = QvVarFileReader({}) 10 | reader.parse_content('') 11 | 12 | 13 | # # should raise an exception for an immutable sequence 14 | # self.assertRaises(TypeError, random.shuffle, (1,2,3)) 15 | 16 | # def test_choice(self): 17 | # element = random.choice(self.seq) 18 | # self.assertTrue(element in self.seq) 19 | 20 | # def test_sample(self): 21 | # with self.assertRaises(ValueError): 22 | # random.sample(self.seq, 20) 23 | # for element in random.sample(self.seq, 5): 24 | # self.assertTrue(element in self.seq) 25 | 26 | def testExpansion(content): 27 | reader = QvVarFileReader({}) 28 | reader.parsedContent(content) 29 | 30 | 31 | 32 | if __name__ == '__main__': 33 | unittest.main() -------------------------------------------------------------------------------- /Settings/QlikView script.sublime-settings: -------------------------------------------------------------------------------- 1 | { 2 | // For internal use 3 | "use_infovizion": false, 4 | "qv_executable": "c:\\Program Files\\QlikView\\qv.exe", 5 | // If set to false, Open qvw file and Reload qvw file command would run by internal python script 6 | // If set to true they would use inqlik command line application (inqlik CLI) 7 | "qv_script_use_cli": false, 8 | // Use QlikView script syntax checker from inqlik CLI (operative only when "qv_script_use_cli": true) 9 | "qv_script_check_syntax": false, 10 | // Valid options are 11 | // - "check": Check syntax of qvs file and print out found errors 12 | // - "check_and_reload": Check syntax, reload qvw file if errors not found 13 | // - "force_reload": Check syntax, reload qvw file even if errors found 14 | "qv_script_check_syntax_mode": "check_and_reload", 15 | // Implicit include file (use it for example to declare predefined variables of QlikView Deployment Framework) 16 | "qv_script_check_syntax_impicit_include_file": "default_inqlude.qvs", 17 | } -------------------------------------------------------------------------------- /Syntax/QlikView-reload-log.YAML-tmLanguage: -------------------------------------------------------------------------------- 1 | # [PackageDev] target_format: plist, ext: tmLanguage 2 | name: QlikView reload log file 3 | scopeName: source.qvw.log 4 | fileTypes: [qvw.log] 5 | uuid: 6b77dc03-7003-41f4-a9f6-287849f88d4a 6 | 7 | patterns: 8 | - include: '#assignment' 9 | - include: '#tableIdentifiers' 10 | - include: '#subroutine' 11 | - include: '#log_line' 12 | 13 | repository: 14 | log_line: 15 | patterns: 16 | - name: meta.line.qvw.log 17 | begin: ^ 18 | end: $ 19 | patterns: 20 | - include: source.qvs 21 | assignment: 22 | patterns: 23 | - match: ^\s*([LlSs][Ee][Tt])\s+((\w|\.)+)\s*= 24 | captures: 25 | '1': {name: keyword.control.qvs} 26 | tableIdentifiers: 27 | patterns: 28 | - name: support.constant.qvs 29 | match: ^(\s)*(\w+):(\s)*$ 30 | captures: 31 | '2': {name: support.constant.qvs} 32 | subroutine: 33 | patterns: 34 | - match: ^\s*([Ss][Uu][Bb])\s+((?:\w|\.)+) 35 | captures: 36 | '1': {name: keyword.control.qvs} 37 | '2': {name: support.class} -------------------------------------------------------------------------------- /Syntax/Comments (QlikView).tmPreferences: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | name 6 | Comments 7 | scope 8 | source.qvs 9 | settings 10 | 11 | shellVariables 12 | 13 | 14 | name 15 | TM_COMMENT_START 16 | value 17 | // 18 | 19 | 20 | name 21 | TM_COMMENT_START_2 22 | value 23 | /* 24 | 25 | 26 | name 27 | TM_COMMENT_END_2 28 | value 29 | */ 30 | 31 | 32 | name 33 | TM_COMMENT_DISABLE_INDENT_2 34 | value 35 | yes 36 | 37 | 38 | 39 | uuid 40 | 4f4b122d-bd00-4458-81e6-9e51be9f7b81 41 | 42 | 43 | -------------------------------------------------------------------------------- /Settings/QlikView variables.sublime-completions: -------------------------------------------------------------------------------- 1 | { 2 | "scope": "meta.newtag.qlikview-vars" , 3 | 4 | "completions": 5 | [ 6 | {"trigger": "comment:", "contents": "comment: $0"}, 7 | {"trigger": "label:", "contents": "label: $0"}, 8 | {"trigger": "definition:", "contents": "definition: $0"}, 9 | {"trigger": "backgroundColor:", "contents": "backgroundColor: $0"}, 10 | {"trigger": "tag:", "contents": "tag: $0"}, 11 | {"trigger": "fontColor:", "contents": "fontColor: $0"}, 12 | {"trigger": "textFormat:", "contents": "textFormat: $0"}, 13 | {"trigger": "macro:", "contents": "macro: $0"}, 14 | {"trigger": "description:", "contents": "description: $0"}, 15 | {"trigger": "enableCondition:", "contents": "enableCondition: $0"}, 16 | {"trigger": "showCondition:", "contents": "showCondition: $0"}, 17 | {"trigger": "sortBy:", "contents": "sortBy: $0"}, 18 | {"trigger": "visualCueUpper:", "contents": "visualCueUpper: $0"}, 19 | {"trigger": "visualCueUpper:", "contents": "visualCueUpper: $0"}, 20 | {"trigger": "visualCueUpper:", "contents": "visualCueUpper: $0"}, 21 | {"trigger": "visualCueUpper:", "contents": "visualCueUpper: $0"}, 22 | {"trigger": "visualCueLower:", "contents": "visualCueLower: $0"} 23 | ] 24 | } 25 | 26 | 27 | -------------------------------------------------------------------------------- /Settings/Default (Windows).sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | {"keys": ["ctrl+shift+l"], "command": "qlikview_open_log", 3 | "context": [{ "key": "selector", "operator": "equal", "operand": "source.qvs" }]}, 4 | {"keys": ["ctrl+shift+l"], "command": "qlikview_transform_log", 5 | "context": [{ "key": "selector", "operator": "equal", "operand": "source.qvw.log" }]}, 6 | {"keys": ["ctrl+shift+s"], "command": "qlikview_variables_export", 7 | "context": [{ "key": "selector", "operator": "equal", "operand": "source.qlikview-vars" }]}, 8 | {"keys": ["f12"], "command": "qlikview_goto_definition", 9 | "context": [{ "key": "selector", "operator": "equal", "operand": "source.qvs"}]}, 10 | {"keys": ["f12"], "command": "qlikview_goto_definition", 11 | "context": [{ "key": "selector", "operator": "equal", "operand": "source.qvw.log"}]}, 12 | {"keys": ["ctrl+f12"], "command": "qlikview_goto_file", 13 | "context": [{ "key": "selector", "operator": "equal", "operand": "source.qvs"}]}, 14 | {"keys": ["ctrl+shift+b"], "command": "qlikview_reload","args": {"commandVariant": "open"}, 15 | "context": [{ "key": "selector", "operator": "equal", "operand": "source.qvs"}]}, 16 | {"keys": ["f7"], "command": "qlikview_reload", 17 | "context": [{ "key": "selector", "operator": "equal", "operand": "source.qvs"}]} 18 | 19 | ] 20 | -------------------------------------------------------------------------------- /Settings/QlikView GUI only.sublime-completions: -------------------------------------------------------------------------------- 1 | { 2 | "scope": "meta.expression.qlikview-vars" , 3 | "completions": 4 | [ 5 | {"trigger": "GetActiveSheetID", "contents": "GetActiveSheetID()$0"}, 6 | {"trigger": "GetCurrentField", "contents": "GetCurrentField(${1:groupname})$0"}, 7 | {"trigger": "GetFieldSelections", "contents": "GetFieldSelections('${1:fieldName}',${2:','},${3:6})$0"}, 8 | {"trigger": "GetSelectedCount", "contents": "GetSelectedCount('${1:fieldName}')$0"}, 9 | {"trigger": "GetPossibleCount", "contents": "GetPossibleCount('${1:fieldName}')$0"}, 10 | {"trigger": "GetExcludedCount", "contents": "GetExcludedCount('${1:fieldName}')$0"}, 11 | {"trigger": "GetAlternativeCount", "contents": "GetAlternativeCount('${1:fieldName}')$0"}, 12 | {"trigger": "GetNotSelectedCount", "contents": "GetNotSelectedCount('${1:fieldName}')$0"}, 13 | {"trigger": "GetExtendedProperty", "contents": "GetExtendedProperty('${1:name}','${2:objectID}')$0"}, 14 | {"trigger": "StateName", "contents": "StateName()$0"}, 15 | {"trigger": "DocumentTitle", "contents": "DocumentTitle()$0"}, 16 | {"trigger": "GetObjectField", "contents": "GetObjectField('${1:index}')$0"}, 17 | {"trigger": "GetCurrentSelections", "contents": "GetCurrentSelections(${1:chr(13)},${2:':'},${3:','},${4:6})$0"} 18 | ] 19 | } -------------------------------------------------------------------------------- /qvw_log_viewer.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: UTF-8 -*- 2 | import sublime 3 | import sublime_plugin 4 | import os 5 | import collections 6 | import sys 7 | import re 8 | class QlikviewTransformLogCommand(sublime_plugin.TextCommand): 9 | moduleSettings = None 10 | edit = None 11 | path = '' 12 | timer = None 13 | def run(self, edit): 14 | self.edit = edit 15 | self.path = self.view.file_name() 16 | self.transform() 17 | 18 | def transform(self): 19 | view = self.view 20 | edit = self.edit 21 | view.set_read_only(False) 22 | view.set_scratch(True) 23 | txt = view.substr(sublime.Region(0,view.size())) 24 | view.erase(edit,sublime.Region(0,view.size())) 25 | trace_mode = False 26 | for line in txt.splitlines(): 27 | if trace_mode: 28 | line = re.sub(r'^(\s*\d+[/.-]\d+[/.-]\d{4} \d+:\d+:\d+( AM| PM)?: .{4})',r'//>> \1',line) 29 | trace_mode = False 30 | else: 31 | line = re.sub(r'^\s*\d+[/.-]\d+[/.-]\d{4} \d+:\d+:\d+( AM| PM)?:\s+\d{4}','',line) 32 | if re.match(r'^\s*TRACE',line,flags=re.IGNORECASE): 33 | trace_mode = True 34 | print(line) 35 | line = re.sub(r'^(\s*\d+[/.-]\d+[/.-]\d{4} \d+:\d+:\d+( AM| PM)?: {4})',r'//>> \1',line) 36 | self.addLine(line) 37 | 38 | def addLine(self,line): 39 | self.view.insert(self.edit, self.view.size(), line+'\n') 40 | def is_enabled(self): 41 | return sublime.active_window().active_view().file_name().upper().endswith('.QVW.LOG') -------------------------------------------------------------------------------- /new_etl_module.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: UTF-8 -*- 2 | import sublime 3 | import sublime_plugin 4 | import re 5 | import os 6 | class NewEtlModuleCommand(sublime_plugin.WindowCommand): 7 | fileName = '' 8 | qvwTemplate = '' 9 | shebang = '' 10 | def run(self, commandVariant=None): 11 | view = self.window.active_view() 12 | qv_executable = view.settings().get("qv_executable","c:\\Program Files\\QlikView\\qv.exe") 13 | firstLine = view.substr(view.line(0)) 14 | self.fileName = view.file_name() 15 | baseName, ext = os.path.splitext(os.path.basename(self.fileName)) 16 | if ext != '.qvs': 17 | sublime.error_message('New ETL module command shoud be invoked from within QlikView script') 18 | return 19 | qvwFile = '' 20 | testFile = '' 21 | print (firstLine) 22 | if re.match(r'\/\/\#\!', firstLine): 23 | self.shebang = re.sub(r'\/\/\#\!','',firstLine) 24 | testFile = os.path.abspath(os.path.join(os.path.dirname(self.fileName),self.shebang,'_NewFileTemplate.qvw')) 25 | if os.path.exists(testFile): 26 | self.qvwTemplate = testFile 27 | if self.qvwTemplate == '': 28 | sublime.error_message('File not found: %s' % self.qvwTemplate) 29 | return 30 | self.window.show_input_panel('Enter name for new module:','NewModule',self.createModule,None,None) 31 | def createModule(self,moduleName): 32 | targetQvwFile = os.path.abspath(os.path.join(os.path.dirname(self.fileName),self.shebang,moduleName + '.qvw')) 33 | if os.path.exists(targetQvwFile): 34 | sublime.error_message('File %s already exists' % targetQvwFile) 35 | return 36 | self.window.run_command("exec", { "cmd": ["cmd","/C","copy",self.qvwTemplate,targetQvwFile]}) 37 | targetQvsFile = os.path.join(os.path.dirname(self.fileName),moduleName+'.qvs') 38 | self.window.run_command("exec", { "cmd": ["cmd","/C","copy",self.fileName,targetQvsFile]}) 39 | self.window.open_file(targetQvsFile) 40 | -------------------------------------------------------------------------------- /qvw_open_log.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: UTF-8 -*- 2 | import sublime 3 | import sublime_plugin 4 | import os 5 | import re 6 | class QlikviewOpenLogCommand(sublime_plugin.WindowCommand): 7 | view = None 8 | def run(self, commandVariant=None): 9 | view = self.window.active_view() 10 | useInfovizion = view.settings().get("use_infovizion",False) 11 | firstLine = view.substr(view.line(0)) 12 | fileName = view.file_name() 13 | if useInfovizion: 14 | baseName, ext = os.path.splitext(os.path.basename(fileName)) 15 | testFile = os.path.join(os.path.dirname(fileName),'logs',baseName +'.qvw.log') 16 | if not os.path.exists(testFile): 17 | print('Log file not found: %s' % testFile) 18 | sublime.error_message('Log file not found: %s' % testFile) 19 | else: 20 | self.view = self.window.open_file(testFile) 21 | else: 22 | baseName, ext = os.path.splitext(os.path.basename(fileName)) 23 | testFile = '' 24 | if re.match(r'\/\/\#\!', firstLine): 25 | shebang = re.sub(r'\/\/\#\!','',firstLine) 26 | if shebang.endswith('.qvw'): 27 | testFile = shebang 28 | if os.path.exists(shebang): 29 | testFile = shebang + '.log' 30 | else: 31 | testFile = os.path.abspath(os.path.join(os.path.dirname(fileName),shebang,baseName + '.qvw.log')) 32 | else: 33 | testFile = os.path.join(os.path.dirname(fileName),baseName +'.qvw.log') 34 | if not os.path.exists(testFile): 35 | print('Log file not found: %s' % testFile) 36 | sublime.error_message('Log file not found: %s' % testFile) 37 | else: 38 | self.view = self.window.open_file(testFile) 39 | self.transform() 40 | def transform(self): 41 | if self.view.is_loading(): 42 | sublime.set_timeout_async(self.transform,100) 43 | else: 44 | self.view.run_command('qlikview_transform_log') 45 | def is_enabled(self): 46 | return self.window.active_view().file_name().upper().endswith('.QVS') -------------------------------------------------------------------------------- /qlikview_goto_file.py: -------------------------------------------------------------------------------- 1 | import sublime, sublime_plugin 2 | import os 3 | from fnmatch import fnmatch 4 | 5 | class QlikviewGotoFile(sublime_plugin.WindowCommand): 6 | def find_files(self, fileName): 7 | result = set([]) 8 | if os.path.isfile(fileName): 9 | return [fileName] 10 | for folder in sublime.active_window().folders(): 11 | for root, dirs, files in os.walk(folder): 12 | for file in files: 13 | if file.upper().endswith(fileName.upper()): 14 | result.add(os.path.join(root, file)) 15 | return list(result) 16 | def open_file(self,fileName): 17 | open_externally = False 18 | for gpat in sublime.active_window().active_view().settings().get("open_externally_patterns", []): 19 | if fnmatch(fileName, gpat): 20 | open_externally = True 21 | print(fileName) 22 | if open_externally: 23 | sublime.status_message("Opening file ...... " + fileName) 24 | os.startfile(fileName) 25 | sublime.status_message("") 26 | else: 27 | sublime.active_window().open_file(fileName) 28 | def run(self, fileName = None): 29 | print ('qlikview_goto_file'); 30 | orig_sel = None 31 | v = self.window.active_view() 32 | if v: 33 | orig_sel = [r for r in v.sel()] 34 | 35 | if not fileName and not v: 36 | return 37 | 38 | if not fileName: 39 | pt = v.sel()[0] 40 | 41 | fileName = v.substr(v.expand_by_class(pt, 42 | sublime.CLASS_WORD_START | sublime.CLASS_WORD_END, 43 | "[]{}()<>:=")) 44 | print('Find and open file %s' % fileName) 45 | fileName = fileName.strip('\\/'); 46 | files = self.find_files(fileName) 47 | print (files) 48 | if len(files) == 0: 49 | sublime.status_message("Unable to find " + fileName) 50 | elif len(files) == 1: 51 | print('Opening') 52 | self.open_file(files[0]) 53 | else: 54 | self.window.show_quick_panel( 55 | files, 56 | lambda x: self.open_file(files[x])) 57 | -------------------------------------------------------------------------------- /Settings/QlikView script only.sublime-completions: -------------------------------------------------------------------------------- 1 | { 2 | "scope": "source.qvs" , 3 | "completions": 4 | [ 5 | {"trigger": "FileBasename", "contents": "FileBasename()$0"}, 6 | {"trigger": "FileDir", "contents": "FileDir()$0"}, 7 | {"trigger": "FileExtension", "contents": "FileExtension()$0"}, 8 | {"trigger": "GetFolderPath", "contents": "GetFolderPath()$0"}, 9 | {"trigger": "FileName", "contents": "FileName()$0"}, 10 | {"trigger": "FilePath", "contents": "FilePath()$0"}, 11 | {"trigger": "FileSize", "contents": "FileSize('${1:fileName}')$0"}, 12 | {"trigger": "FileTime", "contents": "FileTime('${1:fileName}')$0"}, 13 | {"trigger": "QvdCreateTime", "contents": "QvdCreateTime('${1:fileName}')$0"}, 14 | {"trigger": "NoOfFields", "contents": "NoOfFields('${1:tableName}')$0"}, 15 | {"trigger": "NoOfRows", "contents": "NoOfRows('${1:tableName}')$0"}, 16 | {"trigger": "NoOfTables", "contents": "NoOfTables()$0"}, 17 | {"trigger": "TableName", "contents": "TableName(${1:tableNumber})$0"}, 18 | {"trigger": "QvdNoOfRecords", "contents": "QvdNoOfRecords('${1:fileName}')$0"}, 19 | {"trigger": "QvdNoOfFields", "contents": "QvdNoOfFields('${1:fileName}')$0"}, 20 | {"trigger": "QvdFieldName", "contents": "QvdFieldName('${1:fileName}','${2:fieldNo}')$0"}, 21 | {"trigger": "Peek", "contents": "Peek('${1:fieldName}',${2:row}','${3:tableName}')$0"}, 22 | {"trigger": "QvdTableName", "contents": "QvdTableName('${1:fileName}')$0"}, 23 | {"trigger": "FieldName", "contents": "FieldName(${1:fieldNo},'${2:tableName}')$0"}, 24 | {"trigger": "FieldNumber", "contents": "FieldNumber('${1:field}','${2:tableName}')$0"}, 25 | {"trigger": "ConnectString", "contents": "ConnectString()$0"}, 26 | {"trigger": "IsPartialReload", "contents": "IsPartialReload()$0"}, 27 | {"trigger": "MsgBox", "contents": "MsgConnectString:msg}')$0"}, 28 | {"trigger": "Attribute", "contents": "Attribute('${1:fileName}','${2:attributename}')$0"}, 29 | {"trigger": "Input", "contents": "Input('${1:cue}','${2:caption}')$0"}, 30 | {"trigger": "QV_SKIP_PARSING", "contents": "//#!QV_SKIP_PARSING"}, 31 | {"trigger": "QV_TRACE_TABLES", "contents": "//#!QV_TRACE_TABLES"}, 32 | {"trigger": "QV_SUPPRESS_ERROR", "contents": "//#!QV_SUPPRESS_ERROR"}, 33 | {"trigger": "QV_TRACE_USER_VARIABLES", "contents": "//#!QV_TRACE_USER_VARIABLES"} 34 | ] 35 | } -------------------------------------------------------------------------------- /Syntax/QlikView-reload-log.tmLanguage: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | fileTypes 6 | 7 | qvw.log 8 | 9 | name 10 | QlikView reload log file 11 | patterns 12 | 13 | 14 | include 15 | #assignment 16 | 17 | 18 | include 19 | #tableIdentifiers 20 | 21 | 22 | include 23 | #subroutine 24 | 25 | 26 | include 27 | #log_line 28 | 29 | 30 | repository 31 | 32 | assignment 33 | 34 | patterns 35 | 36 | 37 | captures 38 | 39 | 1 40 | 41 | name 42 | keyword.control.qvs 43 | 44 | 45 | match 46 | ^\s*([LlSs][Ee][Tt])\s+((\w|\.)+)\s*= 47 | 48 | 49 | 50 | log_line 51 | 52 | patterns 53 | 54 | 55 | begin 56 | ^ 57 | end 58 | $ 59 | name 60 | meta.line.qvw.log 61 | patterns 62 | 63 | 64 | include 65 | source.qvs 66 | 67 | 68 | 69 | 70 | 71 | subroutine 72 | 73 | patterns 74 | 75 | 76 | captures 77 | 78 | 1 79 | 80 | name 81 | keyword.control.qvs 82 | 83 | 2 84 | 85 | name 86 | support.class 87 | 88 | 89 | match 90 | ^\s*([Ss][Uu][Bb])\s+((?:\w|\.)+) 91 | 92 | 93 | 94 | tableIdentifiers 95 | 96 | patterns 97 | 98 | 99 | captures 100 | 101 | 2 102 | 103 | name 104 | support.constant.qvs 105 | 106 | 107 | match 108 | ^(\s)*(\w+):(\s)*$ 109 | name 110 | support.constant.qvs 111 | 112 | 113 | 114 | 115 | scopeName 116 | source.qvw.log 117 | uuid 118 | 6b77dc03-7003-41f4-a9f6-287849f88d4a 119 | 120 | 121 | -------------------------------------------------------------------------------- /Syntax/QlikView variables file.YAML-tmLanguage: -------------------------------------------------------------------------------- 1 | # [PackageDev] target_format: plist, ext: tmLanguage 2 | name: QlikView vars file 3 | fileTypes: [qlikview-vars] 4 | 5 | scopeName: source.qlikview-vars 6 | 7 | uuid: 6b77dc03-7003-41f4-a9f6-287849f88d5f 8 | 9 | patterns: 10 | 11 | - include: '#expressionName' 12 | 13 | - include: '#section' 14 | 15 | - include: '#yamlNewDocument' 16 | 17 | - include: '#expression' 18 | 19 | - include: '#expressionWithEqualSign' 20 | 21 | - include: '#expressionWithoutEqualSign' 22 | 23 | - include: '#simpleField' 24 | 25 | - include: '#def' 26 | 27 | - include: '#newtag' 28 | 29 | repository: 30 | def: 31 | patterns: 32 | - name: meta.expression.qlikview-vars 33 | begin: ^#define\s+ 34 | beginCaptures: 35 | '0': {name: support.constant.qlikview-vars} 36 | end: ^(?=\S) 37 | endCaptures: 38 | '0': {name: support.constant.qlikview-vars} 39 | patterns: 40 | - include: source.qvs 41 | 42 | expression: 43 | patterns: 44 | - name: meta.expression.qlikview-vars 45 | begin: '^(definition|macro|condition|backgroundColor|fontColor|textFormat|enableCondition|showCondition|sortBy|visualCueUpper|visualCueLower|symbol|thousandSymbol|width|millionSymbol|billionSymbol|family|type|selectorLabel|format|calendar):' 46 | beginCaptures: 47 | '0': {name: support.constant.qlikview-vars} 48 | end: ^(?=\S) 49 | endCaptures: 50 | '0': {name: support.constant.qlikview-vars} 51 | patterns: 52 | - include: source.qvs 53 | 54 | expressionWithEqualSign: 55 | patterns: 56 | - name: meta.expression.qlikview-vars 57 | begin: ^(label|comment):\s*(?==) 58 | beginCaptures: 59 | '0': {name: support.constant.qlikview-vars} 60 | end: ^(?=\S) 61 | endCaptures: 62 | '0': {name: support.constant.qlikview-vars} 63 | patterns: 64 | - include: source.qvs 65 | 66 | expressionWithoutEqualSign: 67 | patterns: 68 | - name: string.single.quoted.qlikview-vars 69 | begin: '^(label|comment):' 70 | beginCaptures: 71 | '0': {name: support.constant.qlikview-vars} 72 | end: ^(?=\S) 73 | endCaptures: 74 | '0': {name: support.constant.qlikview-vars} 75 | newtag: 76 | patterns: 77 | - name: meta.newtag.qlikview-vars 78 | match: (^(\w)*(?!:)) 79 | 80 | expressionName: 81 | patterns: 82 | - match: ^\s*(set|let):\s*(\w[\w_.]+)\s*$ 83 | captures: 84 | '1': {name: support.constant.qlikview-vars} 85 | '2': {name: support.variable.qlikview-vars entity.name.type.expression.qlikview-vars} 86 | 87 | simpleField: 88 | patterns: 89 | - name: support.constant.qlikview-vars 90 | match: (^(\s)*(tag|description):(\s)*) 91 | 92 | yamlNewDocument: 93 | patterns: 94 | - name: support.constant.qlikview-vars 95 | match: ^---$ 96 | section: 97 | patterns: 98 | - match: ^(#SECTION )\s*(.+)$ 99 | captures: 100 | '1': {name: comment.line.double-dash.qlikview-vars} 101 | '2': {name: support.variable.qlikview-vars meta.name.section.qlikview-vars} 102 | -------------------------------------------------------------------------------- /qlikview_goto_definition.py: -------------------------------------------------------------------------------- 1 | import sublime, sublime_plugin 2 | 3 | import sublime, sublime_plugin 4 | 5 | class QlikviewGotoDefinition(sublime_plugin.WindowCommand): 6 | def goto_location(self, l): 7 | fname, display_fname, rowcol = l 8 | row, col = rowcol 9 | 10 | v = self.window.open_file(fname + ":" + str(row) + ":" + str(col), sublime.ENCODED_POSITION) 11 | 12 | def select_entry(self, locations, idx, orig_view, orig_sel): 13 | if idx >= 0: 14 | self.goto_location(locations[idx]) 15 | else: 16 | # TODO: restore sel 17 | if orig_view: 18 | self.window.focus_view(orig_view) 19 | 20 | def highlight_entry(self, locations, idx): 21 | fname, display_fname, rowcol = locations[idx] 22 | row, col = rowcol 23 | 24 | self.window.open_file(fname + ":" + str(row) + ":" + str(col), 25 | sublime.TRANSIENT | sublime.ENCODED_POSITION) 26 | 27 | def format_location(self, l): 28 | fname, display_fname, rowcol = l 29 | row, col = rowcol 30 | 31 | return display_fname + ":" + str(row) 32 | 33 | def lookup_symbol(self, symbol): 34 | index_locations = self.window.lookup_symbol_in_index(symbol) 35 | open_file_locations = self.window.lookup_symbol_in_open_files(symbol) 36 | 37 | def file_in_location_list(fname, locations): 38 | for l in locations: 39 | if l[0] == fname: 40 | return True 41 | return False; 42 | 43 | # Combine the two lists, overriding results in the index with results 44 | # from open files, while trying to preserve the order of the files in 45 | # the index. 46 | locations = [] 47 | ofl_ignore = [] 48 | for l in index_locations: 49 | if file_in_location_list(l[0], open_file_locations): 50 | if not file_in_location_list(l[0], ofl_ignore): 51 | for ofl in open_file_locations: 52 | if l[0] == ofl[0]: 53 | locations.append(ofl) 54 | ofl_ignore.append(ofl) 55 | else: 56 | locations.append(l) 57 | 58 | for ofl in open_file_locations: 59 | if not file_in_location_list(ofl[0], ofl_ignore): 60 | locations.append(ofl) 61 | 62 | return locations 63 | 64 | def run(self, symbol = None): 65 | orig_sel = None 66 | v = self.window.active_view() 67 | if v: 68 | orig_sel = [r for r in v.sel()] 69 | 70 | if not symbol and not v: 71 | return 72 | 73 | if not symbol: 74 | pt = v.sel()[0] 75 | 76 | symbol = v.substr(v.expand_by_class(pt, 77 | sublime.CLASS_WORD_START | sublime.CLASS_WORD_END, 78 | "[]{}()<>:=")) 79 | locations = self.lookup_symbol(symbol) 80 | 81 | if len(locations) == 0: 82 | symbol = v.substr(v.word(pt)) 83 | locations = self.lookup_symbol(symbol) 84 | 85 | else: 86 | locations = self.lookup_symbol(symbol) 87 | 88 | if len(locations) == 0: 89 | sublime.status_message("Unable to find " + symbol) 90 | elif len(locations) == 1: 91 | self.goto_location(locations[0]) 92 | else: 93 | self.window.show_quick_panel( 94 | [self.format_location(l) for l in locations], 95 | lambda x: self.select_entry(locations, x, v, orig_sel), 96 | on_highlight = lambda x: self.highlight_entry(locations, x)) 97 | -------------------------------------------------------------------------------- /qvw_load.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: UTF-8 -*- 2 | import sublime 3 | import sublime_plugin 4 | import os 5 | import re 6 | class QlikviewReloadCommand(sublime_plugin.WindowCommand): 7 | def run(self, commandVariant=None): 8 | self.window.run_command('save') 9 | view = self.window.active_view() 10 | qv_executable = view.settings().get("qv_executable","c:\\Program Files\\QlikView\\qv.exe") 11 | useInfovizion = view.settings().get("use_infovizion",False) 12 | if view.settings().get('qv_script_use_cli') == True: 13 | self.runCli(view, qv_executable, commandVariant) 14 | else: 15 | if useInfovizion: 16 | self.runByInfovizion(view, commandVariant) 17 | else: 18 | self.runPython(view, qv_executable, commandVariant) 19 | def runPython(self, view, qv_executable, commandVariant=None): 20 | self.window.run_command('save') 21 | firstLine = view.substr(view.line(0)) 22 | fileName = view.file_name() 23 | baseName, ext = os.path.splitext(os.path.basename(fileName)) 24 | qvwFile = '' 25 | testFile = '' 26 | print (firstLine) 27 | if re.match(r'\/\/\#\!', firstLine): 28 | shebang = re.sub(r'\/\/\#\!','',firstLine) 29 | if shebang.endswith('.qvw'): 30 | testFile = shebang 31 | if os.path.exists(shebang): 32 | qvwFile = shebang 33 | else: 34 | testFile = os.path.abspath(os.path.join(os.path.dirname(fileName),shebang,baseName + '.qvw')) 35 | if os.path.exists(testFile): 36 | qvwFile = testFile 37 | else: 38 | testFile = os.path.join(os.path.dirname(fileName),baseName +'.qvw') 39 | if os.path.exists(testFile): 40 | qvwFile = testFile 41 | if qvwFile == '': 42 | sublime.error_message('File not found: %s' % testFile) 43 | else: 44 | sublime.status_message('Reloading file %s' % qvwFile) 45 | print("commandVariant", commandVariant) 46 | if commandVariant is None: 47 | self.window.run_command("exec", { "cmd": [qv_executable,"/R","/nodata","/Nosecurity",qvwFile]}) 48 | else: 49 | self.window.run_command("exec", { "cmd": ["cmd","/C",qv_executable,qvwFile]}) 50 | def runCli(self, view, qv_executable, commandVariant=None): 51 | file_regex = "^>* Parse error. File: \"(...*?)\", line: ([0-9]*)" 52 | scriptPath = "%s\\Inqlik-Tools\\bin\\inqlik.bat" % sublime.packages_path() 53 | fileName = view.file_name() 54 | if commandVariant is None: 55 | cliCommand = 'just_reload' 56 | include = 'default_include.qvs' 57 | if view.settings().get('qv_script_check_syntax') == True: 58 | cliCommand = view.settings().get('qv_script_check_syntax_mode','force_reload') 59 | include = view.settings().get('qv_script_check_syntax_impicit_include_file','default_inqlude.qvs') 60 | self.window.run_command("exec", { "file_regex": file_regex, "cmd": [scriptPath,"qvs", "--command=%s" % cliCommand, fileName]}) 61 | print([scriptPath,"qvs", "--command=%s" % cliCommand, fileName]) 62 | else: 63 | self.window.run_command("exec", { "file_regex": file_regex, "cmd": [scriptPath,"qvs", "--command=open",fileName]}) 64 | def runByInfovizion(self, view, commandVariant=None): 65 | file_regex = "^File: (.+), line: (.+)" 66 | executable = "c:\\Programs\\infovizion\\bin\\dart" 67 | snapshot = "c:\\Programs\\infovizion\\bin\\infovizion.dart.snapshot" 68 | work_dir = "c:\\Programs\\infovizion\\" 69 | fileName = view.file_name() 70 | if view.settings().get('qv_script_check_syntax') == True: 71 | dummy = True 72 | self.window.run_command("exec", { "working_dir": work_dir, "file_regex": file_regex, "cmd": [executable, snapshot,"sense","app-reload", "--script", fileName]}) 73 | 74 | 75 | -------------------------------------------------------------------------------- /Settings/Main.sublime-menu: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "children": [ 4 | { 5 | "children": [ 6 | { 7 | "children": [ 8 | { 9 | "command": "new_etl_module", 10 | "caption": "New ETL module" 11 | }, 12 | { 13 | "command": "qlikview_open_log", 14 | "caption": "Open and reformat qvw.log file" 15 | }, 16 | { 17 | "command": "qlikview_transform_log", 18 | "caption": "Reformat qvw.log file" 19 | }, 20 | { 21 | "command": "qlikview_variables_export", 22 | "caption": "Expand variables and export file" 23 | } 24 | ], 25 | "id": "qlikview", 26 | "caption": "InQlik-Tools" 27 | } 28 | ], 29 | "id": "packages", 30 | "caption": "Packages" 31 | } 32 | ], 33 | "id": "tools" 34 | }, 35 | { 36 | "children": [ 37 | { 38 | "children": [ 39 | { 40 | "children": [ 41 | { 42 | "args": { 43 | "file": "${packages}/InQlik-Tools/README.md" 44 | }, 45 | "command": "open_file", 46 | "caption": "README" 47 | }, 48 | { 49 | "caption": "-" 50 | }, 51 | { 52 | "args": { 53 | "file": "${packages}/Inqlik-Tools/Settings/QlikView variables file.sublime-settings" 54 | }, 55 | "command": "open_file", 56 | "caption": "QlikView Variables file settings: Default" 57 | }, 58 | { 59 | "args": { 60 | "file": "${packages}/User/QlikView variables file.sublime-settings" 61 | }, 62 | "command": "open_file", 63 | "caption": "QlikView Variables file settings: User" 64 | }, 65 | { 66 | "caption": "-" 67 | }, 68 | { 69 | "args": { 70 | "file": "${packages}/Inqlik-Tools/Settings/QlikView script.sublime-settings" 71 | }, 72 | "command": "open_file", 73 | "caption": "QlikView script settings: Default" 74 | }, 75 | { 76 | "args": { 77 | "file": "${packages}/User/QlikView script.sublime-settings" 78 | }, 79 | "command": "open_file", 80 | "caption": "QlikView script settings: User" 81 | }, 82 | { 83 | "caption": "-" 84 | } 85 | ], 86 | "caption": "InQlik-Tools" 87 | } 88 | ], 89 | "mnemonic": "P", 90 | "id": "package-settings", 91 | "caption": "Package Settings" 92 | } 93 | ], 94 | "mnemonic": "n", 95 | "id": "preferences", 96 | "caption": "Preferences" 97 | } 98 | ] -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ###Changelog 2 | 3 | ####0.1.40 4 | 5 | - New Expression file export format: JSON. Additional attributes for Infovizion in-house Sense development process 6 | 7 | ####0.1.39 8 | 9 | - Autosave current QVS file before reloading. That imitate build system default behaviour 10 | 11 | ####0.1.38 12 | 13 | - Bugfix for syntax check. Temporarily not using `qv_script_check_syntax_impicit_include_file` setting 14 | 15 | ####0.1.37 16 | 17 | - Sublime Text in version 3080 introduced serious regression in default Build Systems behaviour. Default key binding `F7` and `Ctrl-Shift-B` currently not run alternative build tasks (`Reload` and `Open qvw file` in case if former build system for QVS files) but pops up some menu with some commmand variants. In this version I recover previous behaviour using basic syntax specific key-bindings. `F7` silently run `Reload` command. `Ctrl-Shift-B` run `Open qvw file` command as in previous revisions of ST3. 18 | - Plugin optionally can use `Inqlik CLI` command line utility for some tasks. That utility now shipped with package 19 | - `QlikView script settings` items added to `Package settings\Inqlik-Tools` menu. 20 | - QlikView script settings extended to provide options for using `Inqlik CLI` for `Reload` and `Open qvw file` tasks. `Reload` task can include check syntax phase 21 | - Inqlik CLI supports сustom file name in relative build paths. [See feature request](https://github.com/inqlik/inqlik-tools/issues/5) 22 | - Package settings defaults to using old python code for reload and open qvw file tasks 23 | 24 | ####0.1.36 25 | 26 | - Add build system for Expression files. Use external (Inqlik CLI) tool 27 | 28 | ####0.1.35 29 | 30 | - Bugfix for https://github.com/inqlik/inqlik-tools/issues/3 31 | 32 | ####0.1.34 33 | 34 | - Bugfix for https://github.com/inqlik/inqlik-tools/issues/2 35 | 36 | 37 | ####0.1.33 38 | 39 | - Permit unicode variable names. Again 40 | 41 | ####0.1.32 42 | 43 | - By default TAG column in QDF variables file filled with values from SECTION, it ca be overloaded by explicit tag: tag, Added completions for qvs parser directives. Bugfix for 44 | 45 | ####0.1.31 46 | 47 | - Bugfix for tabular csv export format for Expression Editor. Export format was erroneously named `CVS` both in README and in plugin code. 48 | Name for tabular csv format fixed and changed to `CSV` 49 | 50 | ####0.1.30 51 | 52 | - Some changes in auto-completion for functions 53 | - Added menu command and key combination for command `Expand variables and export file` in Expression editor 54 | - Plain QVS export format temporarily removed from Expression Editor. 55 | - `.no-sublime-package` file flag added to project. Packaged format sometimes lead to bugs. 56 | 57 | ####0.1.29 58 | 59 | - Added auto-completion for most functions with cues for parameters 60 | 61 | ####0.1.28 62 | 63 | - Bugfix in NewExpression snippet 64 | 65 | ####0.1.27 66 | 67 | - Bugfix in goto_files 68 | 69 | ####0.1.24 70 | 71 | - Tabular CSV format added for Expression Editor 72 | 73 | ####0.1.23 74 | 75 | - Bugfix to qvw.log files viewer 76 | 77 | ####0.1.22 78 | 79 | - Qvw.log file viewer added 80 | - Menu and keyboard (ctrl-shilt+l) commands for opening and reformat qvw.log files 81 | 82 | ####0.1.21 83 | 84 | - Package renamed to InQlik Tools 85 | - Tags names changed to lowercase camelCase format 86 | - Menu items for package preferences 87 | - Default settings for variable files 88 | 89 | ####0.1.20 90 | 91 | - Bugfix in error processing in variable files plugin 92 | - Additional tags in variable files plugin 93 | 94 | ####0.1.19 95 | 96 | - Symbols in qvs: suroutines, variables and table identifiers. Tabs are local symbols 97 | - Symbols in expression files: expression names and \#SECTION tags 98 | 99 | ####0.1.18 100 | 101 | - QVD Viewer plugin added. (ST3 only) 102 | 103 | ####0.1.17 104 | 105 | - Expression file plugin refactored 106 | 107 | ####0.1.16 108 | 109 | - qlikview_vars.py refactored 110 | - Syntax for qlikview-vars files changed 111 | - Support for most properties of qlikview expression added. 112 | - Settings for configuration of derived variable names 113 | 114 | ####0.1.15 115 | 116 | - Bugfix for relative paths in shebang for Build system. Tested in QlikView Deployment Framework environment 117 | 118 | ####0.1.14 119 | 120 | - Get rid of qvw_load.bat. Build system now runs custom-made command QlikviewReloadCommand. QlikView executable path can be changed in user settings 121 | 122 | ####0.1.13 123 | 124 | - qvw_load.bat fixed. Build system now works with qvs scripts encoded with UTF8 with BOM 125 | 126 | ####0.1.12 127 | 128 | - Readme sections for Build system usage and installation added 129 | 130 | ####0.1.11 131 | 132 | - Legacy syntax for expression files removed. Futher development will use QlikView variables files compatible with QlikView Deployment Framework 133 | - Build system added. Able to run batch reload of qvw file in same directory or explicitely set by shebang syntax 134 | 135 | 136 | ####0.1.10 137 | 138 | - Initial release for Package Control Channel -------------------------------------------------------------------------------- /qvd_viewer.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: UTF-8 -*- 2 | import sublime 3 | import sublime_plugin 4 | import os 5 | import collections 6 | from xml.dom import minidom 7 | import sys 8 | 9 | class QvdField: 10 | fieldName = '' 11 | uniqValues = 0 12 | memoryUsage = 0 13 | class QvdTable: 14 | noOfRecords = 0 15 | tableName = '' 16 | creatorDoc = '' 17 | createdTime = '' 18 | fields = [] 19 | 20 | class QlikviewQvdFileListener(sublime_plugin.EventListener): 21 | 22 | EXT_QLIKVIEW_QVD = ".QVD" 23 | def on_activated(self,view): 24 | if not self.is_ST3(): 25 | return 26 | fn = view.file_name() 27 | if (fn is None): 28 | return 29 | if (fn.upper().endswith(self.EXT_QLIKVIEW_QVD)): 30 | view.run_command('qvd_viewer',{'cmd':''}) 31 | def is_ST3(self): 32 | ''' check if ST3 based on python version ''' 33 | version = sys.version_info 34 | if isinstance(version, tuple): 35 | version = version[0] 36 | elif getattr(version, 'major', None): 37 | version = version.major 38 | return (version >= 3) 39 | 40 | class QvdViewerCommand(sublime_plugin.TextCommand): 41 | moduleSettings = None 42 | edit = None 43 | path = '' 44 | def run(self, edit, cmd=''): 45 | self.edit = edit 46 | self.path = self.view.file_name() 47 | sublime.active_window().run_command('close') 48 | # view.run_command('close') 49 | self.view = sublime.active_window().new_file() 50 | view = self.view 51 | view.set_scratch(True) 52 | token = collections.deque() 53 | tokenMarker = collections.deque([b'<',b'/',b'Q',b'v',b'd',b'T',b'a',b'b',b'l',b'e',b'H',b'e',b'a',b'd',b'e',b'r',b'>']) 54 | token = collections.deque(tokenMarker) 55 | tokenStr = collections.deque() 56 | buff = collections.deque() 57 | n = 0 58 | headerFound = False 59 | with open(self.path, 'rb') as f: 60 | while True: 61 | char = f.read(1) 62 | n = n + 1 63 | if n > 100000: 64 | break 65 | if char =='': 66 | break 67 | buff.append(char) 68 | token.append(char) 69 | token.popleft() 70 | if token == tokenMarker: 71 | headerFound = True 72 | break 73 | if not headerFound: 74 | self.addLine('ERROR: QvdFile header have not been recognized') 75 | return 76 | buffString = b''.join(buff) 77 | xml = minidom.parseString(buffString) 78 | self.parseHeader(xml) 79 | def parseHeader(self, xml): 80 | table = QvdTable() 81 | table.fields = [] 82 | table.tableName = self.getValue(xml,"TableName") 83 | table.noOfRecords = self.getValue(xml,"NoOfRecords") 84 | table.createdTime = self.getValue(xml,"CreateUtcTime") 85 | for fieldXml in xml.getElementsByTagName("QvdFieldHeader"): 86 | field = QvdField() 87 | field.fieldName = self.getValue(fieldXml,"FieldName") 88 | field.uniqValues = self.getValue(fieldXml,"NoOfSymbols") 89 | field.memoryUsage = self.getValue(fieldXml,"Length") 90 | field.fieldType = 'Number' 91 | if self.getValue(fieldXml,"NumberFormat/Type") == "UNKNOWN": 92 | field.fieldType = "String" 93 | table.fields.append(field) 94 | viewHeader = table.tableName + '.MD' 95 | self.addLine(viewHeader) 96 | self.addLine('---') 97 | self.addLine('') 98 | line = '%s records. QVD created at %s' % (table.noOfRecords,table.createdTime) 99 | self.addLine(line) 100 | self.addLine('') 101 | self.addLine('###Fields:') 102 | self.addLine('') 103 | for field in table.fields: 104 | line = "- **%s**. Unique values: %s, Memory usage: %s" % (field.fieldName, field.uniqValues, field.memoryUsage) 105 | self.addLine(line) 106 | self.addLine('') 107 | self.addLine('####Sample load statement:') 108 | self.addLine('') 109 | self.addLine('```QlikView') 110 | self.addLine('') 111 | self.addLine('LOAD') 112 | comma = ',' 113 | for field in table.fields: 114 | if field.fieldName == table.fields[-1].fieldName: 115 | comma = '' 116 | self.addLine(' ' + field.fieldName + comma) 117 | self.addLine(' FROM [' + self.path+'] (QVD);') 118 | self.addLine('') 119 | self.addLine('```') 120 | self.closeOthers(viewHeader) 121 | def addLine(self,line): 122 | self.view.insert(self.edit, self.view.size(), line + '\n') 123 | def getValue(self,xml,tagName): 124 | nodeList = xml.getElementsByTagName(tagName) 125 | if len(nodeList) == 0: 126 | return '' 127 | tag = nodeList[0].toxml() 128 | xmlData=tag.replace('<'+tagName+'>','').replace('','') 129 | return xmlData 130 | def closeOthers(self,viewHeader): 131 | window = self.view.window() 132 | myId = self.view.id() 133 | for v in window.views(): 134 | if v.id() == myId: 135 | continue 136 | l = v.line(sublime.Region(0,0)) 137 | line = v.substr(l) 138 | if (line == viewHeader): 139 | window.focus_view(v) 140 | window.run_command('close') 141 | window.focus_view(self.view) 142 | -------------------------------------------------------------------------------- /Settings/test.json: -------------------------------------------------------------------------------- 1 | {"trigger": "Acos", "contents": "Acos(${1:x})$0"}, 2 | {"trigger": "AddMonths", "contents": "AddMonths(${1:startdate}, ${2:n})$0"}, 3 | {"trigger: "Aggr", "contents": "Aggr(${1:expression}, ${2:dimension})$0"}, 4 | {"trigger": "Alt", "contents": "Alt(${1:case1}, ${2:else})$0"}, 5 | {"trigger": "ApplyMap", "contents": "ApplyMap('${1:mapname}', ${2:expr}, ${3:defaultexpr})$0"}, 6 | {"trigger": "ARGB", "contents": "ARGB(${1:alpha}, ${2:e1}, ${3:e2}, ${4:e3})$0"}, 7 | {"trigger": "Asin", "contents": "Asin(${1:x})$0"}, 8 | {"trigger": "Atan", "contents": "Atan(${1:x})$0"}, 9 | {"trigger": "Atan2", "contents": "Atan2(${1:x,}, ${2:y})$0"}, 10 | {"trigger": "Autonumber", "contents": "Autonumber(${1:expression}, '${2:AutoID}')$0"}, 11 | {"trigger": "AutonumberHash128", "contents": "AutonumberHash128(${1:expression})$0"}, 12 | {"trigger": "AutonumberHash256", "contents": "AutonumberHash256(${1:expression})$0"}, 13 | {"trigger": "Avg", "contents": "Avg(${1:expression})$0"}, 14 | {"trigger": "Avg", "contents": "Avg(${1:expression})$0"}, 15 | {"trigger": "BitCount", "contents": "BitCount(${1:x})$0"}, 16 | {"trigger": "Ceil", "contents": "Ceil(${1:x})$0"}, 17 | {"trigger": "Chr", "contents": "Chr(${1:n})$0"}, 18 | {"trigger": "Combin", "contents": "Combin(${1:n1},${2:n2})$0"}, 19 | {"trigger": "Concat", "contents": "Concat('${1:expression}', ${2:delimiter}, ${3:sort})$0"}, 20 | {"trigger": "Cos", "contents": "Cos(${1:x})$0"}, 21 | {"trigger": "Cosh", "contents": "Cosh(${1:x})$0"}, 22 | {"trigger": "Count", "contents": "Count(${1:expression})$0"}, 23 | {"trigger": "Div", "contents": "Div(${1:x1}, ${2:x2})$0"}, 24 | {"trigger": "Even", "contents": "Even(x)(${1:x})$0"}, 25 | {"trigger": "Exp", "contents": "Exp(${1:x})$0"}, 26 | {"trigger": "Fabs", "contents": "Fabs(${1:x})$0"}, 27 | {"trigger": "Fact", "contents": "Fact(${1:x})$0"}, 28 | {"trigger": "FieldValueCount", "contents": "FieldValueCount('${1:fieldName}')$0"}, 29 | {"trigger": "FirstSortedValue", "contents": "FirstSortedValue('${1:expression}', ${2:sort-weight}, ${3:n})$0"}, 30 | {"trigger": "FirstValue", "contents": "FirstValue(${1:expression})$0"}, 31 | {"trigger": "Floor", "contents": "Floor(${1:x})$0"}, 32 | {"trigger": "FMod", "contents": "FMod(${1:x1},${2:x2})$0"}, 33 | {"trigger": "frac", "contents": "frac(${1:x})$0"}, 34 | {"trigger": "If", "contents": "If(${1:condition}, ${2:then}, ${3:else})$0"}, 35 | {"trigger": "IterNo", "contents": "IterNo()$0"}, 36 | {"trigger": "LastValue", "contents": "LastValue(${1:expression})$0"}, 37 | {"trigger": "Left", "contents": "Left(${1:string}, ${2:n})$0"}, 38 | {"trigger": "Log", "contents": "Log(${1:x})$0"}, 39 | {"trigger": "Log10", "contents": "Log10(${1:x})$0"}, 40 | {"trigger": "Lookup", "contents": "Lookup('${1:field_name}', '${2:match_field_name}', ${3:match_field_value},'${4:table_name}')$0"}, 41 | {"trigger": "Max", "contents": "Max(${1:expression})$0"}, 42 | {"trigger": "MaxString", "contents": "MaxString(${1:expression})$0"}, 43 | {"trigger": "Min", "contents": "Min(${1:expression})$0"}, 44 | {"trigger": "MinString", "contents": "MinString(${1:expression})$0"}, 45 | {"trigger": "MissingCount", "contents": "MissingCount(${1:expression})$0"}, 46 | {"trigger": "Mod", "contents": "Mod(${1:x1},${2:x2})$0"}, 47 | {"trigger": "Mode", "contents": "Mode(${1:expression})$0"}, 48 | {"trigger": "NullCount", "contents": "NullCount(${1:expression})$0"}, 49 | {"trigger": "NumericCount", "contents": "NumericCount(${1:expression})$0"}, 50 | {"trigger": "Odd", "contents": "Odd(${1:x})$0"}, 51 | {"trigger": "Only", "contents": "Only(${1:expression})$0"}, 52 | {"trigger": "Ord", "contents": "Ord(${1:str})$0"}, 53 | {"trigger": "Permut", "contents": "Permut(${1:n1},${2:n2})$0"}, 54 | {"trigger": "Pow", "contents": "Pow(${1:x}, ${2:y})$0"}, 55 | {"trigger": "RangeAvg", "contents": "RangeAvg(${1:expr1}, ${2:expr2})$0"}, 56 | {"trigger": "RangeCorrel", "contents": "RangeCorrel(${1:x-value}, ${2:y-value})$0"}, 57 | {"trigger": "RangeCount", "contents": "RangeCount(${1:expr1}, ${2:expr2})$0"}, 58 | {"trigger": "RangeFractile", "contents": "RangeFractile(${1:expr1}, ${2:expr2})$0"}, 59 | {"trigger": "RangeKurtosis", "contents": "RangeKurtosis(${1:expr1}, ${2:expr2})$0"}, 60 | {"trigger": "RangeMax", "contents": "RangeMax(${1:expr1}, ${2:expr2})$0"}, 61 | {"trigger": "RangeMaxString", "contents": "RangeMaxString(${1:expr1}, ${2:expr2})$0"}, 62 | {"trigger": "RangeMin", "contents": "RangeMin(${1:expr1}, ${2:expr2})$0"}, 63 | {"trigger": "RangeMinString", "contents": "RangeMinString(${1:expr1}, ${2:expr2})$0"}, 64 | {"trigger": "RangeMissingCount", "contents": "RangeMissingCount(${1:expr1}, ${2:expr2})$0"}, 65 | {"trigger": "RangeMode", "contents": "RangeMode(${1:expr1}, ${2:expr2})$0"}, 66 | {"trigger": "RangeNullCount", "contents": "RangeNullCount(${1:expr1}, ${2:expr2})$0"}, 67 | {"trigger": "RangeNumericCount", "contents": "RangeNumericCount(${1:expr1}, ${2:expr2})$0"}, 68 | {"trigger": "RangeOnly", "contents": "RangeOnly(${1:expr1}, ${2:expr2})$0"}, 69 | {"trigger": "RangeSkew", "contents": "RangeSkew(${1:expr1}, ${2:expr2})$0"}, 70 | {"trigger": "RangeStdev", "contents": "RangeStdev(${1:expr1}, ${2:expr2})$0"}, 71 | {"trigger": "RangeSum", "contents": "RangeSum(${1:expr1}, ${2:expr2})$0"}, 72 | {"trigger": "RangeTextCount", "contents": "RangeTextCount(${1:expr1}, ${2:expr2})$0"}, 73 | {"trigger": "RecNo", "contents": "RecNo()$0"}, 74 | {"trigger": "Right", "contents": "Right(${1:string}, ${2:n})$0"}, 75 | {"trigger": "Round", "contents": "Round(${1:x})$0"}, 76 | {"trigger": "RowNo", "contents": "RowNo()$0"}, 77 | {"trigger": "Sign", "contents": "Sign(${1:x})$0"}, 78 | {"trigger": "Sin", "contents": "Sin(${1:x})$0"}, 79 | {"trigger": "Sinh", "contents": "Sinh(${1:x})$0"}, 80 | {"trigger": "Sqr", "contents": "Sqr(${1:x})$0"}, 81 | {"trigger": "Sqrt", "contents": "Sqrt(${1:x})$0"}, 82 | {"trigger": "Sum", "contents": "Sum(${1:expression})$0"}, 83 | {"trigger": "Tan", "contents": "Tan(${1:x})$0"}, 84 | {"trigger": "Tanh", "contents": "Tanh(${1:x})$0"}, 85 | {"trigger": "TextCount", "contents": "TextCount(${1:expression})$0"}, 86 | {"trigger": "Chr", "contents": "Chr(${1:n})$0"}, 87 | {"trigger": "FieldValueCount", "contents": "FieldValueCount('${1:fieldName}')$0"}, 88 | -------------------------------------------------------------------------------- /Syntax/QlikView variables file.tmLanguage: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | fileTypes 6 | 7 | qlikview-vars 8 | 9 | name 10 | QlikView vars file 11 | patterns 12 | 13 | 14 | include 15 | #expressionName 16 | 17 | 18 | include 19 | #section 20 | 21 | 22 | include 23 | #yamlNewDocument 24 | 25 | 26 | include 27 | #expression 28 | 29 | 30 | include 31 | #expressionWithEqualSign 32 | 33 | 34 | include 35 | #expressionWithoutEqualSign 36 | 37 | 38 | include 39 | #simpleField 40 | 41 | 42 | include 43 | #def 44 | 45 | 46 | include 47 | #newtag 48 | 49 | 50 | repository 51 | 52 | def 53 | 54 | patterns 55 | 56 | 57 | begin 58 | ^#define\s+ 59 | beginCaptures 60 | 61 | 0 62 | 63 | name 64 | support.constant.qlikview-vars 65 | 66 | 67 | end 68 | ^(?=\S) 69 | endCaptures 70 | 71 | 0 72 | 73 | name 74 | support.constant.qlikview-vars 75 | 76 | 77 | name 78 | meta.expression.qlikview-vars 79 | patterns 80 | 81 | 82 | include 83 | source.qvs 84 | 85 | 86 | 87 | 88 | 89 | expression 90 | 91 | patterns 92 | 93 | 94 | begin 95 | ^(definition|macro|condition|backgroundColor|fontColor|textFormat|enableCondition|showCondition|sortBy|visualCueUpper|visualCueLower|width|symbol|thousandSymbol|millionSymbol|billionSymbol|family|type|selectorLabel|format|calendar): 96 | beginCaptures 97 | 98 | 0 99 | 100 | name 101 | support.constant.qlikview-vars 102 | 103 | 104 | end 105 | ^(?=\S) 106 | endCaptures 107 | 108 | 0 109 | 110 | name 111 | support.constant.qlikview-vars 112 | 113 | 114 | name 115 | meta.expression.qlikview-vars 116 | patterns 117 | 118 | 119 | include 120 | source.qvs 121 | 122 | 123 | 124 | 125 | 126 | expressionName 127 | 128 | patterns 129 | 130 | 131 | captures 132 | 133 | 1 134 | 135 | name 136 | support.constant.qlikview-vars 137 | 138 | 2 139 | 140 | name 141 | support.variable.qlikview-vars entity.name.type.expression.qlikview-vars 142 | 143 | 144 | match 145 | ^\s*(set|let):\s*(\w[\w_.]+)\s*$ 146 | 147 | 148 | 149 | expressionWithEqualSign 150 | 151 | patterns 152 | 153 | 154 | begin 155 | ^(label|comment):\s*(?==) 156 | beginCaptures 157 | 158 | 0 159 | 160 | name 161 | support.constant.qlikview-vars 162 | 163 | 164 | end 165 | ^(?=\S) 166 | endCaptures 167 | 168 | 0 169 | 170 | name 171 | support.constant.qlikview-vars 172 | 173 | 174 | name 175 | meta.expression.qlikview-vars 176 | patterns 177 | 178 | 179 | include 180 | source.qvs 181 | 182 | 183 | 184 | 185 | 186 | expressionWithoutEqualSign 187 | 188 | patterns 189 | 190 | 191 | begin 192 | ^(label|comment): 193 | beginCaptures 194 | 195 | 0 196 | 197 | name 198 | support.constant.qlikview-vars 199 | 200 | 201 | end 202 | ^(?=\S) 203 | endCaptures 204 | 205 | 0 206 | 207 | name 208 | support.constant.qlikview-vars 209 | 210 | 211 | name 212 | string.single.quoted.qlikview-vars 213 | 214 | 215 | 216 | newtag 217 | 218 | patterns 219 | 220 | 221 | match 222 | (^(\w)*(?!:)) 223 | name 224 | meta.newtag.qlikview-vars 225 | 226 | 227 | 228 | section 229 | 230 | patterns 231 | 232 | 233 | captures 234 | 235 | 1 236 | 237 | name 238 | comment.line.double-dash.qlikview-vars 239 | 240 | 2 241 | 242 | name 243 | support.variable.qlikview-vars meta.name.section.qlikview-vars 244 | 245 | 246 | match 247 | ^(#SECTION )\s*(.+)$ 248 | 249 | 250 | 251 | simpleField 252 | 253 | patterns 254 | 255 | 256 | match 257 | (^(\s)*(tag|description):(\s)*) 258 | name 259 | support.constant.qlikview-vars 260 | 261 | 262 | 263 | yamlNewDocument 264 | 265 | patterns 266 | 267 | 268 | match 269 | ^---$ 270 | name 271 | support.constant.qlikview-vars 272 | 273 | 274 | 275 | 276 | scopeName 277 | source.qlikview-vars 278 | uuid 279 | 6b77dc03-7003-41f4-a9f6-287849f88d5f 280 | 281 | 282 | -------------------------------------------------------------------------------- /util/qvvars.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: UTF-8 -*- 2 | import os 3 | import re 4 | import xml.etree.ElementTree as etree 5 | import csv 6 | import sys 7 | import datetime 8 | 9 | class QvVarFileReader: 10 | ALLOWED_TAGS = ('Label','Comment', 'Definition','BackgroundColor','FontColor','TextFormat', 11 | 'Tag','Separator','#define', 'Macro','Description','EnableCondition', 12 | 'ShowCondition','SortBy','VisualCueUpper','VisualCueLower') 13 | FIELDS_TO_SKIP = ('Definition','Tag','SET','LET','command','name','separator','Macro','Description') 14 | NAME_MAP = {} 15 | 16 | line_template = re.compile(r'^(?P\w+?):\s*(?P.*)$') 17 | define_template = re.compile(r'^#define\s*(?P\S+)\s+(?P.*)$') 18 | param_template = re.compile(r'^\s*\-\s*(?P.*)$') 19 | 20 | linenum = 0 21 | defs = {} 22 | macro = [] 23 | output = [] 24 | define_directives = {} 25 | moduleSettings = None 26 | def __init__(self,moduleSettings): 27 | self.linenum = 0 28 | self.defs = {} 29 | self.macro = [] 30 | self.output = [] 31 | self.define_directives = {} 32 | self.moduleSettings = moduleSettings 33 | 34 | def put_row(self, key, value, command, comment, priority): 35 | self.output.append([command.upper(), key ,value, comment, priority]) 36 | def parse_content(self,text): 37 | print('IN PARSE_CONTENT') 38 | self.NAME_MAP = {} 39 | mappings = self.moduleSettings.get('mappings',{}) 40 | for tag in self.ALLOWED_TAGS: 41 | self.NAME_MAP[tag] = mappings.get(tag,tag); 42 | self.NAME_MAP['separator'] = self.moduleSettings.get('separator','.') 43 | expression = {} 44 | defs = {} 45 | define_directives = {} 46 | self.linenum = 0 47 | self.macro = [] 48 | self.output = [] 49 | def expand_macro(): 50 | if defs.get(self.macro[0]) is None: 51 | raise SyntaxError('Parsing error: definition for macro `%s` is not found' % self.macro[0]) 52 | result = defs[self.macro[0]] 53 | i = 1 54 | while i < len(self.macro): 55 | param = self.macro[i] 56 | subs = '$%s' % str(i) 57 | if not subs in result: 58 | print('macro',self.macro) 59 | raise SyntaxError('Parsing error: definition for macro `%s` does not contain substring %s' % (self.macro[0],subs)) 60 | result = result.replace(subs,param) 61 | i = i + 1 62 | return result 63 | def init_expression(): 64 | self.macro = [] 65 | expression = {} 66 | def process_expression(exp): 67 | if exp == {}: 68 | return None 69 | if exp.get('name') is None: 70 | return'Parsing error: `name` property is absent' 71 | if exp['name'] in defs: 72 | return 'Parsing error: duplicate expression with name `%s`' % exp['name'] 73 | if exp.get('Definition') is not None and exp.get('Macro') is not None: 74 | return 'Parsing error: Expression have defined both `definition` and `macro` property. Something one must be defined' 75 | if exp.get('Definition') is None: 76 | if exp.get('Macro') is None: 77 | return 'Parsing error: Expression `%s` have not defined `definition` or `macro` property' % exp['name'] 78 | exp['Definition'] = expand_macro() 79 | local_def = exp['Definition'] 80 | for k, v in define_directives.items(): 81 | local_def = local_def.replace(k,v) 82 | exp['Definition'] = local_def 83 | defs[exp['name']] = exp['Definition'] 84 | comment = exp.get('Description') 85 | tag = exp.get('Tag') 86 | command = exp.get('command') 87 | name = exp.get('name') 88 | self.put_row(name,expression['Definition'],command, comment, tag) 89 | for key in exp.keys(): 90 | if key not in self.FIELDS_TO_SKIP: 91 | varName = '%s%s%s' % (name,self.NAME_MAP['separator'],self.NAME_MAP[key]) 92 | self.put_row(varName,expression[key],'SET', '', tag) 93 | init_expression() 94 | return None 95 | def parse_val(text): 96 | if text == None: 97 | return '' 98 | return text.strip() 99 | def parse_define_directive(line): 100 | match = self.define_template.match(line) 101 | if match is None: 102 | raise SyntaxError('Invalid define specification') 103 | m = match.groupdict() 104 | define_key = m['key'].strip() 105 | define_val = m['val'].strip() 106 | if (define_key == '' or define_val == ''): 107 | print(line) 108 | raise SyntaxError('Invalid define specification') 109 | define_directives[define_key] = define_val 110 | current_field = None 111 | for line in text.splitlines(): 112 | self.linenum = self.linenum + 1 113 | print("%s %s" % (self.linenum, line)) 114 | if (line.startswith('#define')): 115 | parse_define_directive(line) 116 | continue 117 | if line.strip()=='': 118 | continue 119 | match = self.line_template.match(line) 120 | if match is None: 121 | line = line.strip() 122 | if line == '---': 123 | error = process_expression(expression) 124 | if error is not None: 125 | raise SyntaxError(error) 126 | expression = {} 127 | continue 128 | if current_field is not None: 129 | if current_field == 'Macro': 130 | if len(self.macro) == 0: 131 | self.macro.append(expression['Macro']) 132 | param_match = self.param_template.match(line) 133 | if param_match is None: 134 | raise SyntaxError('Unexpected macro param format: "%s" for macro "%s"' % (line,self.macro[1])) 135 | else: 136 | self.macro.append(param_match.groupdict()['val'].strip()) 137 | continue 138 | else: 139 | expression[current_field] += ' ' + line 140 | continue 141 | raise SyntaxError('Unexpected format') 142 | m = match.groupdict() 143 | m['key'] = m['key'].strip() 144 | m['val'] = m['val'].strip() 145 | current_field = m['key'] 146 | if m['key'] == 'SET' or m['key'] == 'LET': 147 | expression['name'] = m['val'] 148 | expression['command'] = m['key'] 149 | elif m['key'] in self.ALLOWED_TAGS: 150 | expression[m['key']] = m['val'] 151 | else: 152 | if m['key'] == 'Macro': 153 | self.macro.append(m['val']) 154 | expression['Macro'] = self.macro 155 | else: 156 | raise SyntaxError('Unexpected QlikView expression property: "%s"' % m['key']) 157 | error = process_expression(expression) 158 | if error is not None: 159 | raise SyntaxError(error) 160 | return None 161 | class QlikViewCommandExpander: 162 | expressions = [] 163 | exp_dict = {} 164 | output = [] 165 | VAR_PATTERN = re.compile('\\$\\((?P[^=][^)]+)\\)') 166 | def __init__(self, expressions, expressionsToExpand = None): 167 | self.expressions = list(expressions) 168 | if expressionsToExpand is not None: 169 | self.expressionsToExpand = list(expressionsToExpand) 170 | for exp in self.expressions: 171 | if '.' in exp[0]: 172 | continue 173 | self.exp_dict[exp[0]] = exp[1] 174 | def expand(self): 175 | for exp in self.expressions: 176 | expanded = exp[1] 177 | for match in self.VAR_PATTERN.finditer(exp[1]): 178 | variable = match.groupdict()['key'] 179 | if variable in self.exp_dict: 180 | replace_string = self.exp_dict[variable] 181 | expanded = expanded.replace('$(%s)' % variable, replace_string) 182 | else: 183 | print('Cannot find variable: %s for expression %s' % (variable,exp[0])) 184 | self.output.append((exp[0],expanded)) 185 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | InQlik Tools for Sublime Text 2 | ============================= 3 | 4 | ##Language plugin for QlikView load scripts 5 | 6 | 7 | ###Syntax Highlighting 8 | 9 | * Both block comments (/\* \*/) and line comments (//) are supported. CTRL-/ key combination comment/uncomment code block with line comments (//) 10 | * Variables, built-in functions and so on are trying properly show their boundaries by by coloring corresponding braces. 11 | * Nesting supported for all constructs - variable as parameter of function, function within function, function within variable and so on 12 | * Conflicts between some build-in functions and keywords (IF, LEFT, RIGHT, REPLACE) are resolving by context 13 | 14 | ![Sublime Text QLikView plugin](https://monosnap.com/image/R3lNiNrl9dKs143WVCh16vh9SIWd6F.png) 15 | 16 | ####Symbols 17 | 18 | In qvs files subroutine names, variables in variable assignment commands and table identifiers marked as symbols. So `Goto Definition`, `Goto Symbol` and `Goto Symbol in Project` commands work. 19 | 20 | ####Goto file 21 | 22 | You can open any file in your project's directories from within qvs script. Default keymap is `Ctrl-F12`. That is something like [Open-include](https://github.com/SublimeText/Open-Include) but it can properly open files when part of the path presented by qlikview variables (important in Qlikview Deployment Framework environment). It is useful for opening of include scripts, qvd files (in embedded metadata viewer), 23 | and so on. 24 | 25 | ####Build System 26 | 27 | Build system allow to `batch reload` of qvw file from within of corresponding qvs script file opened in Sublime Text. 28 | To run `batch reload` Sublime Text should be able to find corresponding qvw file. 29 | By default it will look up qvw file with same name as qvs script in same directory. For example: We are editing file `c:\QvProjects\Project1\Apps\LoadData.qvs` in Sublime Text. By invoking build system (keyboard shortcuts for this command are `F7` or `Ctrl-B`) we effectively run shell command 30 | 31 | c:\Program Files\QlikView\qv.exe /R /NoData c:\QvProjects\Project1\Apps\LoadData.qvw 32 | 33 | Load script in your LoadData.qvw file should contain include directive pointing to LoadData.qvs, for example `$(must_include=LoadData.qvs)` 34 | 35 | If qvw file stays in another directory it may be indicated explicitly in first line of qvs script file by shebang-like comment string. For example, if your script file is `c:\QvProjects\Project1\Scripts\LoadData.qvs` corresponding qvd-generator `c:\QvProjects\Project1\Apps\LoadData.qvw` would be found if qvs script has first line 36 | 37 | //#!c:\QvProjects\Project1\Apps\LoadData.qvw 38 | 39 | You may also use relative path syntax and skip file part of path string if base names of qvs and qvw file are identical. So in our last example we with same effect may put in first line of qvs script 40 | 41 | //#!..\Apps 42 | 43 | `Ctrl-Shift-B` key combination opens corresponding qlikview application instead of reloading it 44 | 45 | ####QVD Viewer 46 | 47 | Clicking on QVD file in project panel instantly open view with information available in qvd header and LOAD statement for that QVD 48 | 49 | ![QVD Viewer](http://monosnap.com/image/3AcB6j9A7ktIx1FPyzgkflmWi63gh6.png) 50 | 51 | ####QVW.LOG file viewer 52 | 53 | While you editing qvs script file `Ctrl-Shift-L` key combination or menu command `Tools->Packages->InQlik-Tools->Open and refomat qvw.log file` opens qvw.log file corresponding to currently edited script. If you open qvw.log file directly `Ctrl-Shift-L` key combination or menu command just reformat log file. 54 | 55 | ![Qvw.log file viewer](http://monosnap.com/image/QGjpepWj1VNxcR5Vvx4VmACqsIB2Vh.png) 56 | 57 | - Output of qlikview script, including output from TRACE command are formatted as comment 58 | - Input commands are cleared from timestamp info 59 | - Syntax coloring is applied to input commands 60 | - Tables, variables and subroutine definitions are not added to list of defined symbols (Goto definition for example never jump to qvw.log file but to source qvs file) 61 | 62 | ##Expression Editor: Language plugin for editing QlikView expressions and variables 63 | 64 | *Article about Expression Editor with downloadable sample project placed [here](http://community.qlik.com/docs/DOC-6033) and [here](http://inqlik.github.io/2014/02/expression-editor/)* 65 | 66 | Expression editor used to write and store QlikView variables in convenient form. 67 | Expression editor use YAML like format. 68 | 69 | ####Syntax highlighting 70 | 71 | Expression editor highlight both syntax for it format and syntax of QlikView expressions edited. 72 | 73 | ![Expression editor](http://monosnap.com/image/iM1bfwD7PVuDp3eSjnKFmPjiVzI7ab.png) 74 | 75 | #####Formats of autogenerated file 76 | 77 | On every save plugin autogenerate external file in one of available formats: 78 | 79 | - Csv variable file in format used by QlikView Deployment Framework (by `LoadVariableCSV` subroutine). That is default option `QDF`. 80 | - Csv file in table format (option `CSV`), each row corresponds to whole expression and contains fields 81 | - Name 82 | - Definition 83 | - Label 84 | - Comment 85 | - Descirption 86 | - Section 87 | 88 | Current export format can be changed at menu item `Preferences->Packages Settings->InQlik-Tools->QlikView variables file settings: User` 89 | #####Mandatory tags: 90 | 91 | - LET or SET: precede name of expression. Corresponds to `Variable` field of standard QDF variable csv file 92 | - Definition: Corresponds to `VariableValue` field. 93 | 94 | #####Optional tags: 95 | 96 | - Tag (exported as `Priority` field of csv variable file) 97 | - Description (exported as `Comments` field of csv variable file) 98 | 99 | #####Optional tags exported as additional variables in `QDF` format (additional rows in csv variable file) 100 | 101 | - label 102 | - comment 103 | - backgroundColor 104 | - fontColor 105 | - nextFormat 106 | - enableCondition 107 | - showCondition 108 | - sortBy 109 | - visualCueUpper 110 | - visualCueLower 111 | - symbol 112 | - thousandSymbol 113 | - villionSymbol 114 | - billionSymbol 115 | 116 | Usual minimal expression definition looks like 117 | 118 | ``` 119 | --- 120 | set: SalesSum 121 | definition: Sum({} Amount) 122 | label: Sales 123 | comment: Sales amount 124 | ``` 125 | And corresponding auto-generated csv file would be 126 | 127 | ``` 128 | VariableName,VariableValue,Comments,Priority 129 | SET SalesSum,Sum({} Amount),, 130 | SET SalesSum.Comment,Sales amount,, 131 | SET SalesSum.Label,Sales,, 132 | ``` 133 | 134 | ####Symbols 135 | 136 | In qlikview-vars files expression names (name after `LET` or `SET` tag) marked as symbols. So `Goto Definition`, `Goto Symbol` and `Goto Symbol in Project` commands work for expressions. 137 | 138 | ####Syntax checks 139 | 140 | Minimal syntax checks provided: Checks for mandatory tags, checks for uniqueness of expression names 141 | 142 | ####Expand variables mode 143 | 144 | Given expressions 145 | 146 | ``` 147 | --- 148 | set: SalesSum 149 | definition: Sum({} Amount) 150 | label: Sales 151 | comment: Sales amount 152 | --- 153 | set: CostSum 154 | definition: Sum({} Amount) 155 | label: Cost 156 | comment: Cost amount 157 | --- 158 | set: MarginAmout 159 | definition: ($(SalesSum) - $(CostSum)) 160 | label: Margin 161 | comment: Margin amount 162 | ``` 163 | Expression `MarginAmount` defined in terms of expressions `SalesSum` and `CostSum`. Normally `MarginAmount` would be exported in same form to csv file. So in `QDF` format it would be 164 | 165 | ``` 166 | SET MarginAmout,($(SalesSum) - $(CostSum)),, 167 | ``` 168 | You can expand variables before export expressions (Menu command `Tools\Packages\InQlik-Tools\Expand variables and export file` or `Ctrl-Shift-S` key combination). So target expression in export file would be 169 | 170 | ``` 171 | SET MarginAmout,(Sum({} Amount) - Sum({} Amount)),, 172 | ``` 173 | 174 | 175 | ## Installation : 176 | 177 | ### Using [Package Control][1] (*Recommended*) 178 | 179 | For all Sublime Text 2/3 users we recommend install via [Package Control][1]. 180 | 181 | 1. [Install][2] Package Control if you haven't yet. 182 | 2. Use `Ctrl+Shift+P` then `Package Control: Install Package` 183 | 3. Look for `InQlik-Tools` and install it. 184 | 185 | ### Manual Install 186 | 187 | 1. Click the `Preferences > Browse Packages…` menu 188 | 2. Download [zip package][3] and extract its content into `InQlik-Tools` directory in this folder. (Check that README.md is immediately under `InQlik-Tools' folder not in nested folder) 189 | 3. Restart Sublime Text 190 | 191 | [Last changes](CHANGELOG.md) 192 | 193 | ### Compatibility 194 | 195 | I've switched to ST3, further development will be focused on that platform. 196 | Basic functionality (syntax highlighting for qvs files and expression files) should work on ST2. 197 | Specifically QVD viewer does not work for ST2 and disabled explicitly in it. 198 | 199 | 200 | 201 | [home]: https://github.com/inqlik/inqlik-tools 202 | [1]: https://sublime.wbond.net/ 203 | [2]: https://sublime.wbond.net/installation 204 | [3]: https://github.com/inqlik/inqlik-tools/archive/master.zip 205 | -------------------------------------------------------------------------------- /Syntax/QlikView script.YAML-tmLanguage: -------------------------------------------------------------------------------- 1 | # [PackageDev] target_format: plist, ext: tmLanguage 2 | name: QlikView script file 3 | scopeName: source.qvs 4 | fileTypes: [qvs] 5 | uuid: 6b77dc03-7003-41f4-a9f6-287849f88d4f 6 | 7 | patterns: 8 | - include: '#subroutine' 9 | 10 | - include: '#null_assignment' 11 | 12 | - include: '#assignment' 13 | 14 | - include: '#tab' 15 | 16 | - include: '#comments' 17 | 18 | - include: '#strings' 19 | 20 | - include: '#numericLiterals' 21 | 22 | - include: '#variables' 23 | 24 | - include: '#qualifiers' 25 | 26 | - include: '#functions' 27 | 28 | - include: '#keyword' 29 | 30 | - include: '#operators' 31 | 32 | - include: '#tableIdentifiers' 33 | 34 | - include: '#braces' 35 | 36 | 37 | repository: 38 | braces: 39 | patterns: 40 | - begin: \( 41 | beginCaptures: 42 | '0': {name: support.constant.qvs} 43 | end: \) 44 | endCaptures: 45 | '0': {name: support.constant.qvs} 46 | patterns: 47 | - include: '#fileQualifiers' 48 | - include: '#functions' 49 | - include: '#variables' 50 | - include: '#braces' 51 | - include: '#strings' 52 | - include: '#numericLiterals' 53 | 54 | comments: 55 | patterns: 56 | - name: comment.line.double-dash.source.qvs 57 | begin: // 58 | end: $\n 59 | - name: comment.line.rem.source.qvs 60 | begin: '^\s*REM ' 61 | end: $\n 62 | - name: comment.block.source.qvs 63 | begin: /\* 64 | end: \*/ 65 | 66 | fileQualifiers: 67 | patterns: 68 | - name: support.constant.qvs 69 | match: (?i)(?<=\s|\(|,)(codepage is \d+|ansi|oem|mac|utf8|unicode|qvd|qvx|txt|fix|dif|biff|html|delimiter 70 | is|no eof|no quotes|msq|xmlsax|xmlsimple|no labels|embedded labels|explicit 71 | labels|no eof|msq|header is (\d+ lines)?|record is (\d+ lines)?)|table is 72 | 73 | functions: 74 | patterns: 75 | - begin: (?i)(\b|=)(if|getselectedcount|aggr|left|right|acos|addmonths|addyears|age|alt|applycodepage|applymap|argb|asin|atan|atan2|attribute|author|autonumber|autonumberhash128|autonumberhash256|avg|bitcount|black|blackandschole|blue|brown|capitalize|ceil|chi2test_chi2|chi2test_df|chi2test_p|chidist|chiinv|chr|class|clientplatform|color|colormaphue|colormapjet|colormix1|colormix2|combin|computername|concat|connectstring|converttolocaltime|correl|cos|cosh|count|cyan|darkgray|date|date#|day|dayend|daylightsaving|dayname|daynumberofquarter|daynumberofyear|daystart|div|DocumentName|DocumentPath|DocumentTitle|Dual|e|Evaluate|Even|Exists|exp|fabs|Fact|False|FDIST|FieldIndex|FieldName|FieldNumber|FieldValue|FieldValueCount|FileBaseName|FileDir|FileExtension|FileName|FilePath|FileSize|FileTime|FindOneOf|FINV|FirstSortedValue|FirstValue|FirstWorkDate|Floor|fmod|Frac|Fractile|FV|GetExtendedProperty|GetFolderPath|GetObjectField|GetRegistryString|GMT|Green|Hash128|Hash160|Hash256|Hour|HSL|InDay|InDayToTime|Index|InLunarWeek|InLunarWeekToDate|InMonth|InMonths|InMonthsToDate|InMonthToDate|Input|InputAvg|InputSum|InQuarter|InQuarterToDate|Interval|Interval#|InWeek|InWeekToDate|InYear|InYearToDate|IRR|IsNull|IsNum|IsPartialReload|IsText|IterNo|KeepChar|Kurtosis|LastValue|LastWorkDate|Len|LightBlue|LightCyan|LightGray|LightGreen|LightMagenta|LightRed|LINEST_B|LINEST_DF|LINEST_F|LINEST_M|LINEST_R2|LINEST_SEB|LINEST_SEM|LINEST_SEY|LINEST_SSREG|LINEST_SSRESID|LocalTime|log|log10|Lookup|Lower|LTrim|LunarWeekEnd|LunarWeekName|LunarWeekStart|Magenta|MakeDate|MakeTime|MakeWeekDate|MapSubString|Match|Max|MaxString|Median|Mid|Min|MinString|Minute|MissingCount|MixMatch|Mod|Mode|Money|Money#|Month|MonthEnd|MonthName|MonthsEnd|MonthsName|MonthsStart|MonthStart|MsgBox|NetWorkDays|NoOfFields|NoOfReports|NoOfRows|NoOfTables|NORMDIST|NORMINV|Now|nPer|NPV|Null|NullCount|Num|Num#|NumAvg|NumCount|NumericCount|NumMax|NumMin|NumSum|Odd|Only|Ord|OSUser|Peek|Permut|Pi|Pick|Pmt|pow|Previous|PurgeChar|PV|QlikTechBlue|QlikTechGray|QlikViewVersion|QuarterEnd|QuarterName|QuarterStart|QvdCreateTime|QvdFieldName|QvdNoOfFields|QvdNoOfRecords|QvdTableName|QVUser|Rand|RangeAvg|RangeCorrel|RangeCount|RangeFractile|RangeIRR|RangeKurtosis|RangeMax|RangeMaxString|RangeMin|RangeMinString|RangeMissingCount|RangeMode|RangeNPV|RangeNullCount|RangeNumericCount|RangeOnly|RangeSkew|RangeStdev|RangeSum|RangeTextCount|RangeXIRR|RangeXNPV|Rank|Rate|RecNo|Red|ReloadTime|Repeat|Replace|ReportComment|ReportId|ReportName|ReportNumber|RGB|Round|RowNo|RTrim|Second|SetDateYear|SetDateYearMonth|Sign|sin|sinh|Skew|sqr|sqrt|Stdev|Sterr|STEYX|SubField|SubStringCount|Sum|SysColor|TableName|TableNumber|tan|tanh|TDIST|Text|TextBetween|TextCount|Time|Time#|Timestamp|Timestamp#|TimeZone|TINV|Today|Trim|True|TTest1_conf|TTest1_df|TTest1_dif|TTest1_lower|TTest1_sig|TTest1_sterr|TTest1_t|TTest1_upper|TTest1w_conf|TTest1w_df|TTest1w_dif|TTest1w_lower|TTest1w_sig|TTest1w_sterr|TTest1w_t|TTest1w_upper|TTest_conf|TTest_df|TTest_dif|TTest_lower|TTest_sig|TTest_sterr|TTest_t|TTest_upper|TTestw_conf|TTestw_df|TTestw_dif|TTestw_lower|TTestw_sig|TTestw_sterr|TTestw_t|TTestw_upper|Upper|UTC|Week|WeekDay|WeekEnd|WeekName|WeekStart|WeekYear|White|WildMatch|WildMatch5|XIRR|XNPV|Year|Year2Date|YearEnd|YearName|YearStart|YearToDate|Yellow|ZTest_conf|ZTest_dif|ZTest_lower|ZTest_sig|ZTest_sterr|ZTest_upper|ZTest_z|ZTestw_conf|ZTestw_dif|ZTestw_lower|ZTestw_sig|ZTestw_sterr|ZTestw_upper|ZTestw_z)(\s*\() 76 | beginCaptures: 77 | '2': {name: entity.other.attribute-name.qvs} 78 | '3': {name: entity.other.attribute-name.qvs} 79 | end: \) 80 | endCaptures: 81 | '0': {name: entity.other.attribute-name.qvs} 82 | patterns: 83 | - include: '#comments' 84 | - include: '#variables' 85 | - include: '#functions' 86 | - include: '#qualifiers' 87 | - include: '#numericLiterals' 88 | - include: '#operators' 89 | - include: '#strings' 90 | - include: '#braces' 91 | 92 | keyword: 93 | patterns: 94 | - match: (?i)(^|\s)\b(SUB|NOT|OR|AND|ADD|ALIAS|ASC|BINARY|BUFFER|BUNDLE|CALL|COMMENT|CONCATENATE|CONNECT|CROSSTABLE|DESC|DIRECTORY|DISCONNECT|DO|DROP|EXECUTE|EACH|END|EXIT|FIELD|FIELDS|FIRST|FOR|FORCE|GENERIC|HIERARCHY|HIERARCHYBELONGSTO|THEN|ELSEIF|ELSE|ENDIF|IMAGE_SIZE|INFO|INNER|INPUTFIELD|INTERVALMATCH|JOIN|KEEP|LOAD|LOOSEN|MAPPING|MAP|USING|NOCONCATENATE|NULLASVALUE|NULLASNULL|OUTER|QUALIFY|RENAME|RIGHT|SAMPLE|SECTION|SELECT|SEMANTIC|SEMANTIC|SLEEP|SQL|SQLCOLUMNS|SQLTABLES|SQLTYPES|STAR|STORE|ENDSUB|SWITCH|CASE|DEFAULT|ENDSWITCH|TAG|TRACE|UNLESS|UNMAP|UNQUALIFY|UNTAG|WHEN|LOOP|NEXT|SCRIPT|TABLE|TABLES|FROM|INTO|AS|INTO|TO|STEP|AUTOGENERATE|RESIDENT|WHILE|WHERE|ORDER|GROUP|BY|WITH)\b 95 | captures: 96 | '2': {name: keyword.control.qvs} 97 | - match: (?i)(^|\s)(LEFT|RIGHT|IF|REPLACE)(?!\() 98 | captures: 99 | '2': {name: keyword.control.qvs} 100 | 101 | numericLiterals: 102 | patterns: 103 | - match: (?<=\s|^|=|\(|,|>|<|\+|/|\*)(\-?\d+)(\.\d+)? 104 | captures: 105 | '1': {name: constant.numeric.qvs} 106 | '2': {name: constant.numeric.qvs} 107 | 108 | subroutine: 109 | patterns: 110 | - match: ^\s*([Ss][Uu][Bb])\s+((?:\w|\.)+) 111 | captures: 112 | '1': {name: keyword.control.qvs} 113 | '2': {name: support.class entity.name.type.subroutine.qvs} 114 | 115 | null_assignment: 116 | patterns: 117 | - match: ^\s*([LlSs][Ee][Tt])\s+(\w|\.)+\s*=\s*; 118 | captures: 119 | '1': {name: keyword.control.qvs} 120 | assignment: 121 | patterns: 122 | - match: ^\s*([LlSs][Ee][Tt])\s+((\w|\.)+)\s*= 123 | captures: 124 | '1': {name: keyword.control.qvs} 125 | '2': {name: entity.name.type.variable.qvs} 126 | 127 | tab: 128 | patterns: 129 | - match: ^(///\$tab) (.+)$ 130 | captures: 131 | '1': {name: comment.line.double-dash.source.qvs} 132 | '2': {name: support.constant.qvs meta.tab.qvs} 133 | operators: 134 | patterns: 135 | - name: keyword.control.qvs 136 | match: (&|/) 137 | - name: keyword.control.qvs 138 | match: (?i)\b(AND|OR)\b 139 | 140 | qualifiers: 141 | patterns: 142 | - match: (?i)(DISTINCT|TOTAL)(\s|<) 143 | captures: 144 | '1': {name: keyword.control.qvs} 145 | 146 | strings: 147 | patterns: 148 | - name: string.double.quoted.qvs 149 | begin: '"' 150 | end: '"' 151 | patterns: 152 | - include: '#variables' 153 | - name: string.single.quoted.qvs 154 | begin: "'" 155 | end: "'" 156 | patterns: 157 | - include: '#variables' 158 | - name: string.single.quoted.qvs 159 | begin: \[ 160 | end: \] 161 | patterns: 162 | - include: '#variables' 163 | 164 | tableIdentifiers: 165 | patterns: 166 | - name: support.constant.qvs 167 | match: ^(\s)*(\w+):(\s)*$ 168 | captures: 169 | '2': {name: support.constant.qvs entity.name.type.table.qvs} 170 | variables: 171 | patterns: 172 | - name: variable.parameter.qvs 173 | begin: \$\( 174 | beginCaptures: 175 | '0': {name: variable.parameter.qvs} 176 | end: \) 177 | endCaptures: 178 | '0': {name: variable.parameter.qvs} 179 | patterns: 180 | - include: '#functions' 181 | - include: '#variables' 182 | - include: '#numericLiterals' 183 | - include: '#braces' 184 | - name: variable.parameter.qvs 185 | match: \$\d+ 186 | -------------------------------------------------------------------------------- /qlickview_vars.py: -------------------------------------------------------------------------------- 1 | import sublime 2 | import sublime_plugin 3 | import os 4 | import re 5 | import xml.etree.ElementTree as etree 6 | import csv 7 | import sys 8 | import datetime 9 | from sublime import Region 10 | from .util.qvvars import QlikViewCommandExpander 11 | import json 12 | #from .util.qvvars import QvVarFileReader 13 | 14 | 15 | EXT_QLIKVIEW_VARS = ".qlikview-vars" 16 | class QlikviewVariableFileListener(sublime_plugin.EventListener): 17 | """Save variables in one of export formats 18 | along with current expression file in YAML like format (extentsion EXT_QLIKVIEW_VARS) 19 | 20 | Implements: 21 | on_post_save""" 22 | EXT_QLIKVIEW_VARS = ".qlikview-vars" 23 | EXT_QLIKVIEW_QDF_CSV = ".csv" 24 | EXT_QLIKVIEW_TABLE_CSV = ".table.csv" 25 | EXT_QLIKVIEW_VARS_QVS = ".vars.qvs" 26 | EXT_QLIKVIEW_VARS_JSON = ".json" 27 | modulesettings = None 28 | reader = None 29 | def is_ST3(self): 30 | ''' check if ST3 based on python version ''' 31 | version = sys.version_info 32 | if isinstance(version, tuple): 33 | version = version[0] 34 | elif getattr(version, 'major', None): 35 | version = version.major 36 | return (version >= 3) 37 | def on_post_save(self, view): 38 | fn = view.file_name() 39 | if (fn.endswith(self.EXT_QLIKVIEW_VARS)): 40 | view.window().run_command("qlikview_variables_export") 41 | 42 | class QlikviewVariablesExportCommand(sublime_plugin.WindowCommand): 43 | """Save variables in one of export formats 44 | along with current expression file in YAML like format (extentsion EXT_QLIKVIEW_VARS) 45 | 46 | Implements: 47 | on_post_save""" 48 | EXT_QLIKVIEW_VARS = ".qlikview-vars" 49 | EXT_QLIKVIEW_QDF_CSV = ".csv" 50 | EXT_QLIKVIEW_TABLE_CSV = ".table.csv" 51 | EXT_QLIKVIEW_VARS_QVS = ".vars.qvs" 52 | EXT_QLIKVIEW_VARS_JSON = ".json" 53 | modulesettings = None 54 | reader = None 55 | def is_ST3(self): 56 | ''' check if ST3 based on python version ''' 57 | version = sys.version_info 58 | if isinstance(version, tuple): 59 | version = version[0] 60 | elif getattr(version, 'major', None): 61 | version = version.major 62 | return (version >= 3) 63 | def run(self, commandVariant=None): 64 | view = self.window.active_view() 65 | fn = view.file_name() 66 | if (fn.endswith(self.EXT_QLIKVIEW_VARS)): 67 | self.modulesettings = view.settings() 68 | self.reader = QvVarFileReader(self.modulesettings) 69 | self.regenerate_expression_tab_file(view.file_name()) 70 | 71 | def regenerate_tab_file_content(self,path, onload=False): 72 | (name, ext) = os.path.splitext(os.path.basename(path)) 73 | f = None 74 | if self.is_ST3(): 75 | f = open(path, 'r', encoding="utf-8") 76 | else: 77 | f = open(path, 'rb') 78 | read = f.read() 79 | f.close() 80 | 81 | try: 82 | self.reader.parse_content(read) 83 | except Exception as e: 84 | msg = isinstance(e, SyntaxError) and str(e) or "Error parsing QlikView expression " 85 | msg += " in file `%s` line: %d" % (path, self.reader.linenum) 86 | if onload: 87 | # Sublime Text likes "hanging" itself when an error_message is pushed at initialization 88 | print("Error: " + msg) 89 | else: 90 | sublime.error_message(msg) 91 | if not isinstance(e, SyntaxError): 92 | print(e) # print the error only if it's not raised intentionally 93 | return None 94 | def regenerate_expression_tab_file(self,path, onload=False, force=False): 95 | start = datetime.datetime.utcnow() 96 | output_mode = self.modulesettings.get("output_mode","QDF") 97 | expandVariables = self.modulesettings.get("expand_variables",False); 98 | print("expandVariables: %s" % expandVariables) 99 | if output_mode == 'QDF': 100 | outExt = self.EXT_QLIKVIEW_QDF_CSV 101 | elif output_mode == 'QVS': 102 | outExt = self.EXT_QLIKVIEW_VARS_QVS 103 | elif output_mode == 'JSON': 104 | outExt = self.EXT_QLIKVIEW_VARS_JSON 105 | elif output_mode == 'CSV': 106 | outExt = self.EXT_QLIKVIEW_TABLE_CSV 107 | else: 108 | sublime.error_message('Unknown output_format %s. Known formats are QDF (Csv file QlikView Deployment framework), QVS (Plain include script), CSV (Plain tabular csv)') 109 | outPath = path.replace(self.EXT_QLIKVIEW_VARS,outExt) 110 | self.regenerate_tab_file_content(path, onload=onload) 111 | f = None 112 | if self.is_ST3(): 113 | if output_mode == 'QDF': 114 | enc = 'utf-8' 115 | elif output_mode == 'CSV': 116 | enc = 'utf-8-sig' 117 | else: 118 | enc = 'utf-8-sig' 119 | f = open(outPath, 'w', encoding=enc, newline='') 120 | else: 121 | f = open(outPath,'wb') 122 | if expandVariables == True: 123 | print('Expandind variables') 124 | expander = QlikViewVariableExpander(self.reader.output) 125 | expander.expandAll() 126 | for exp in self.reader.expressions: 127 | exp['expandedDefinition'] = expander.exp_dict[exp.get('name')] 128 | if output_mode == 'QDF': 129 | writer = csv.writer(f) 130 | writer.writerow(['VariableName','VariableValue','Comments','Priority']) 131 | for row in self.reader.output: 132 | writer.writerow(['%s %s' % (row[0] , row[1]), expander.exp_dict[row[1]] if expandVariables else row[2], row[3], row[4]]) 133 | elif output_mode == 'QVS': 134 | f.write('//////THIS IS AUTOGENERATED FILE. DO NOT EDIT IT DIRECTLY!!!!!!!!!!!!!\n\n') 135 | for row in self.reader.output: 136 | exp = expander.exp_dict[row[1]] if expandVariables else row[2] 137 | if '$(' in exp: 138 | command = 'let' 139 | exp = exp.replace("'","~~~") 140 | exp = exp.replace("$(","@(") 141 | exp = "replace(replace('%s','~~~', chr(39)), '@(', chr(36) & '(')" % exp 142 | else: 143 | command = row[0] 144 | varName = row[1] 145 | line = "%s %s = %s;\n" % (command,varName,exp) 146 | f.write(line) 147 | elif output_mode == 'JSON': 148 | jsonContent = json.dumps(self.reader.expressions, sort_keys=True, indent=4, ensure_ascii=False) 149 | f.write(jsonContent) 150 | elif output_mode == 'CSV': 151 | writer = csv.writer(f) 152 | writer.writerow(['ExpressionName','Label','Comment','Description','Section','Definition','Width']) 153 | for exp in self.reader.expressions: 154 | writer.writerow([exp.get('name'),exp.get('label'),exp.get('comment'),exp.get('description'),None,exp.get('expandedDefinition') if expandVariables else exp.get('definition'),exp.get('width')]) 155 | f.close() 156 | print(' Saving elapsed: ' + str(datetime.datetime.utcnow()-start)) 157 | 158 | 159 | class QvVarFileReader: 160 | ALLOWED_TAGS = ('label','comment', 'definition','backgroundColor','fontColor','textFormat', 161 | 'tag','separator','#define', 'macro','description','enableCondition', 162 | 'showCondition','sortBy','visualCueUpper','visualCueLower','width','symbol', 163 | 'thousandSymbol','millionSymbol','billionSymbol','family','type', 'selectorLabel', 'format', 'calendar') 164 | FIELDS_TO_SKIP = ('definition','tag','set','let','command','name','separator','macro','description', 'family', 'type', 'selectorLabel', 'format') 165 | NAME_MAP = {} 166 | 167 | line_template = re.compile(r'^(?P\w+?):\s*(?P.*)$') 168 | define_template = re.compile(r'^#define\s*(?P\S+)\s+(?P.*)$') 169 | param_template = re.compile(r'^\s*\-\s*(?P.*)$') 170 | 171 | linenum = 0 172 | defs = {} 173 | macro = [] 174 | output = [] 175 | expressions = [] 176 | define_directives = {} 177 | modulesettings = None 178 | def __init__(self,modulesettings): 179 | self.linenum = 0 180 | self.defs = {} 181 | self.macro = [] 182 | self.output = [] 183 | self.define_directives = {} 184 | self.modulesettings = modulesettings 185 | self.currentSection = '' 186 | def put_row(self, key, value, command, comment, priority): 187 | self.output.append([command.upper(), key ,value, comment, priority]) 188 | def parse_content(self,text): 189 | self.NAME_MAP = {} 190 | mappings = self.modulesettings.get('mappings',{}) 191 | for tag in self.ALLOWED_TAGS: 192 | self.NAME_MAP[tag] = mappings.get(tag,tag) 193 | self.NAME_MAP['separator'] = self.modulesettings.get('separator','.') 194 | expression = {} 195 | defs = {} 196 | surrogateNames = {} 197 | define_directives = {} 198 | self.linenum = 0 199 | self.macro = [] 200 | self.output = [] 201 | self.expressions = [] 202 | def expand_macro(): 203 | if defs.get(self.macro[0]) is None: 204 | raise SyntaxError('Parsing error: definition for macro `%s` is not found' % self.macro[0]) 205 | result = defs[self.macro[0]] 206 | i = 1 207 | while i < len(self.macro): 208 | param = self.macro[i] 209 | subs = '$%s' % str(i) 210 | if not subs in result: 211 | print('macro',self.macro) 212 | raise SyntaxError('Parsing error: definition for macro `%s` does not contain substring %s' % (self.macro[0],subs)) 213 | result = result.replace(subs,param) 214 | i = i + 1 215 | return result 216 | def init_expression(): 217 | self.macro = [] 218 | expression = {} 219 | def process_expression(exp): 220 | if exp == {}: 221 | return None 222 | expFamily = exp.get('family') 223 | expType = exp.get('type') 224 | expCalendar = exp.get('calendar') 225 | surrogateName = '' 226 | if ((expFamily is not None) and (expType is not None)): 227 | surrogateName = expFamily + '|' + expType 228 | if (expCalendar is not None): 229 | surrogateName = surrogateName + '|' + expCalendar 230 | name = exp.get('name') 231 | if (name is None): 232 | if (surrogateName == ''): 233 | return 'Parsing error: `name` property (or family, type, calendar) is absent' 234 | else: 235 | name = surrogateName 236 | exp['name'] = name 237 | if name in defs: 238 | return 'Parsing error: duplicate expression with name `%s`' % name 239 | if surrogateName in surrogateNames: 240 | return 'Parsing error: duplicate expression with surrogate name `%s`' % surrogateName 241 | if exp.get('definition') is not None and exp.get('macro') is not None: 242 | return 'Parsing error: Expression have defined both `definition` and `macro` property. Something one must be defined' 243 | if exp.get('definition') is None: 244 | if exp.get('macro') is None: 245 | return 'Parsing error: Expression `%s` have not defined `definition` or `macro` property' % exp['name'] 246 | exp['definition'] = expand_macro() 247 | local_def = exp['definition'] 248 | for k, v in define_directives.items(): 249 | local_def = local_def.replace(k,v) 250 | exp['definition'] = local_def 251 | defs[name] = exp['definition'] 252 | if (surrogateName != ''): 253 | surrogateNames[surrogateName] = 0 254 | comment = exp.get('Description') 255 | tag = exp.get('tag') 256 | if tag is None: 257 | tag = self.currentSection 258 | command = exp.get('command') 259 | if (command is None): 260 | command = name 261 | self.expressions.append(exp) 262 | self.put_row(name,expression['definition'],command, comment, tag) 263 | for key in exp.keys(): 264 | if key not in self.FIELDS_TO_SKIP: 265 | varName = '%s%s%s' % (name,self.NAME_MAP['separator'],self.NAME_MAP[key]) 266 | self.put_row(varName,expression[key],'set', '', tag) 267 | init_expression() 268 | return None 269 | def parse_val(text): 270 | if text == None: 271 | return '' 272 | return text.strip() 273 | def parse_define_directive(line): 274 | match = self.define_template.match(line) 275 | if match is None: 276 | raise SyntaxError('Invalid define specification') 277 | m = match.groupdict() 278 | define_key = m['key'].strip() 279 | define_val = m['val'].strip() 280 | if (define_key == '' or define_val == ''): 281 | print(line) 282 | raise SyntaxError('Invalid define specification') 283 | define_directives[define_key] = define_val 284 | current_field = None 285 | for line in text.splitlines(): 286 | self.linenum = self.linenum + 1 287 | #print("%s %s" % (self.linenum, line)) 288 | if (line.startswith('#define')): 289 | parse_define_directive(line) 290 | continue 291 | if line.strip()=='': 292 | continue 293 | if (line.startswith('#SECTION ')): 294 | self.currentSection = re.sub("#SECTION :?", "", line) 295 | continue 296 | match = self.line_template.match(line) 297 | if match is None: 298 | line = line.strip() 299 | if line == '---': 300 | error = process_expression(expression) 301 | if error is not None: 302 | raise SyntaxError(error) 303 | expression = {} 304 | continue 305 | if current_field is not None: 306 | if current_field == 'macro': 307 | if len(self.macro) == 0: 308 | self.macro.append(expression['macro']) 309 | param_match = self.param_template.match(line) 310 | if param_match is None: 311 | raise SyntaxError('Unexpected macro param format: "%s" for macro "%s"' % (line,self.macro[1])) 312 | else: 313 | self.macro.append(param_match.groupdict()['val'].strip()) 314 | continue 315 | else: 316 | expression[current_field] += ' ' + line 317 | continue 318 | raise SyntaxError('Unexpected format') 319 | m = match.groupdict() 320 | m['key'] = m['key'].strip() 321 | m['val'] = m['val'].strip() 322 | current_field = m['key'] 323 | if m['key'] == 'set' or m['key'] == 'let': 324 | expression['name'] = m['val'] 325 | expression['command'] = m['key'] 326 | elif m['key'] in self.ALLOWED_TAGS: 327 | expression[m['key']] = m['val'] 328 | else: 329 | if m['key'] == 'macro': 330 | self.macro.append(m['val']) 331 | expression['macro'] = self.macro 332 | else: 333 | raise SyntaxError('Unexpected QlikView expression property: "%s"' % m['key']) 334 | error = process_expression(expression) 335 | if error is not None: 336 | raise SyntaxError(error) 337 | return None 338 | class QlikViewVariableExpander: 339 | expressions = [] 340 | exp_dict = {} 341 | output = [] 342 | not_found = set() 343 | VAR_PATTERN = re.compile('\\$\\((?P[^=$][^())]+)\\)') 344 | def __init__(self, expressions): 345 | not_found = set() 346 | self.expressions = list(expressions) 347 | for exp in self.expressions: 348 | self.exp_dict[exp[1]] = exp[2] 349 | def expandAll(self): 350 | for exp in self.expressions: 351 | self.expandVariable(exp[1]) 352 | def expandVariable(self, key): 353 | varToExpand = self.exp_dict[key] 354 | needToTestFuther = False 355 | for match in self.VAR_PATTERN.finditer(varToExpand): 356 | variable = match.groupdict()['key'] 357 | if variable in self.exp_dict: 358 | replace_string = self.exp_dict[variable] 359 | varToExpand = varToExpand.replace('$(%s)' % variable, replace_string) 360 | needToTestFuther = True 361 | else: 362 | print('Cannot find variable: %s in expression %s' % (variable,key)) 363 | self.exp_dict[key] = varToExpand 364 | if needToTestFuther: 365 | self.expandVariable(key) 366 | -------------------------------------------------------------------------------- /Settings/QlikView script.sublime-completions: -------------------------------------------------------------------------------- 1 | { 2 | "scope": "source.qvs" , 3 | "completions": 4 | [ 5 | {"trigger": "Acos", "contents": "Acos(${1:x})$0"}, 6 | {"trigger": "AddMonths", "contents": "AddMonths(${1:startdate}, ${2:n})$0"}, 7 | {"trigger": "Aggr", "contents": "Aggr(${1:expression}, ${2:dimension})$0"}, 8 | {"trigger": "Alt", "contents": "Alt(${1:case1}, ${2:else})$0"}, 9 | {"trigger": "ApplyMap", "contents": "ApplyMap('${1:mapname}', ${2:expr}, ${3:defaultexpr})$0"}, 10 | {"trigger": "ARGB", "contents": "ARGB(${1:alpha}, ${2:e1}, ${3:e2}, ${4:e3})$0"}, 11 | {"trigger": "Asin", "contents": "Asin(${1:x})$0"}, 12 | {"trigger": "Atan", "contents": "Atan(${1:x})$0"}, 13 | {"trigger": "Atan2", "contents": "Atan2(${1:x,}, ${2:y})$0"}, 14 | {"trigger": "Autonumber", "contents": "Autonumber(${1:expression}, '${2:AutoID}')$0"}, 15 | {"trigger": "AutonumberHash128", "contents": "AutonumberHash128(${1:expression})$0"}, 16 | {"trigger": "AutonumberHash256", "contents": "AutonumberHash256(${1:expression})$0"}, 17 | {"trigger": "Avg", "contents": "Avg(${1:expression})$0"}, 18 | {"trigger": "Avg", "contents": "Avg(${1:expression})$0"}, 19 | {"trigger": "BitCount", "contents": "BitCount(${1:x})$0"}, 20 | {"trigger": "Ceil", "contents": "Ceil(${1:x})$0"}, 21 | {"trigger": "Chr", "contents": "Chr(${1:n})$0"}, 22 | {"trigger": "Combin", "contents": "Combin(${1:n1},${2:n2})$0"}, 23 | {"trigger": "Concat", "contents": "Concat(${1:expression}, ${2:delimiter}, ${3:sort})$0"}, 24 | {"trigger": "Cos", "contents": "Cos(${1:x})$0"}, 25 | {"trigger": "Cosh", "contents": "Cosh(${1:x})$0"}, 26 | {"trigger": "Count", "contents": "Count(${1:expression})$0"}, 27 | {"trigger": "Div", "contents": "Div(${1:x1}, ${2:x2})$0"}, 28 | {"trigger": "Even", "contents": "Even(x)(${1:x})$0"}, 29 | {"trigger": "Exp", "contents": "Exp(${1:x})$0"}, 30 | {"trigger": "Fabs", "contents": "Fabs(${1:x})$0"}, 31 | {"trigger": "Fact", "contents": "Fact(${1:x})$0"}, 32 | {"trigger": "FieldValueCount", "contents": "FieldValueCount('${1:fieldName}')$0"}, 33 | {"trigger": "FirstSortedValue", "contents": "FirstSortedValue('${1:expression}', ${2:sort-weight}, ${3:n})$0"}, 34 | {"trigger": "FirstValue", "contents": "FirstValue(${1:expression})$0"}, 35 | {"trigger": "Floor", "contents": "Floor(${1:x})$0"}, 36 | {"trigger": "FMod", "contents": "FMod(${1:x1},${2:x2})$0"}, 37 | {"trigger": "frac", "contents": "frac(${1:x})$0"}, 38 | {"trigger": "If", "contents": "If(${1:condition}, ${2:then}, ${3:else})$0"}, 39 | {"trigger": "IterNo", "contents": "IterNo()$0"}, 40 | {"trigger": "LastValue", "contents": "LastValue(${1:expression})$0"}, 41 | {"trigger": "Left", "contents": "Left(${1:string}, ${2:n})$0"}, 42 | {"trigger": "Log", "contents": "Log(${1:x})$0"}, 43 | {"trigger": "Log10", "contents": "Log10(${1:x})$0"}, 44 | {"trigger": "Lookup", "contents": "Lookup('${1:field_name}', '${2:match_field_name}', ${3:match_field_value},'${4:table_name}')$0"}, 45 | {"trigger": "Max", "contents": "Max(${1:expression})$0"}, 46 | {"trigger": "MaxString", "contents": "MaxString(${1:expression})$0"}, 47 | {"trigger": "Min", "contents": "Min(${1:expression})$0"}, 48 | {"trigger": "MinString", "contents": "MinString(${1:expression})$0"}, 49 | {"trigger": "MissingCount", "contents": "MissingCount(${1:expression})$0"}, 50 | {"trigger": "Mod", "contents": "Mod(${1:x1},${2:x2})$0"}, 51 | {"trigger": "Mode", "contents": "Mode(${1:expression})$0"}, 52 | {"trigger": "NullCount", "contents": "NullCount(${1:expression})$0"}, 53 | {"trigger": "NumericCount", "contents": "NumericCount(${1:expression})$0"}, 54 | {"trigger": "Odd", "contents": "Odd(${1:x})$0"}, 55 | {"trigger": "Only", "contents": "Only(${1:expression})$0"}, 56 | {"trigger": "Ord", "contents": "Ord(${1:str})$0"}, 57 | {"trigger": "Permut", "contents": "Permut(${1:n1},${2:n2})$0"}, 58 | {"trigger": "Pow", "contents": "Pow(${1:x}, ${2:y})$0"}, 59 | {"trigger": "RangeAvg", "contents": "RangeAvg(${1:expr1}, ${2:expr2})$0"}, 60 | {"trigger": "RangeCorrel", "contents": "RangeCorrel(${1:x-value}, ${2:y-value})$0"}, 61 | {"trigger": "RangeCount", "contents": "RangeCount(${1:expr1}, ${2:expr2})$0"}, 62 | {"trigger": "RangeFractile", "contents": "RangeFractile(${1:expr1}, ${2:expr2})$0"}, 63 | {"trigger": "RangeKurtosis", "contents": "RangeKurtosis(${1:expr1}, ${2:expr2})$0"}, 64 | {"trigger": "RangeMax", "contents": "RangeMax(${1:expr1}, ${2:expr2})$0"}, 65 | {"trigger": "RangeMaxString", "contents": "RangeMaxString(${1:expr1}, ${2:expr2})$0"}, 66 | {"trigger": "RangeMin", "contents": "RangeMin(${1:expr1}, ${2:expr2})$0"}, 67 | {"trigger": "RangeMinString", "contents": "RangeMinString(${1:expr1}, ${2:expr2})$0"}, 68 | {"trigger": "RangeMissingCount", "contents": "RangeMissingCount(${1:expr1}, ${2:expr2})$0"}, 69 | {"trigger": "RangeMode", "contents": "RangeMode(${1:expr1}, ${2:expr2})$0"}, 70 | {"trigger": "RangeNullCount", "contents": "RangeNullCount(${1:expr1}, ${2:expr2})$0"}, 71 | {"trigger": "RangeNumericCount", "contents": "RangeNumericCount(${1:expr1}, ${2:expr2})$0"}, 72 | {"trigger": "RangeOnly", "contents": "RangeOnly(${1:expr1}, ${2:expr2})$0"}, 73 | {"trigger": "RangeSkew", "contents": "RangeSkew(${1:expr1}, ${2:expr2})$0"}, 74 | {"trigger": "RangeStdev", "contents": "RangeStdev(${1:expr1}, ${2:expr2})$0"}, 75 | {"trigger": "RangeSum", "contents": "RangeSum(${1:expr1}, ${2:expr2})$0"}, 76 | {"trigger": "RangeTextCount", "contents": "RangeTextCount(${1:expr1}, ${2:expr2})$0"}, 77 | {"trigger": "RecNo", "contents": "RecNo()$0"}, 78 | {"trigger": "Right", "contents": "Right(${1:string}, ${2:n})$0"}, 79 | {"trigger": "Round", "contents": "Round(${1:x})$0"}, 80 | {"trigger": "RowNo", "contents": "RowNo()$0"}, 81 | {"trigger": "Sign", "contents": "Sign(${1:x})$0"}, 82 | {"trigger": "Sin", "contents": "Sin(${1:x})$0"}, 83 | {"trigger": "Sinh", "contents": "Sinh(${1:x})$0"}, 84 | {"trigger": "Sqr", "contents": "Sqr(${1:x})$0"}, 85 | {"trigger": "Sqrt", "contents": "Sqrt(${1:x})$0"}, 86 | {"trigger": "Sum", "contents": "Sum(${1:expression})$0"}, 87 | {"trigger": "Tan", "contents": "Tan(${1:x})$0"}, 88 | {"trigger": "Tanh", "contents": "Tanh(${1:x})$0"}, 89 | {"trigger": "TextCount", "contents": "TextCount(${1:expression})$0"}, 90 | {"trigger": "Chr", "contents": "Chr(${1:n})$0"}, 91 | {"trigger": "Len", "contents": "Len(${1:str})$0"}, 92 | {"trigger": "Mid", "contents": "Mid(${1:str},${2:startPos},${3:len} )$0"}, 93 | {"trigger": "Index", "contents": "Index(${1:str},${2:subStr})$0"}, 94 | {"trigger": "Upper", "contents": "Upper(${1:str})$0"}, 95 | {"trigger": "Lower", "contents": "Lower(${1:str})$0"}, 96 | {"trigger": "Repeat", "contents": "Repeat(${1:str}, ${2:nTimes})$0"}, 97 | {"trigger": "Ltrim", "contents": "Ltrim(${1:str})$0"}, 98 | {"trigger": "Rtrim", "contents": "Rtrim(${1:str})$0"}, 99 | {"trigger": "Subfield", "contents": "Subfield(${1:str},${2:delimiter},${3:index} )$0"}, 100 | {"trigger": "KeepChar", "contents": "KeepChar(${1:str},${2:subStr})$0"}, 101 | {"trigger": "PurgeChar", "contents": "PurgeChar(${1:str},${2:subStr})$0"}, 102 | {"trigger": "Capitalize", "contents": "Capitalize(${1:str})$0"}, 103 | {"trigger": "Evaluate", "contents": "Evaluate(${1:str})$0"}, 104 | {"trigger": "TextBetween", "contents": "TextBetween(${1:str},${2:beforeText},${3:afterText})$0"}, 105 | {"trigger": "Replace", "contents": "Replace(${1:str},${2:fromString},${3:toString})$0"}, 106 | {"trigger": "FindOneOf", "contents": "FindOneOf(${1:text},${2:characterset})$0"}, 107 | {"trigger": "Hash128", "contents": "Hash128(${1:expression},${2:moreExpressions})$0"}, 108 | {"trigger": "Hash256", "contents": "Hash256(${1:expression},${2:moreExpressions})$0"}, 109 | {"trigger": "SubstringCount", "contents": "SubstringCount(${1:text},${2:substring})$0"}, 110 | {"trigger": "ApplyCodepage", "contents": "ApplyCodepage(${1:text},${2:nCodepage})$0"}, 111 | {"trigger": "MapSubstring", "contents": "MapSubstring('${1:mapName}',${2:expr})$0"}, 112 | {"trigger": "Pick", "contents": "Pick('${1:n}',${2:expr1},${3:expr2})$0"}, 113 | {"trigger": "Match", "contents": "Match('${1:str}',${2:expr1},${3:expr2})$0"}, 114 | {"trigger": "MixMatch", "contents": "MixMatch('${1:str}',${2:expr1},${3:expr2})$0"}, 115 | {"trigger": "WildMatch", "contents": "WildMatch('${1:str}',${2:expr1},${3:expr2})$0"}, 116 | {"trigger": "Class", "contents": "Class('${1:expression}',${2:interval},${3:label_optional},${4:interval_optional})$0"}, 117 | {"trigger": "Null", "contents": "Null()$0"}, 118 | {"trigger": "IsNull", "contents": "IsNull(${1:expr})$0"}, 119 | {"trigger": "ClientPlatform", "contents": "ClientPlatform()$0"}, 120 | {"trigger": "OSuser", "contents": "OSuser()$0"}, 121 | {"trigger": "QVuser", "contents": "QVuser()$0"}, 122 | {"trigger": "ComputerName", "contents": "ComputerName()$0"}, 123 | {"trigger": "ReloadTime", "contents": "ReloadTime()$0"}, 124 | {"trigger": "GetRegistryString", "contents": "GetRegistryString('${1:path}','${2:key}')$0"}, 125 | {"trigger": "QlikViewVersion", "contents": "QlikViewVersion()$0"}, 126 | {"trigger": "DocumentName", "contents": "DocumentName()$0"}, 127 | {"trigger": "DocumentName", "contents": "DocumentName()$0"}, 128 | {"trigger": "MapSubstring", "contents": "MapSubstring('${1:mapName}',${2:expr})$0"}, 129 | {"trigger": "Second", "contents": "Second(${1:expression})$0"}, 130 | {"trigger": "Minute", "contents": "Minute(${1:expression})$0"}, 131 | {"trigger": "Hour", "contents": "Hour(${1:expression})$0"}, 132 | {"trigger": "Day", "contents": "Day(${1:date})$0"}, 133 | {"trigger": "Week", "contents": "Week(${1:date})$0"}, 134 | {"trigger": "Month", "contents": "Month(${1:date})$0"}, 135 | {"trigger": "Year", "contents": "Year(${1:date})$0"}, 136 | {"trigger": "WeekYear", "contents": "WeekYear(${1:date})$0"}, 137 | {"trigger": "WeekDay", "contents": "WeekDay(${1:date})$0"}, 138 | {"trigger": "Now", "contents": "Now(${1:1})$0"}, 139 | {"trigger": "Today", "contents": "Today(${1:2})$0"}, 140 | {"trigger": "LocalTime", "contents": "LocalTime()"}, 141 | {"trigger": "MakeDate", "contents": "MakeDate(${1:Year},${2:Month},${3:Day})$0"}, 142 | {"trigger": "MakeWeekDate", "contents": "MakeWeekDate(${1:Year},${2:Week},${3:Day})$0"}, 143 | {"trigger": "MakeTime", "contents": "MakeTime(${1:Hour},${2:Minute},${3:Second})$0"}, 144 | {"trigger": "AddMonths", "contents": "AddMonths(${1:startDate},${2:n})$0"}, 145 | {"trigger": "YearToDate", "contents": "YearToDate(${1:date},${2:yearOffset},${3:firstmonth},${4:todayDate})$0"}, 146 | {"trigger": "TimeZone", "contents": "TimeZone()$0"}, 147 | {"trigger": "UTC", "contents": "UTC()$0"}, 148 | {"trigger": "GMT", "contents": "GMT()$0"}, 149 | {"trigger": "DaylightSaving", "contents": "DaylightSaving()$0"}, 150 | {"trigger": "SetDateYearMonth", "contents": "SetDateYearMonth(${1:timestamp},${2:year},${3:month})$0"}, 151 | {"trigger": "SetDateYear", "contents": "SetDateYear(${1:timestamp},${2:year})$0"}, 152 | {"trigger": "InYear", "contents": "InYear(${1:date},${2:basedate},${3:shift},${4:first_month_of_year})$0"}, 153 | {"trigger": "InYearToDate", "contents": "InYearToDate(${1:date},${2:basedate},${3:shift},${4:first_month_of_year})$0"}, 154 | {"trigger": "InQuarter", "contents": "InQuarter(${1:date},${2:basedate},${3:shift},${4:first_month_of_year})$0"}, 155 | {"trigger": "InQuarterToDate", "contents": "InQuarterToDate(${1:date},${2:basedate},${3:shift},${4:first_month_of_year})$0"}, 156 | {"trigger": "InMonth", "contents": "InQuarter(${1:date},${2:basedate},${3:shift})$0"}, 157 | {"trigger": "InMonthToDate", "contents": "InQuarterToDate(${1:date},${2:basedate},${3:shift})$0"}, 158 | {"trigger": "InMonths", "contents": "InMonths(${1:n},${2:date},${3:basedate},${4:shift},${5:first_month_of_year})$0"}, 159 | {"trigger": "InMonthsToDate", "contents": "InMonthsToDate(${1:n},${2:date},${3:basedate},${4:shift},${5:first_month_of_year})$0"}, 160 | {"trigger": "InWeek", "contents": "InWeek(${1:date},${2:basedate},${3:shift},${4:weekstart})$0"}, 161 | {"trigger": "InWeekToDate", "contents": "InWeekToDate(${1:date},${2:basedate},${3:shift},${4:weekstart})$0"}, 162 | {"trigger": "InLunarWeek", "contents": "InLunarWeek(${1:date},${2:basedate},${3:shift},${4:weekstart})$0"}, 163 | {"trigger": "InLunarWeekToDate", "contents": "InLunarWeekToDate(${1:date},${2:basedate},${3:shift},${4:weekstart})$0"}, 164 | {"trigger": "InDay", "contents": "InDay(${1:timestamp},${2:basetimestamp},${3:shift},${4:daystart})$0"}, 165 | {"trigger": "InDayToTime", "contents": "InDayToTime(${1:timestamp},${2:basetimestamp},${3:shift},${4:daystart})$0"}, 166 | {"trigger": "YearStart", "contents": "YearStart(${1:date},${2:shift})$0"}, 167 | {"trigger": "YearEnd", "contents": "YearEnd(${1:date},${2:shift},${3:first_month_of_year})$0"}, 168 | {"trigger": "YearName", "contents": "YearName(${1:date},${2:shift},${3:first_month_of_year})$0"}, 169 | {"trigger": "QuarterStart", "contents": "QuarterStart(${1:date},${2:shift},${3:first_month_of_year})$0"}, 170 | {"trigger": "QuarterEnd", "contents": "QuarterEnd(${1:date},${2:shift},${3:first_month_of_year})$0"}, 171 | {"trigger": "QuarterName", "contents": "QuarterName(${1:date},${2:shift},${3:first_month_of_year})$0"}, 172 | {"trigger": "MonthStart", "contents": "MonthStart(${1:date},${2:shift})$0"}, 173 | {"trigger": "MonthEnd", "contents": "MonthEnd(${1:date},${2:shift})$0"}, 174 | {"trigger": "MonthName", "contents": "MonthName(${1:date},${2:shift})$0"}, 175 | {"trigger": "MonthsStart", "contents": "MonthsStart(${1:n},${2:date},${3:shift},${4:first_month_of_year})$0"}, 176 | {"trigger": "MonthsEnd", "contents": "MonthsEnd(${1:n},${2:date},${3:shift},${4:first_month_of_year})$0"}, 177 | {"trigger": "MonthsName", "contents": "MonthsName(${1:n},${2:date},${3:shift},${4:first_month_of_year})$0"}, 178 | {"trigger": "WeekStart", "contents": "WeekStart(${1:date},${2:shift},${3:weekoffset})$0"}, 179 | {"trigger": "WeekEnd", "contents": "WeekEnd(${1:date},${2:shift},${3:weekoffset})$0"}, 180 | {"trigger": "WeekName", "contents": "WeekName(${1:date},${2:shift},${3:weekoffset})$0"}, 181 | {"trigger": "LunarWeekStart", "contents": "LunarWeekStart(${1:date},${2:shift},${3:weekoffset})$0"}, 182 | {"trigger": "LunarWeekEnd", "contents": "LunarWeekEnd(${1:date},${2:shift},${3:weekoffset})$0"}, 183 | {"trigger": "LunarWeekName", "contents": "LunarWeekName(${1:date},${2:shift},${3:weekoffset})$0"}, 184 | {"trigger": "WeekStart", "contents": "WeekStart(${1:timestamp},${2:shift},${3:dayoffset})$0"}, 185 | {"trigger": "DayEnd", "contents": "DayEnd(${1:timestamp},${2:shift},${3:dayoffset})$0"}, 186 | {"trigger": "DayName", "contents": "DayName(${1:timestamp},${2:shift},${3:dayoffset})$0"}, 187 | {"trigger": "Age", "contents": "Age(${1:timestamp},${2:date_of_birth})$0"}, 188 | {"trigger": "NetWorkdays", "contents": "NetWorkdays(${1:startDate},${2:endDate},${3:holiday})$0"}, 189 | {"trigger": "FirstWorkdate", "contents": "FirstWorkdate(${1:end_date},${2:no_of_workdays},${3:holiday})$0"}, 190 | {"trigger": "LastWorkdate", "contents": "LastWorkdate(${1:start_date},${2:no_of_workdays},${3:holiday})$0"}, 191 | {"trigger": "ConvertToLocalTime", "contents": "ConvertToLocalTime(${1:timestamp},${2:place},${3:ignore_dst})$0"}, 192 | {"trigger": "DayNumberOfYear", "contents": "DayNumberOfYear(${1:date},${2:firstmonth})$0"}, 193 | {"trigger": "DayNumberOfQuarter", "contents": "DayNumberOfQuarter(${1:date},${2:firstmonth})$0"}, 194 | {"trigger": "Money", "contents": "Money(${1:expression},'${2:format_code}')$0"}, 195 | {"trigger": "Num", "contents": "Num(${1:expression})$0"}, 196 | {"trigger": "Date", "contents": "Date(${1:expression})$0"}, 197 | {"trigger": "Time", "contents": "Time(${1:expression},'${2:format_code}')$0"}, 198 | {"trigger": "Dual", "contents": "Dual('${1:text}',${2:number})$0"}, 199 | {"trigger": "Interval", "contents": "Interval(${1:expression},'${2:format_code}')$0"}, 200 | {"trigger": "Timestamp", "contents": "Timestamp(${1:expression},'${2:format_code}')$0"}, 201 | {"trigger": "Color", "contents": "Color(${1:n})$0"}, 202 | {"trigger": "RGB", "contents": "RGB(${1:rExpr},${2:gExpr},${3:bExpr})$0"}, 203 | {"trigger": "ARGB", "contents": "ARGB(${1:alfa},${2:rExpr},${3:gExpr},${4:bExpr})$0"}, 204 | {"trigger": "HSL", "contents": "HSL(${1:hue},${2:saturation},${3:luminosity})$0"}, 205 | {"trigger": "Black", "contents": "Black()$0"}, 206 | {"trigger": "DarkGray", "contents": "DarkGray()$0"}, 207 | {"trigger": "LightGray", "contents": "LightGray()$0"}, 208 | {"trigger": "White", "contents": "White()$0"}, 209 | {"trigger": "Blue", "contents": "Blue()$0"}, 210 | {"trigger": "LightBlue", "contents": "LightBlue()$0"}, 211 | {"trigger": "Green", "contents": "Green()$0"}, 212 | {"trigger": "LightGreen", "contents": "LightGreen()$0"}, 213 | {"trigger": "Cyan", "contents": "Cyan()$0"}, 214 | {"trigger": "LightCyan", "contents": "LightCyan()$0"}, 215 | {"trigger": "Red", "contents": "Red()$0"}, 216 | {"trigger": "Magenta", "contents": "Magenta()$0"}, 217 | {"trigger": "LightMagenta", "contents": "LightMagenta()$0"}, 218 | {"trigger": "Brown", "contents": "Brown()$0"}, 219 | {"trigger": "Yellow", "contents": "Yellow()$0"}, 220 | {"trigger": "QliktechBlue", "contents": "QliktechBlue()$0"}, 221 | {"trigger": "QliktechGray", "contents": "QliktechGray()$0"}, 222 | {"trigger": "Colormix1", "contents": "HSL(${1:Value},${2:ColorZero},${3:ColorOne})$0"}, 223 | {"trigger": "Colormix2", "contents": "HSL(${1:Value},${2:ColorMinusOne},${3:ColorOne},${4:ColorZeroOptional})$0"}, 224 | {"trigger": "SysColor", "contents": "SysColor(${1:n})$0"}, 225 | {"trigger": "FieldValueCount", "contents": "FieldValueCount('${1:fieldName}')$0"} 226 | ] 227 | } -------------------------------------------------------------------------------- /Syntax/QlikView script.tmLanguage: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | fileTypes 6 | 7 | qvs 8 | 9 | name 10 | QlikView script file 11 | patterns 12 | 13 | 14 | include 15 | #subroutine 16 | 17 | 18 | include 19 | #null_assignment 20 | 21 | 22 | include 23 | #assignment 24 | 25 | 26 | include 27 | #tab 28 | 29 | 30 | include 31 | #comments 32 | 33 | 34 | include 35 | #strings 36 | 37 | 38 | include 39 | #numericLiterals 40 | 41 | 42 | include 43 | #variables 44 | 45 | 46 | include 47 | #qualifiers 48 | 49 | 50 | include 51 | #functions 52 | 53 | 54 | include 55 | #keyword 56 | 57 | 58 | include 59 | #operators 60 | 61 | 62 | include 63 | #tableIdentifiers 64 | 65 | 66 | include 67 | #braces 68 | 69 | 70 | repository 71 | 72 | assignment 73 | 74 | patterns 75 | 76 | 77 | captures 78 | 79 | 1 80 | 81 | name 82 | keyword.control.qvs 83 | 84 | 2 85 | 86 | name 87 | entity.name.type.variable.qvs 88 | 89 | 90 | match 91 | ^\s*([LlSs][Ee][Tt])\s+((\w|\.)+)\s*= 92 | 93 | 94 | 95 | braces 96 | 97 | patterns 98 | 99 | 100 | begin 101 | \( 102 | beginCaptures 103 | 104 | 0 105 | 106 | name 107 | support.constant.qvs 108 | 109 | 110 | end 111 | \) 112 | endCaptures 113 | 114 | 0 115 | 116 | name 117 | support.constant.qvs 118 | 119 | 120 | patterns 121 | 122 | 123 | include 124 | #fileQualifiers 125 | 126 | 127 | include 128 | #functions 129 | 130 | 131 | include 132 | #variables 133 | 134 | 135 | include 136 | #braces 137 | 138 | 139 | include 140 | #strings 141 | 142 | 143 | include 144 | #numericLiterals 145 | 146 | 147 | 148 | 149 | 150 | comments 151 | 152 | patterns 153 | 154 | 155 | begin 156 | // 157 | end 158 | $\n 159 | name 160 | comment.line.double-dash.source.qvs 161 | 162 | 163 | begin 164 | ^\s*REM 165 | end 166 | $\n 167 | name 168 | comment.line.rem.source.qvs 169 | 170 | 171 | begin 172 | /\* 173 | end 174 | \*/ 175 | name 176 | comment.block.source.qvs 177 | 178 | 179 | 180 | fileQualifiers 181 | 182 | patterns 183 | 184 | 185 | match 186 | (?i)(?<=\s|\(|,)(codepage is \d+|ansi|oem|mac|utf8|unicode|qvd|qvx|txt|fix|dif|biff|html|delimiter is|no eof|no quotes|msq|xmlsax|xmlsimple|no labels|embedded labels|explicit labels|no eof|msq|header is (\d+ lines)?|record is (\d+ lines)?)|table is 187 | name 188 | support.constant.qvs 189 | 190 | 191 | 192 | functions 193 | 194 | patterns 195 | 196 | 197 | begin 198 | (?i)(\b|=)(if|getselectedcount|aggr|left|right|acos|addmonths|addyears|age|alt|applycodepage|applymap|argb|asin|atan|atan2|attribute|author|autonumber|autonumberhash128|autonumberhash256|avg|bitcount|black|blackandschole|blue|brown|capitalize|ceil|chi2test_chi2|chi2test_df|chi2test_p|chidist|chiinv|chr|class|clientplatform|color|colormaphue|colormapjet|colormix1|colormix2|combin|computername|concat|connectstring|converttolocaltime|correl|cos|cosh|count|cyan|darkgray|date|date#|day|dayend|daylightsaving|dayname|daynumberofquarter|daynumberofyear|daystart|div|DocumentName|DocumentPath|DocumentTitle|Dual|e|Evaluate|Even|Exists|exp|fabs|Fact|False|FDIST|FieldIndex|FieldName|FieldNumber|FieldValue|FieldValueCount|FileBaseName|FileDir|FileExtension|FileName|FilePath|FileSize|FileTime|FindOneOf|FINV|FirstSortedValue|FirstValue|FirstWorkDate|Floor|fmod|Frac|Fractile|FV|GetExtendedProperty|GetFolderPath|GetObjectField|GetRegistryString|GMT|Green|Hash128|Hash160|Hash256|Hour|HSL|InDay|InDayToTime|Index|InLunarWeek|InLunarWeekToDate|InMonth|InMonths|InMonthsToDate|InMonthToDate|Input|InputAvg|InputSum|InQuarter|InQuarterToDate|Interval|Interval#|InWeek|InWeekToDate|InYear|InYearToDate|IRR|IsNull|IsNum|IsPartialReload|IsText|IterNo|KeepChar|Kurtosis|LastValue|LastWorkDate|Len|LightBlue|LightCyan|LightGray|LightGreen|LightMagenta|LightRed|LINEST_B|LINEST_DF|LINEST_F|LINEST_M|LINEST_R2|LINEST_SEB|LINEST_SEM|LINEST_SEY|LINEST_SSREG|LINEST_SSRESID|LocalTime|log|log10|Lookup|Lower|LTrim|LunarWeekEnd|LunarWeekName|LunarWeekStart|Magenta|MakeDate|MakeTime|MakeWeekDate|MapSubString|Match|Max|MaxString|Median|Mid|Min|MinString|Minute|MissingCount|MixMatch|Mod|Mode|Money|Money#|Month|MonthEnd|MonthName|MonthsEnd|MonthsName|MonthsStart|MonthStart|MsgBox|NetWorkDays|NoOfFields|NoOfReports|NoOfRows|NoOfTables|NORMDIST|NORMINV|Now|nPer|NPV|Null|NullCount|Num|Num#|NumAvg|NumCount|NumericCount|NumMax|NumMin|NumSum|Odd|Only|Ord|OSUser|Peek|Permut|Pi|Pick|Pmt|pow|Previous|PurgeChar|PV|QlikTechBlue|QlikTechGray|QlikViewVersion|QuarterEnd|QuarterName|QuarterStart|QvdCreateTime|QvdFieldName|QvdNoOfFields|QvdNoOfRecords|QvdTableName|QVUser|Rand|RangeAvg|RangeCorrel|RangeCount|RangeFractile|RangeIRR|RangeKurtosis|RangeMax|RangeMaxString|RangeMin|RangeMinString|RangeMissingCount|RangeMode|RangeNPV|RangeNullCount|RangeNumericCount|RangeOnly|RangeSkew|RangeStdev|RangeSum|RangeTextCount|RangeXIRR|RangeXNPV|Rank|Rate|RecNo|Red|ReloadTime|Repeat|Replace|ReportComment|ReportId|ReportName|ReportNumber|RGB|Round|RowNo|RTrim|Second|SetDateYear|SetDateYearMonth|Sign|sin|sinh|Skew|sqr|sqrt|Stdev|Sterr|STEYX|SubField|SubStringCount|Sum|SysColor|TableName|TableNumber|tan|tanh|TDIST|Text|TextBetween|TextCount|Time|Time#|Timestamp|Timestamp#|TimeZone|TINV|Today|Trim|True|TTest1_conf|TTest1_df|TTest1_dif|TTest1_lower|TTest1_sig|TTest1_sterr|TTest1_t|TTest1_upper|TTest1w_conf|TTest1w_df|TTest1w_dif|TTest1w_lower|TTest1w_sig|TTest1w_sterr|TTest1w_t|TTest1w_upper|TTest_conf|TTest_df|TTest_dif|TTest_lower|TTest_sig|TTest_sterr|TTest_t|TTest_upper|TTestw_conf|TTestw_df|TTestw_dif|TTestw_lower|TTestw_sig|TTestw_sterr|TTestw_t|TTestw_upper|Upper|UTC|Week|WeekDay|WeekEnd|WeekName|WeekStart|WeekYear|White|WildMatch|WildMatch5|XIRR|XNPV|Year|Year2Date|YearEnd|YearName|YearStart|YearToDate|Yellow|ZTest_conf|ZTest_dif|ZTest_lower|ZTest_sig|ZTest_sterr|ZTest_upper|ZTest_z|ZTestw_conf|ZTestw_dif|ZTestw_lower|ZTestw_sig|ZTestw_sterr|ZTestw_upper|ZTestw_z)(\s*\() 199 | beginCaptures 200 | 201 | 2 202 | 203 | name 204 | entity.other.attribute-name.qvs 205 | 206 | 3 207 | 208 | name 209 | entity.other.attribute-name.qvs 210 | 211 | 212 | end 213 | \) 214 | endCaptures 215 | 216 | 0 217 | 218 | name 219 | entity.other.attribute-name.qvs 220 | 221 | 222 | patterns 223 | 224 | 225 | include 226 | #comments 227 | 228 | 229 | include 230 | #variables 231 | 232 | 233 | include 234 | #functions 235 | 236 | 237 | include 238 | #qualifiers 239 | 240 | 241 | include 242 | #numericLiterals 243 | 244 | 245 | include 246 | #operators 247 | 248 | 249 | include 250 | #strings 251 | 252 | 253 | include 254 | #braces 255 | 256 | 257 | 258 | 259 | 260 | keyword 261 | 262 | patterns 263 | 264 | 265 | captures 266 | 267 | 2 268 | 269 | name 270 | keyword.control.qvs 271 | 272 | 273 | match 274 | (?i)(^|\s)\b(SUB|NOT|OR|AND|ADD|ALIAS|ASC|BINARY|BUFFER|BUNDLE|CALL|COMMENT|CONCATENATE|CONNECT|CROSSTABLE|DESC|DIRECTORY|DISCONNECT|DO|DROP|EXECUTE|EACH|END|EXIT|FIELD|FIELDS|FIRST|FOR|FORCE|GENERIC|HIERARCHY|HIERARCHYBELONGSTO|THEN|ELSEIF|ELSE|ENDIF|IMAGE_SIZE|INFO|INNER|INPUTFIELD|INTERVALMATCH|JOIN|KEEP|LOAD|LOOSEN|MAPPING|MAP|USING|NOCONCATENATE|NULLASVALUE|NULLASNULL|OUTER|QUALIFY|RENAME|RIGHT|SAMPLE|SECTION|SELECT|SEMANTIC|SEMANTIC|SLEEP|SQL|SQLCOLUMNS|SQLTABLES|SQLTYPES|STAR|STORE|ENDSUB|SWITCH|CASE|DEFAULT|ENDSWITCH|TAG|TRACE|UNLESS|UNMAP|UNQUALIFY|UNTAG|WHEN|LOOP|NEXT|SCRIPT|TABLE|TABLES|FROM|INTO|AS|INTO|TO|STEP|AUTOGENERATE|RESIDENT|WHILE|WHERE|ORDER|GROUP|BY|WITH)\b 275 | 276 | 277 | captures 278 | 279 | 2 280 | 281 | name 282 | keyword.control.qvs 283 | 284 | 285 | match 286 | (?i)(^|\s)(LEFT|RIGHT|IF|REPLACE)(?!\() 287 | 288 | 289 | 290 | null_assignment 291 | 292 | patterns 293 | 294 | 295 | captures 296 | 297 | 1 298 | 299 | name 300 | keyword.control.qvs 301 | 302 | 303 | match 304 | ^\s*([LlSs][Ee][Tt])\s+(\w|\.)+\s*=\s*; 305 | 306 | 307 | 308 | numericLiterals 309 | 310 | patterns 311 | 312 | 313 | captures 314 | 315 | 1 316 | 317 | name 318 | constant.numeric.qvs 319 | 320 | 2 321 | 322 | name 323 | constant.numeric.qvs 324 | 325 | 326 | match 327 | (?<=\s|^|=|\(|,|>|<|\+|/|\*)(\-?\d+)(\.\d+)? 328 | 329 | 330 | 331 | operators 332 | 333 | patterns 334 | 335 | 336 | match 337 | (&|/) 338 | name 339 | keyword.control.qvs 340 | 341 | 342 | match 343 | (?i)\b(AND|OR)\b 344 | name 345 | keyword.control.qvs 346 | 347 | 348 | 349 | qualifiers 350 | 351 | patterns 352 | 353 | 354 | captures 355 | 356 | 1 357 | 358 | name 359 | keyword.control.qvs 360 | 361 | 362 | match 363 | (?i)(DISTINCT|TOTAL)(\s|<) 364 | 365 | 366 | 367 | strings 368 | 369 | patterns 370 | 371 | 372 | begin 373 | " 374 | end 375 | " 376 | name 377 | string.double.quoted.qvs 378 | patterns 379 | 380 | 381 | include 382 | #variables 383 | 384 | 385 | 386 | 387 | begin 388 | ' 389 | end 390 | ' 391 | name 392 | string.single.quoted.qvs 393 | patterns 394 | 395 | 396 | include 397 | #variables 398 | 399 | 400 | 401 | 402 | begin 403 | \[ 404 | end 405 | \] 406 | name 407 | string.single.quoted.qvs 408 | patterns 409 | 410 | 411 | include 412 | #variables 413 | 414 | 415 | 416 | 417 | 418 | subroutine 419 | 420 | patterns 421 | 422 | 423 | captures 424 | 425 | 1 426 | 427 | name 428 | keyword.control.qvs 429 | 430 | 2 431 | 432 | name 433 | support.class entity.name.type.subroutine.qvs 434 | 435 | 436 | match 437 | ^\s*([Ss][Uu][Bb])\s+((?:\w|\.)+) 438 | 439 | 440 | 441 | tab 442 | 443 | patterns 444 | 445 | 446 | captures 447 | 448 | 1 449 | 450 | name 451 | comment.line.double-dash.source.qvs 452 | 453 | 2 454 | 455 | name 456 | support.constant.qvs meta.tab.qvs 457 | 458 | 459 | match 460 | ^(///\$tab) (.+)$ 461 | 462 | 463 | 464 | tableIdentifiers 465 | 466 | patterns 467 | 468 | 469 | captures 470 | 471 | 2 472 | 473 | name 474 | support.constant.qvs entity.name.type.table.qvs 475 | 476 | 477 | match 478 | ^(\s)*(\w+):(\s)*$ 479 | name 480 | support.constant.qvs 481 | 482 | 483 | 484 | variables 485 | 486 | patterns 487 | 488 | 489 | begin 490 | \$\( 491 | beginCaptures 492 | 493 | 0 494 | 495 | name 496 | variable.parameter.qvs 497 | 498 | 499 | end 500 | \) 501 | endCaptures 502 | 503 | 0 504 | 505 | name 506 | variable.parameter.qvs 507 | 508 | 509 | name 510 | variable.parameter.qvs 511 | patterns 512 | 513 | 514 | include 515 | #functions 516 | 517 | 518 | include 519 | #variables 520 | 521 | 522 | include 523 | #numericLiterals 524 | 525 | 526 | include 527 | #braces 528 | 529 | 530 | 531 | 532 | match 533 | \$\d+ 534 | name 535 | variable.parameter.qvs 536 | 537 | 538 | 539 | 540 | scopeName 541 | source.qvs 542 | uuid 543 | 6b77dc03-7003-41f4-a9f6-287849f88d4f 544 | 545 | 546 | --------------------------------------------------------------------------------