Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
erp5
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Labels
Merge Requests
7
Merge Requests
7
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Jobs
Commits
Open sidebar
Jérome Perrin
erp5
Commits
18e165d6
Commit
18e165d6
authored
Oct 28, 2019
by
Jérome Perrin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
monaco: integrate jedi for completions
parent
1fc7b223
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
356 additions
and
0 deletions
+356
-0
bt5/erp5_monaco_editor/ExtensionTemplateItem/portal_components/extension.erp5.Jedi.py
...sionTemplateItem/portal_components/extension.erp5.Jedi.py
+164
-0
bt5/erp5_monaco_editor/ExtensionTemplateItem/portal_components/extension.erp5.Jedi.xml
...ionTemplateItem/portal_components/extension.erp5.Jedi.xml
+123
-0
bt5/erp5_monaco_editor/SkinTemplateItem/portal_skins/erp5_monaco_editor/ERP5Site_getPythonSourceCodeCompletionList.xml
...aco_editor/ERP5Site_getPythonSourceCodeCompletionList.xml
+28
-0
bt5/erp5_monaco_editor/SkinTemplateItem/portal_skins/erp5_monaco_editor/monaco_editor_support.zpt
...portal_skins/erp5_monaco_editor/monaco_editor_support.zpt
+40
-0
bt5/erp5_monaco_editor/bt/template_extension_id_list
bt5/erp5_monaco_editor/bt/template_extension_id_list
+1
-0
No files found.
bt5/erp5_monaco_editor/ExtensionTemplateItem/portal_components/extension.erp5.Jedi.py
0 → 100644
View file @
18e165d6
import
json
import
sys
from
threading
import
RLock
import
logging
import
jedi
# increase default cache duration
jedi
.
settings
.
call_signatures_validity
=
30
# XXX needed ?
# map jedi type to the name of monaco.languages.CompletionItemKind
# This mapping and the functions below (_format_completion, _label, _detail, _sort_text )
# are copied/inspired by jedi integration in python-language-server
# https://github.com/palantir/python-language-server/blob/19b10c47988df504872a4fe07c421b0555b3127e/pyls/plugins/jedi_completion.py
# python-language-server is Copyright 2017 Palantir Technologies, Inc. and distributed under MIT License.
# https://github.com/palantir/python-language-server/blob/19b10c47988df504872a4fe07c421b0555b3127e/LICENSE
_TYPE_MAP
=
{
'none'
:
'Value'
,
'type'
:
'Class'
,
'tuple'
:
'Class'
,
'dict'
:
'Class'
,
'dictionary'
:
'Class'
,
'function'
:
'Function'
,
'lambda'
:
'Function'
,
'generator'
:
'Function'
,
'class'
:
'Class'
,
'instance'
:
'Reference'
,
'method'
:
'Method'
,
'builtin'
:
'Class'
,
'builtinfunction'
:
'Function'
,
'module'
:
'Module'
,
'file'
:
'File'
,
'xrange'
:
'Class'
,
'slice'
:
'Class'
,
'traceback'
:
'Class'
,
'frame'
:
'Class'
,
'buffer'
:
'Class'
,
'dictproxy'
:
'Class'
,
'funcdef'
:
'Function'
,
'property'
:
'Property'
,
'import'
:
'Module'
,
'keyword'
:
'Keyword'
,
'constant'
:
'Variable'
,
'variable'
:
'Variable'
,
'value'
:
'Value'
,
'param'
:
'Variable'
,
'statement'
:
'Keyword'
,
}
def
_label
(
definition
):
if
definition
.
type
in
(
'function'
,
'method'
)
and
hasattr
(
definition
,
'params'
):
params
=
', '
.
join
([
param
.
name
for
param
in
definition
.
params
])
return
'{}({})'
.
format
(
definition
.
name
,
params
)
return
definition
.
name
def
_detail
(
definition
):
try
:
return
definition
.
parent
().
full_name
or
''
except
AttributeError
:
return
definition
.
full_name
or
''
def
_sort_text
(
definition
):
""" Ensure builtins appear at the bottom.
Description is of format <type>: <module>.<item>
"""
# If its 'hidden', put it next last
prefix
=
'z{}'
if
definition
.
name
.
startswith
(
'_'
)
else
'a{}'
return
prefix
.
format
(
definition
.
name
)
def
_format_docstring
(
docstring
):
return
docstring
def
_format_completion
(
d
):
completion
=
{
'label'
:
_label
(
d
),
'_kind'
:
_TYPE_MAP
.
get
(
d
.
type
),
'detail'
:
_detail
(
d
),
'documentation'
:
_format_docstring
(
d
.
docstring
()),
'sortText'
:
_sort_text
(
d
),
'insertText'
:
d
.
name
}
return
completion
def
_guessType
(
name
):
"""guess the type of python script parameters based on naming conventions.
TODO: depend on the script name, for Person_getSomething, context is a erp5.portal_type.Person
"""
name
=
name
.
split
(
'='
)[
0
]
# support also assigned names ( like REQUEST=None in params)
if
name
in
(
'context'
,
'container'
,):
return
'Products.ERP5Type.Core.Folder.Folder'
if
name
==
'script'
:
return
'Products.PythonScripts.PythonScript'
if
name
==
'REQUEST'
:
return
'ZPublisher.HTTPRequest.HTTPRequest'
if
name
==
'RESPONSE'
:
return
'ZPublisher.HTTPRequest.HTTPResponse'
return
'str'
# assume string by default
#jedi_lock = RLock() # jedi is not thread safe
import
Products.ERP5Type.Utils
logger
=
logging
.
getLogger
(
"erp5.extension.Jedi"
)
# Jedi is not thread safe
jedi_lock
=
getattr
(
Products
.
ERP5Type
.
Utils
,
'jedi_lock'
,
None
)
if
jedi_lock
is
None
:
logger
.
critical
(
"There was no lock, making a new one"
)
jedi_lock
=
Products
.
ERP5Type
.
Utils
.
jedi_lock
=
RLock
()
logger
.
info
(
"Jedi locking with %s (%s)"
,
jedi_lock
,
id
(
jedi_lock
))
def
ERP5Site_getPythonSourceCodeCompletionList
(
self
,
data
,
REQUEST
=
None
):
"""Complete source code with jedi.
"""
logger
.
info
(
'jedi get lock %s (%s)'
,
jedi_lock
,
id
(
jedi_lock
))
with
jedi_lock
:
if
isinstance
(
data
,
basestring
):
data
=
json
.
loads
(
data
)
# data contains the code, the bound names and the script params. From this
# we reconstruct a function that can be checked
def
indent
(
text
):
return
''
.
join
((
" "
+
line
)
for
line
in
text
.
splitlines
(
True
))
is_python_script
=
'bound_names'
in
data
if
is_python_script
:
signature_parts
=
data
[
'bound_names'
]
if
data
[
'params'
]:
signature_parts
+=
[
data
[
'params'
]]
signature
=
", "
.
join
(
signature_parts
)
imports
=
"import Products.ERP5Type.Core.Folder; import ZPublisher.HTTPRequest; import Products.PythonScripts"
function_name
=
"function_name"
type_annotation
=
" # type: (%s) -> None"
%
(
', '
.
join
([
_guessType
(
part
)
for
part
in
signature_parts
]))
body
=
"%s
\
n
def %s(%s):
\
n
%s
\
n
%s"
%
(
imports
,
function_name
,
signature
,
type_annotation
,
indent
(
data
[
'code'
])
or
" pass"
)
data
[
'position'
][
'line'
]
=
data
[
'position'
][
'line'
]
+
3
# imports, fonction header + type annotation line
data
[
'position'
][
'column'
]
=
data
[
'position'
][
'column'
]
+
2
# " " from indent(text)
else
:
body
=
data
[
'code'
]
logger
.
info
(
"jedi getting completions...."
)
script
=
jedi
.
Script
(
body
,
data
[
'position'
][
'line'
],
data
[
'position'
][
'column'
]
-
1
,
'example.py'
,
# TODO name
sys_path
=
list
(
sys
.
path
),
)
completions
=
[
_format_completion
(
c
)
for
c
in
script
.
completions
()]
logger
.
info
(
"jedi got completion"
)
if
REQUEST
is
not
None
:
REQUEST
.
RESPONSE
.
setHeader
(
'content-type'
,
'application/json'
)
return
json
.
dumps
(
completions
)
bt5/erp5_monaco_editor/ExtensionTemplateItem/portal_components/extension.erp5.Jedi.xml
0 → 100644
View file @
18e165d6
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"Extension Component"
module=
"erp5.portal_type"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
_recorded_property_dict
</string>
</key>
<value>
<persistent>
<string
encoding=
"base64"
>
AAAAAAAAAAI=
</string>
</persistent>
</value>
</item>
<item>
<key>
<string>
default_reference
</string>
</key>
<value>
<string>
Jedi
</string>
</value>
</item>
<item>
<key>
<string>
description
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
extension.erp5.Jedi
</string>
</value>
</item>
<item>
<key>
<string>
portal_type
</string>
</key>
<value>
<string>
Extension Component
</string>
</value>
</item>
<item>
<key>
<string>
sid
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
text_content_error_message
</string>
</key>
<value>
<tuple/>
</value>
</item>
<item>
<key>
<string>
text_content_warning_message
</string>
</key>
<value>
<tuple/>
</value>
</item>
<item>
<key>
<string>
version
</string>
</key>
<value>
<string>
erp5
</string>
</value>
</item>
<item>
<key>
<string>
workflow_history
</string>
</key>
<value>
<persistent>
<string
encoding=
"base64"
>
AAAAAAAAAAM=
</string>
</persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record
id=
"2"
aka=
"AAAAAAAAAAI="
>
<pickle>
<global
name=
"PersistentMapping"
module=
"Persistence.mapping"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
data
</string>
</key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record
id=
"3"
aka=
"AAAAAAAAAAM="
>
<pickle>
<global
name=
"PersistentMapping"
module=
"Persistence.mapping"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
data
</string>
</key>
<value>
<dictionary>
<item>
<key>
<string>
component_validation_workflow
</string>
</key>
<value>
<persistent>
<string
encoding=
"base64"
>
AAAAAAAAAAQ=
</string>
</persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record
id=
"4"
aka=
"AAAAAAAAAAQ="
>
<pickle>
<global
name=
"WorkflowHistoryList"
module=
"Products.ERP5Type.patches.WorkflowTool"
/>
</pickle>
<pickle>
<tuple>
<none/>
<list>
<dictionary>
<item>
<key>
<string>
action
</string>
</key>
<value>
<string>
validate
</string>
</value>
</item>
<item>
<key>
<string>
validation_state
</string>
</key>
<value>
<string>
validated
</string>
</value>
</item>
</dictionary>
</list>
</tuple>
</pickle>
</record>
</ZopeData>
bt5/erp5_monaco_editor/SkinTemplateItem/portal_skins/erp5_monaco_editor/ERP5Site_getPythonSourceCodeCompletionList.xml
0 → 100644
View file @
18e165d6
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"ExternalMethod"
module=
"Products.ExternalMethod.ExternalMethod"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
_function
</string>
</key>
<value>
<string>
ERP5Site_getPythonSourceCodeCompletionList
</string>
</value>
</item>
<item>
<key>
<string>
_module
</string>
</key>
<value>
<string>
Jedi
</string>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
ERP5Site_getPythonSourceCodeCompletionList
</string>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string></string>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/erp5_monaco_editor/SkinTemplateItem/portal_skins/erp5_monaco_editor/monaco_editor_support.zpt
View file @
18e165d6
...
...
@@ -251,6 +251,46 @@ $script.onload = function() {
}
});
monaco.languages.registerCompletionItemProvider('python', {
provideCompletionItems: async function(model, position, context, token) {
const controller = new AbortController();
token.onCancellationRequested(() => {controller.abort()})
const data = new FormData();
const complete_parameters = {
code: model.getValue(),
position: {line: position.lineNumber, column: position.column}
};
// ZMI python scripts pass extra parameters to linter
if (bound_names) {
complete_parameters["bound_names"] = JSON.parse(bound_names);
complete_parameters["params"] = document.querySelector(
'input[name="params"]'
).value;
}
data.append("data", JSON.stringify(complete_parameters));
return fetch(portal_url + "/ERP5Site_getPythonSourceCodeCompletionList", {
method: "POST",
body: data,
signal: controller.signal
})
.then(response => response.json())
.then(data => {
return {suggestions: data.map(c => {
c.kind = monaco.languages.CompletionItemKind[c._kind];
// this makes monaco render documentation as markdown.
c.documentation = {value: c.documentation};
return c
})};
}, e => {
if (!e instanceof DOMException /* AbortError */ ) {
throw e;
}
/* ignore aborted requests */
});
}
});
if (mode === "python") {
// Perform a first check when loading document.
checkPythonSourceCode(new AbortController());
...
...
bt5/erp5_monaco_editor/bt/template_extension_id_list
0 → 100644
View file @
18e165d6
extension.erp5.Jedi
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment