Merge pull request #12888 from netchampfaris/server-script-autocompletion
feat: Add autocompletion items in Server Script
This commit is contained in:
commit
bdbdef92a8
4 changed files with 105 additions and 2 deletions
|
|
@ -18,7 +18,7 @@ global_cache_keys = ("app_hooks", "installed_apps", 'all_apps',
|
|||
'scheduler_events', 'time_zone', 'webhooks', 'active_domains',
|
||||
'active_modules', 'assignment_rule', 'server_script_map', 'wkhtmltopdf_version',
|
||||
'domain_restricted_doctypes', 'domain_restricted_pages', 'information_schema:counts',
|
||||
'sitemap_routes', 'db_tables') + doctype_map_keys
|
||||
'sitemap_routes', 'db_tables', 'server_script_autocompletion_items') + doctype_map_keys
|
||||
|
||||
user_cache_keys = ("bootinfo", "user_recent", "roles", "user_doc", "lang",
|
||||
"defaults", "user_permissions", "home_page", "linked_with",
|
||||
|
|
|
|||
|
|
@ -9,6 +9,12 @@ frappe.ui.form.on('Server Script', {
|
|||
if (frm.doc.script_type != 'Scheduler Event') {
|
||||
frm.dashboard.hide();
|
||||
}
|
||||
|
||||
frm.call('get_autocompletion_items')
|
||||
.then(r => r.message)
|
||||
.then(items => {
|
||||
frm.set_df_property('script', 'autocompletions', items);
|
||||
});
|
||||
},
|
||||
|
||||
setup_help(frm) {
|
||||
|
|
|
|||
|
|
@ -5,11 +5,12 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
import ast
|
||||
from types import FunctionType, MethodType, ModuleType
|
||||
from typing import Dict, List
|
||||
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils.safe_exec import safe_exec
|
||||
from frappe.utils.safe_exec import get_safe_globals, safe_exec, NamespaceDict
|
||||
from frappe import _
|
||||
|
||||
|
||||
|
|
@ -122,6 +123,51 @@ class ServerScript(Document):
|
|||
if locals["conditions"]:
|
||||
return locals["conditions"]
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_autocompletion_items(self):
|
||||
"""Generates a list of a autocompletion strings from the context dict
|
||||
that is used while executing a Server Script.
|
||||
|
||||
Returns:
|
||||
list: Returns list of autocompletion items.
|
||||
For e.g., ["frappe.utils.cint", "frappe.db.get_all", ...]
|
||||
"""
|
||||
def get_keys(obj):
|
||||
out = []
|
||||
for key in obj:
|
||||
if key.startswith('_'):
|
||||
continue
|
||||
value = obj[key]
|
||||
if isinstance(value, (NamespaceDict, dict)) and value:
|
||||
if key == 'form_dict':
|
||||
out.append(['form_dict', 7])
|
||||
continue
|
||||
for subkey, score in get_keys(value):
|
||||
fullkey = f'{key}.{subkey}'
|
||||
out.append([fullkey, score])
|
||||
else:
|
||||
if isinstance(value, type) and issubclass(value, Exception):
|
||||
score = 0
|
||||
elif isinstance(value, ModuleType):
|
||||
score = 10
|
||||
elif isinstance(value, (FunctionType, MethodType)):
|
||||
score = 9
|
||||
elif isinstance(value, type):
|
||||
score = 8
|
||||
elif isinstance(value, dict):
|
||||
score = 7
|
||||
else:
|
||||
score = 6
|
||||
out.append([key, score])
|
||||
return out
|
||||
|
||||
items = frappe.cache().get_value('server_script_autocompletion_items')
|
||||
if not items:
|
||||
items = get_keys(get_safe_globals())
|
||||
items = [{'value': d[0], 'score': d[1]} for d in items]
|
||||
frappe.cache().set_value('server_script_autocompletion_items', items)
|
||||
return items
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def setup_scheduler_events(script_name, frequency):
|
||||
|
|
|
|||
|
|
@ -31,6 +31,57 @@ frappe.ui.form.ControlCode = frappe.ui.form.ControlText.extend({
|
|||
const input_value = this.get_input_value();
|
||||
this.parse_validate_and_set_in_model(input_value);
|
||||
}, 300));
|
||||
|
||||
// setup autocompletion when it is set the first time
|
||||
Object.defineProperty(this.df, 'autocompletions', {
|
||||
get() {
|
||||
return this._autocompletions || [];
|
||||
},
|
||||
set: (value) => {
|
||||
this.setup_autocompletion();
|
||||
this.df._autocompletions = value;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
setup_autocompletion() {
|
||||
if (this._autocompletion_setup) return;
|
||||
|
||||
const ace = window.ace;
|
||||
const get_autocompletions = () => this.df.autocompletions;
|
||||
|
||||
ace.config.loadModule("ace/ext/language_tools", langTools => {
|
||||
this.editor.setOptions({
|
||||
enableBasicAutocompletion: true,
|
||||
enableLiveAutocompletion: true
|
||||
});
|
||||
|
||||
langTools.addCompleter({
|
||||
getCompletions: function(editor, session, pos, prefix, callback) {
|
||||
if (prefix.length === 0) {
|
||||
callback(null, []);
|
||||
return;
|
||||
}
|
||||
let autocompletions = get_autocompletions();
|
||||
if (autocompletions.length) {
|
||||
callback(
|
||||
null,
|
||||
autocompletions.map(a => {
|
||||
if (typeof a === 'string') {
|
||||
a = { value: a };
|
||||
}
|
||||
return {
|
||||
name: 'frappe',
|
||||
value: a.value,
|
||||
score: a.score
|
||||
};
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
this._autocompletion_setup = true;
|
||||
},
|
||||
|
||||
refresh_height() {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue