forked from peterk87/sublime-nextflow
-
Notifications
You must be signed in to change notification settings - Fork 2
/
params_completions.py
129 lines (116 loc) · 4.13 KB
/
params_completions.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import re
from typing import Iterator
import sys
from pathlib import Path
import json
import sublime
import sublime_plugin
regex_params = re.compile(
r'\nparams\s*\{\n\s*(.*)',
flags=re.MULTILINE | re.UNICODE | re.DOTALL
)
regex_param_val = re.compile(r'^(\w+)\s*=\s*(\S+).*$')
def params_list(nf_config):
params_match = regex_params.search(nf_config)
if params_match:
brackets = 1
regex_param_val = re.compile(r'^(\w+)\s*=\s*(\S+).*$')
param_val = []
for l in params_match.group(1).split('\n'):
l = l.strip()
if not l or l.startswith('//'):
continue
m = regex_param_val.match(l)
if m:
param_val.append(m.groups())
elif l.startswith('}'):
brackets -= 1
else:
print("NOMATCH", l)
if brackets == 0:
break
return param_val
else:
return None
def get_param_info(nf_schema: dict, param: str) -> dict:
for defn in nf_schema['definitions'].values():
try:
return defn['properties'][param]
except KeyError:
continue
return {}
def format_param_info(param_info: dict) -> str:
param_type = param_info.get('type', 'string')
default = param_info.get('default', '?')
description = param_info.get('description', 'N/A')
out = ''
out += f'<p>{description}</p>'
out += (
f'<p><b>Type:</b> <code>{param_type}</code></p>'
f'<p><b>Default:</b> <code>{default}</code></p>'
)
if 'pattern' in param_info:
out += f'<p><b>Pattern:</b> <code>{param_info["pattern"]}</code><p>'
if 'enum' in param_info:
enum = param_info['enum']
if isinstance(enum, list):
enum = ', '.join(sorted(enum))
out += f'<p><b>Enum:</b> {enum}</p>'
if 'help_text' in param_info:
out += f'<p><i>{param_info["help_text"]}</i><p>'
return out
class NextflowParamsEventListener(sublime_plugin.EventListener):
def on_query_completions(self, view, prefix, locations):
if view.syntax().name != 'Nextflow':
return
if len(locations) > 1:
return
point = locations[0]
if not view.score_selector(point-1, 'source.nextflow punctuation.params.dot'):
return
folders = view.window().folders()
if not folders:
return
root_dir = Path(folders[0])
nf_config_path = root_dir / 'nextflow.config'
if not nf_config_path.exists():
print(f'Cannot get params completions. "{nf_config_path}" does not exist!')
return None
with open(nf_config_path) as f:
nf_config = f.read()
params_values = params_list(nf_config)
if params_values:
flags = sublime.INHIBIT_REORDER | sublime.INHIBIT_WORD_COMPLETIONS
completions = sublime.CompletionList(
completions=[
sublime.CompletionItem(
trigger=x,
annotation=f'default: {y}',
details=f'<i>nextflow.config</i>: <code>params.<b>{x}</b> = {y}</code>'
) for x,y in params_values
],
flags=flags
)
return completions
def on_selection_modified_async(self, view):
if view.syntax().name != 'Nextflow':
return
if len(view.selection) > 1:
return
region = view.selection[0]
point = region.a
if not view.score_selector(point, 'source.nextflow entity.name.parameter.nextflow'):
return
folders = view.window().folders()
if not folders:
return
root_dir = Path(folders[0])
nf_schema_path = root_dir / 'nextflow_schema.json'
if not nf_schema_path.exists():
return
scope_region = view.extract_scope(point)
param_text = view.substr(scope_region)
with open(nf_schema_path) as f:
nf_schema = json.load(f)
param_info = get_param_info(nf_schema, param_text)
view.show_popup(format_param_info(param_info))