diff --git a/gui/l10n/bundle.l10n.json b/gui/l10n/bundle.l10n.json
index 016e8ee..65dd368 100644
--- a/gui/l10n/bundle.l10n.json
+++ b/gui/l10n/bundle.l10n.json
@@ -14,5 +14,6 @@
"Invalid property format": "Invalid property format",
"Invalid property name '{0}'": "Invalid property name '{0}'",
". Do you mean '{0}'?": ". Do you mean '{0}'?",
- "Negated value of property '{0}' will be ignored": "Negated value of property '{0}' will be ignored"
+ "Negated value of property '{0}' will be ignored": "Negated value of property '{0}' will be ignored",
+ "Function '{0}' in property '{1}' is not available": "Function '{0}' in property '{1}' is not available"
}
\ No newline at end of file
diff --git a/gui/src/assets/viselements.json b/gui/src/assets/viselements.json
index b70763e..aad6488 100644
--- a/gui/src/assets/viselements.json
+++ b/gui/src/assets/viselements.json
@@ -4,7 +4,6 @@
"text",
{
"inherits": [
- "on_change",
"shared"
],
"properties": [
@@ -47,7 +46,8 @@
{
"name": "on_action",
"type": "Callback",
- "doc": "The name of a function that is triggered when the button is pressed.\nThe parameters of that function are all optional:\n
\n- state (
State^
): the state instance. \n- id (optional[str]): the identifier of the button.
\n- action (optional[str]): the name of the action that provoked the change.
\n
"
+ "doc": "The name of a function that is triggered when the button is pressed.\nThe parameters of that function are all optional:\n\n- state (
State^
): the state instance. \n- id (optional[str]): the identifier of the button.
\n- action (optional[str]): the name of the action that provoked the change.
\n
",
+ "signature": [["state", "State"], ["id", "str"], ["action", "str"], ["payload", "dict"]]
}
]
}
@@ -293,12 +293,12 @@
{
"name": "lon",
"type": "indexed(str)",
- "doc": "Column name for the longitude value. Cf. [Plotly Map Traces](https://plotly.com/javascript/reference/scattergeo/)."
+ "doc": "Column name for the longitude value. Cf. [Plotly Map Traces](https://plotly.com/javascript/reference/scattergeo/#scattergeo-lon)."
},
{
"name": "lat",
"type": "indexed(str)",
- "doc": "Column name for the latitude value. Cf. [Plotly Map Traces](https://plotly.com/javascript/reference/scattergeo/)."
+ "doc": "Column name for the latitude value. Cf. [Plotly Map Traces](https://plotly.com/javascript/reference/scattergeo/#scattergeo-lat)."
},
{
"name": "r",
@@ -313,22 +313,37 @@
{
"name": "high",
"type": "indexed(str)",
- "doc": "Column name for the high value. Cf. [Ploty candlestick](https://plotly.com/javascript/reference/candlestick/)."
+ "doc": "Column name for the high value. Cf. [Ploty Candlestick](https://plotly.com/javascript/reference/candlestick/#candlestick-high)."
},
{
"name": "low",
"type": "indexed(str)",
- "doc": "Column name for the low value. Cf. [Ploty candlestick](https://plotly.com/javascript/reference/candlestick/)."
+ "doc": "Column name for the low value. Cf. [Ploty Candlestick](https://plotly.com/javascript/reference/candlestick/#candlestick-low)."
},
{
"name": "open",
"type": "indexed(str)",
- "doc": "Column name for the open value. Cf. [Ploty candlestick](https://plotly.com/javascript/reference/candlestick/)."
+ "doc": "Column name for the open value. Cf. [Ploty Candlestick](https://plotly.com/javascript/reference/candlestick/#candlestick-open)."
},
{
"name": "close",
"type": "indexed(str)",
- "doc": "Column name for the close value. Cf. [Ploty candlestick](https://plotly.com/javascript/reference/candlestick/)."
+ "doc": "Column name for the close value. Cf. [Ploty Candlestick](https://plotly.com/javascript/reference/candlestick/#candlestick-close)."
+ },
+ {
+ "name": "locations",
+ "type": "indexed(str)",
+ "doc": "Column name for the locations value. Cf. [Ploty Choropleth Map](https://plotly.com/javascript/choropleth-maps/)."
+ },
+ {
+ "name": "values",
+ "type": "indexed(str)",
+ "doc": "Column name for the values value. Cf. [Ploty Pie](https://plotly.com/javascript/reference/pie/#pie-values) or [Ploty FunnelArea](https://plotly.com/javascript/reference/funnelarea/#funnelarea-values)."
+ },
+ {
+ "name": "labels",
+ "type": "indexed(str)",
+ "doc": "Column name for the labels value. Cf. [Ploty Pie](https://plotly.com/javascript/reference/pie/#pie-labels)."
},
{
"name": "text",
@@ -354,7 +369,9 @@
{
"name": "on_range_change",
"type": "Callback",
- "doc": "Callback function called when the visible part of the x axis changes.\nThe function receives four parameters:\n\n- state (
State^
): the state instance. \n- id (optional[str]): the identifier of the chart control.
\n- action (optional[str]): the name of the action that provoked the change.
\n- payload (dict[str, any]): the full details on this callback's invocation, as emitted by plotly.
\n
"
+ "doc": "Callback function called when the visible part of the x axis changes.\nThe function receives four parameters:\n\n- state (
State^
): the state instance. \n- id (optional[str]): the identifier of the chart control.
\n- action (optional[str]): the name of the action that provoked the change.
\n- payload (dict[str, any]): the full details on this callback's invocation, as emitted by plotly.
\n
",
+ "signature": [["state", "State"], ["id", "str"], ["action", "str"], ["payload", "dict"]]
+
},
{
"name": "columns",
@@ -390,7 +407,7 @@
{
"name": "marker",
"type": "indexed(dict[str, any])",
- "doc": "The type of markers used for the indicated trace.\nSee marker for details.\nColor and size can be column name."
+ "doc": "The type of markers used for the indicated trace.\nSee marker for details.\nColor, opacity, size and symbol can be column name."
},
{
"name": "line",
@@ -448,6 +465,21 @@
"type": "str|int|float",
"doc": "The height, in CSS units, of this element."
},
+ {
+ "name": "template",
+ "type": "dict",
+ "doc": "The plotly layout [template](https://plotly.com/javascript/layout-template/)."
+ },
+ {
+ "name": "template[dark]",
+ "type": "dict",
+ "doc": "The plotly layout template applied over the base template when theme is dark."
+ },
+ {
+ "name": "template[light]",
+ "type": "dict",
+ "doc": "The plotly layout template applied over the base template when theme is not dark."
+ },
{
"name": "decimator",
"type": "taipy.gui.data.Decimator",
@@ -478,7 +510,8 @@
{
"name": "on_action",
"type": "Callback",
- "doc": "The name of a function that is triggered when the download is initiated.\nAll the parameters of that function are optional:\n\n- state (
State^
): the state instance. \n- id (optional[str]): the identifier of the button.
\n- action (optional[str]): the name of the action that provoked the change.
\n
"
+ "doc": "The name of a function that is triggered when the download is initiated.\nAll the parameters of that function are optional:\n\n- state (
State^
): the state instance. \n- id (optional[str]): the identifier of the button.
\n- action (optional[str]): the name of the action that provoked the change.
\n
",
+ "signature": [["state", "State"], ["id", "str"], ["action", "str"]]
},
{
"name": "auto",
@@ -528,7 +561,8 @@
{
"name": "on_action",
"type": "Callback",
- "doc": "The name of the function that will be triggered.\nAll the parameters of that function are optional:\n\n- state (
State^
): the state instance. \n- id (optional[str]): the identifier of the button.
\n- action (optional[str]): the name of the action that provoked the change.
\n
"
+ "doc": "The name of the function that will be triggered.\nAll the parameters of that function are optional:\n\n- state (
State^
): the state instance. \n- id (optional[str]): the identifier of the button.
\n- action (optional[str]): the name of the action that provoked the change.
\n
",
+ "signature": [["state", "State"], ["id", "str"], ["action", "str"]]
},
{
"name": "multiple",
@@ -573,7 +607,8 @@
{
"name": "on_action",
"type": "str",
- "doc": "The name of a function that is triggered when the user clicks on the image.\nAll the parameters of that function are optional:\n\n- state (
State^
): the state instance. \n- id (optional[str]): the identifier of the button.
\n- action (optional[str]): the name of the action that provoked the change.
\n
"
+ "doc": "The name of a function that is triggered when the user clicks on the image.\nAll the parameters of that function are optional:\n\n- state (
State^
): the state instance. \n- id (optional[str]): the identifier of the button.
\n- action (optional[str]): the name of the action that provoked the change.
\n
",
+ "signature": [["state", "State"], ["id", "str"], ["action", "str"], ["payload", "dict"]]
},
{
"name": "width",
@@ -691,7 +726,8 @@
{
"name": "on_action",
"type": "Callback",
- "doc": "The name of the function that will be triggered when a menu option is selected.
\nAll the parameters of that function are optional:\n\n- state (
State^
): the state instance. \n- id (str): the identifier of the button.
\n- action (str): the name of the action that provoked the change.
\n- payload (dict): the details on this callback's invocation.
\nThis dictionary has the following keys:\n\n- args: List where the first element contains the id of the selected option.
\n
\n \n
"
+ "doc": "The name of the function that will be triggered when a menu option is selected.
\nAll the parameters of that function are optional:\n\n- state (
State^
): the state instance. \n- id (str): the identifier of the button.
\n- action (str): the name of the action that provoked the change.
\n- payload (dict): the details on this callback's invocation.
\nThis dictionary has the following keys:\n\n- args: List where the first element contains the id of the selected option.
\n
\n \n
",
+ "signature": [["state", "State"], ["id", "str"], ["action", "str"], ["payload", "dict"]]
}
]
}
@@ -919,17 +955,26 @@
{
"name": "on_edit",
"type": "Callback",
- "doc": "The name of a function that is to be triggered when a cell edition is validated.
\nAll parameters of that function are optional:\n\n- state (
State^
): the state instance. \n- var_name (str): the name of the tabular data variable.
\n- action (str): the name of the action that provoked the change.
\n- payload (dict): the details on this callback's invocation.
\nThis dictionary has the following keys:\n\n- index (int): the row index.
\n- col (str): the column name.
\n- value (any): the new cell value cast to the type of the column.
\n- user_value (str): the new cell value as entered by the user.
\n
\n \n
\nIf this property is not set, the user cannot edit cells."
+ "doc": "The name of a function that is to be triggered when a cell edition is validated.
\nAll parameters of that function are optional:\n\n- state (
State^
): the state instance. \n- var_name (str): the name of the tabular data variable.
\n- action (str): the name of the action that provoked the change.
\n- payload (dict): the details on this callback's invocation.
\nThis dictionary has the following keys:\n\n- index (int): the row index.
\n- col (str): the column name.
\n- value (any): the new cell value cast to the type of the column.
\n- user_value (str): the new cell value as entered by the user.
\n
\n \n
\nIf this property is not set, the user cannot edit cells.",
+ "signature": [["state", "State"], ["var_name", "str"], ["action", "str"], ["payload", "dict"]]
},
{
"name": "on_delete",
"type": "str",
- "doc": "The name of a function that is to be triggered when a row is deleted.
\nAll parameters of that function are optional:\n\n- state (
State^
): the state instance. \n- var_name (str): the name of the tabular data variable.
\n- action (str): the name of the action that provoked the change.
\n- payload (dict): the details on this callback's invocation.
\nThis dictionary has the following keys:\n\n- index (int): the row index.
\n
\n \n
\nIf this property is not set, the user cannot delete rows."
+ "doc": "The name of a function that is to be triggered when a row is deleted.
\nAll parameters of that function are optional:\n\n- state (
State^
): the state instance. \n- var_name (str): the name of the tabular data variable.
\n- action (str): the name of the action that provoked the change.
\n- payload (dict): the details on this callback's invocation.
\nThis dictionary has the following keys:\n\n- index (int): the row index.
\n
\n \n
\nIf this property is not set, the user cannot delete rows.",
+ "signature": [["state", "State"], ["var_name", "str"], ["action", "str"], ["payload", "dict"]]
},
{
"name": "on_add",
"type": "str",
- "doc": "The name of a function that is to be triggered when the user requests a row to be added.
\nAll parameters of that function are optional:\n\n- state (
State^
): the state instance. \n- var_name (str): the name of the tabular data variable.
\n- action (str): the name of the action that provoked the change.
\n- payload (dict): the details on this callback's invocation.
\nThis dictionary has the following keys:\n\n- index (int): the row index.
\n
\n \n
\nIf this property is not set, the user cannot add rows."
+ "doc": "The name of a function that is to be triggered when the user requests a row to be added.
\nAll parameters of that function are optional:\n\n- state (
State^
): the state instance. \n- var_name (str): the name of the tabular data variable.
\n- action (str): the name of the action that provoked the change.
\n- payload (dict): the details on this callback's invocation.
\nThis dictionary has the following keys:\n\n- index (int): the row index.
\n
\n \n
\nIf this property is not set, the user cannot add rows.",
+ "signature": [["state", "State"], ["var_name", "str"], ["action", "str"], ["payload", "dict"]]
+ },
+ {
+ "name": "on_action",
+ "type": "str",
+ "doc": "The name of a function that is to be triggered when the user selects a row.
\nAll parameters of that function are optional:\n\n- state (
State^
): the state instance. \n- var_name (str): the name of the tabular data variable.
\n- action (str): the name of the action that provoked the change.
\n- payload (dict): the details on this callback's invocation.
\nThis dictionary has the following keys:\n\n- index (int): the row index.
.",
+ "signature": [["state", "State"], ["var_name", "str"], ["action", "str"], ["payload", "dict"]]
},
{
"name": "size",
@@ -959,7 +1004,8 @@
{
"name": "on_action",
"type": "Callback",
- "doc": "Name of a function that is triggered when a button is pressed.
\nThe parameters of that function are all optional:\n\n- state (
State^
): the state instance. \n- id (str): the identifier of the dialog.
\n- action (str): the name of the action that provoked the change.
\n- payload (dict): the details on this callback's invocation.
\nThis dictionary has the following keys:\n\n- args: a list where the first element contains the index of the selected label.
\n
\n \n
\n"
+ "doc": "Name of a function that is triggered when a button is pressed.
\nThe parameters of that function are all optional:\n\n- state (
State^
): the state instance. \n- id (str): the identifier of the dialog.
\n- action (str): the name of the action that provoked the change.
\n- payload (dict): the details on this callback's invocation.
\nThis dictionary has the following keys:\n\n- args: a list where the first element contains the index of the selected label.
\n
\n \n
\n",
+ "signature": [["state", "State"], ["id", "str"], ["action", "str"], ["payload", "dict"]]
},
{
"name": "close_label",
@@ -1110,7 +1156,8 @@
{
"name": "on_close",
"type": "Callback",
- "doc": "The name of a function that is be triggered when this pane is closed (if the user clicks outside of it or presses the Esc key).\nAll parameters of that function are optional:\n\n- state (
State^
): the state instance. \n- id (optional[str]): the identifier of the button.
\n- action (optional[str]): the name of the action that provoked the change.
\n
\nIf this property is not set, no function is called when this pane is closed."
+ "doc": "The name of a function that is be triggered when this pane is closed (if the user clicks outside of it or presses the Esc key).\nAll parameters of that function are optional:\n\n- state (
State^
): the state instance. \n- id (optional[str]): the identifier of the button.
\n- action (optional[str]): the name of the action that provoked the change.
\n
\nIf this property is not set, no function is called when this pane is closed.",
+ "signature": [["state", "State"], ["id", "str"], ["action", "str"]]
},
{
"name": "anchor",
@@ -1200,7 +1247,8 @@
{
"name": "on_change",
"type": "Callback",
- "doc": "The name of a function that is triggered when the value is updated.
\nThe parameters of that function are all optional:\n\n- state (
State^
): the state instance. \n- var_name (str): the variable name.
\n- value (any): the new value.
\n
"
+ "doc": "The name of a function that is triggered when the value is updated.
\nThe parameters of that function are all optional:\n\n- state (
State^
): the state instance. \n- var_name (str): the variable name.
\n- value (any): the new value.
\n
",
+ "signature": [["state", "State"], ["var_name", "str"], ["value", ""]]
}
]
}
@@ -1252,7 +1300,8 @@
{
"name": "on_action",
"type": "Callback",
- "doc": "Name of a function that is triggered when a specific key is pressed.
\nThe parameters of that function are all optional:\n\n- state (
State^
): the state instance. \n- id (str): the identifier of the dialog.
\n- action (str): the name of the action that provoked the change.
\n- payload (dict): the details on this callback's invocation.
\nThis dictionary has the following keys:\n\n- args (List):\n
- key name
- variable name
- current value
\n \n
\n \n
\n"
+ "doc": "Name of a function that is triggered when a specific key is pressed.
\nThe parameters of that function are all optional:\n\n- state (
State^
): the state instance. \n- id (str): the identifier of the input.
\n- action (str): the name of the action that provoked the change.
\n- payload (dict): the details on this callback's invocation.
\nThis dictionary has the following keys:\n\n- args (List):\n
- key name
- variable name
- current value
\n \n
\n \n
\n",
+ "signature": [["state", "State"], ["id", "str"], ["action", "str"], ["payload", "dict"]]
},
{
"name": "action_keys",
diff --git a/gui/src/gui/codeAction.ts b/gui/src/gui/codeAction.ts
index 6d06afa..2fee2c6 100644
--- a/gui/src/gui/codeAction.ts
+++ b/gui/src/gui/codeAction.ts
@@ -6,6 +6,7 @@ import {
CodeActionProvider,
Diagnostic,
ExtensionContext,
+ l10n,
languages,
Position,
Range,
@@ -13,14 +14,16 @@ import {
TextDocument,
WorkspaceEdit,
} from "vscode";
+import { defaultOnFunctionSignature } from "./constant";
-import { DiagnosticCode } from "./diagnostics";
-import { markdownDocumentFilter, pythonDocumentFilter } from "./utils";
+import { DiagnosticCode, PROPERTY_RE } from "./diagnostics";
+import { generateOnFunction, markdownDocumentFilter, pythonDocumentFilter } from "./utils";
export class MarkdownActionProvider implements CodeActionProvider {
public static readonly providedCodeActionKinds = [CodeActionKind.QuickFix];
- private readonly codeActionMap: Record CodeAction> = {
+ private readonly codeActionMap: Record CodeAction | null> = {
[DiagnosticCode.missCSyntax]: this.createMCSCodeAction,
+ [DiagnosticCode.functionNotFound]: this.createFNFCodeAction,
};
static register(context: ExtensionContext): void {
@@ -43,8 +46,8 @@ export class MarkdownActionProvider implements CodeActionProvider {
token: CancellationToken
): CodeAction[] {
const codeActions: CodeAction[] = [];
- context.diagnostics.forEach((v) => {
- const codeAction = this.createCodeAction(document, v);
+ context.diagnostics.forEach((diagnostic) => {
+ const codeAction = this.createCodeAction(document, diagnostic);
codeAction !== null && codeActions.push(codeAction);
});
return codeActions;
@@ -59,7 +62,7 @@ export class MarkdownActionProvider implements CodeActionProvider {
}
private createMCSCodeAction(document: TextDocument, diagnostic: Diagnostic): CodeAction {
- const action = new CodeAction("Add closing tag", CodeActionKind.QuickFix);
+ const action = new CodeAction(l10n.t("Add closing tag"), CodeActionKind.QuickFix);
action.diagnostics = [diagnostic];
action.isPreferred = true;
action.edit = new WorkspaceEdit();
@@ -73,4 +76,35 @@ export class MarkdownActionProvider implements CodeActionProvider {
}
return action;
}
+
+ private createFNFCodeAction(document: TextDocument, diagnostic: Diagnostic): CodeAction | null {
+ const propMatch = PROPERTY_RE.exec(document.getText(diagnostic.range));
+ if (!propMatch) {
+ return null;
+ }
+ const onFunctionType = propMatch[2];
+ const functionName = propMatch[3];
+ const action = new CodeAction(l10n.t("Create function '{0}'", functionName), CodeActionKind.QuickFix);
+ const diagnosticPosition = diagnostic.range.end;
+ const quotePositions: Position[] = document
+ .getText()
+ .split(/\r?\n/)
+ .reduce((obj: Position[], v: string, i: number) => {
+ return [...obj, ...Array.from(v.matchAll(new RegExp('"""', "g")), (a) => new Position(i, a.index || 0))];
+ }, [])
+ .filter(
+ (v) =>
+ v.line > diagnosticPosition.line ||
+ (v.line === diagnosticPosition.line && v.character > diagnosticPosition.character)
+ );
+ action.diagnostics = [diagnostic];
+ action.isPreferred = true;
+ action.edit = new WorkspaceEdit();
+ action.edit.insert(
+ document.uri,
+ quotePositions.length > 0 ? quotePositions[0].translate(0, 3) : new Position(document.lineCount - 1, 0),
+ "\n\n" + generateOnFunction(defaultOnFunctionSignature[onFunctionType] || [["state", "State"]], functionName)
+ );
+ return action;
+ }
}
diff --git a/gui/src/gui/command.ts b/gui/src/gui/command.ts
index 960165a..b5216c6 100644
--- a/gui/src/gui/command.ts
+++ b/gui/src/gui/command.ts
@@ -1,6 +1,6 @@
import { commands, ExtensionContext, l10n, window, workspace, WorkspaceEdit } from "vscode";
import { defaultElementList, defaultElementProperties } from "./constant";
-import { countChar } from "./utils";
+import { countChar, parseProperty } from "./utils";
interface GuiElement {
elementName: string;
@@ -50,7 +50,7 @@ export class GenerateGuiCommand {
}
quickPick.items = Object.keys(propertyObject).map((label) => ({
label,
- detail: propertyObject[label as keyof typeof propertyObject],
+ detail: parseProperty(propertyObject[label as keyof typeof propertyObject]),
}));
quickPick.canSelectMany = true;
quickPick.title = l10n.t("Select properties for element '{0}'", guiElement.elementName);
diff --git a/gui/src/gui/completion.ts b/gui/src/gui/completion.ts
index fef8f6d..45905cf 100644
--- a/gui/src/gui/completion.ts
+++ b/gui/src/gui/completion.ts
@@ -16,7 +16,7 @@ import {
Uri,
} from "vscode";
import { defaultElementList, defaultElementProperties, defaultOnFunctionList, LanguageId } from "./constant";
-import { markdownDocumentFilter, pythonDocumentFilter } from "./utils";
+import { markdownDocumentFilter, parseProperty, pythonDocumentFilter } from "./utils";
const RE_LINE = /<(([\|]{1})([^\|]*)){1,2}/;
@@ -42,8 +42,7 @@ export class GuiCompletionItemProvider implements CompletionItemProvider {
);
}
- private constructor(private readonly language: LanguageId) {
- }
+ private constructor(private readonly language: LanguageId) {}
public async provideCompletionItems(
document: TextDocument,
@@ -112,7 +111,7 @@ export class GuiCompletionItemProvider implements CompletionItemProvider {
}, [])
.map((v) => {
let completionItem = new CompletionItem(v, CompletionItemKind.Property);
- completionItem.documentation = new MarkdownString(properties[v as keyof typeof properties]);
+ completionItem.documentation = new MarkdownString(parseProperty(properties[v as keyof typeof properties]));
return completionItem;
});
}
@@ -126,7 +125,7 @@ export class GuiCompletionItemProvider implements CompletionItemProvider {
symbolKind: SymbolKind,
completionItemKind: CompletionItemKind
): Promise {
- let symbols = (await commands.executeCommand("vscode.executeDocumentSymbolProvider", uri)) as SymbolInformation[];
- return symbols.filter((v) => v.kind === symbolKind).map((v) => new CompletionItem(v.name, completionItemKind));
+ const symbols = (await commands.executeCommand("vscode.executeDocumentSymbolProvider", uri)) as SymbolInformation[];
+ return !symbols ? [] : symbols.filter((v) => v.kind === symbolKind).map((v) => new CompletionItem(v.name, completionItemKind));
}
}
diff --git a/gui/src/gui/constant.ts b/gui/src/gui/constant.ts
index 3debf17..0723a7c 100644
--- a/gui/src/gui/constant.ts
+++ b/gui/src/gui/constant.ts
@@ -1,5 +1,5 @@
import visualElements from "../assets/viselements.json";
-import { getBlockElementList, getControlElementList, getElementList, getElementProperties, getOnFunctionList } from "./utils";
+import { getBlockElementList, getControlElementList, getElementList, getElementProperties, getOnFunctionList, getOnFunctionSignature } from "./utils";
// object of all elements each with all of its properties
export const defaultElementProperties = getElementProperties(visualElements);
@@ -13,6 +13,8 @@ export const defaultBlockElementList = getBlockElementList(visualElements);
export const defaultOnFunctionList = getOnFunctionList(defaultElementProperties);
+export const defaultOnFunctionSignature = getOnFunctionSignature(defaultElementProperties);
+
export enum LanguageId {
py = "python",
md = "markdown",
diff --git a/gui/src/gui/diagnostics.ts b/gui/src/gui/diagnostics.ts
index 0f2b688..ada2d53 100644
--- a/gui/src/gui/diagnostics.ts
+++ b/gui/src/gui/diagnostics.ts
@@ -1,4 +1,5 @@
import {
+ commands,
Diagnostic,
DiagnosticCollection,
DiagnosticSeverity,
@@ -7,6 +8,8 @@ import {
languages,
Position,
Range,
+ SymbolInformation,
+ SymbolKind,
TextDocument,
window,
workspace,
@@ -18,12 +21,13 @@ const CONTROL_RE = /<\|(.*?)\|>/;
const OPENING_TAG_RE = /<([0-9a-zA-Z\_\.]*)\|((?:(?!\|>).)*)\s*$/;
const CLOSING_TAG_RE = /^\s*\|([0-9a-zA-Z\_\.]*)>/;
const SPLIT_RE = /(? {
+export const registerDiagnostics = async (context: ExtensionContext): Promise => {
const mdDiagnosticCollection = languages.createDiagnosticCollection("taipy-gui-markdown");
- const didOpen = workspace.onDidOpenTextDocument((doc) => refreshDiagnostics(doc, mdDiagnosticCollection));
- const didChange = workspace.onDidChangeTextDocument((e) => refreshDiagnostics(e.document, mdDiagnosticCollection));
+ const didOpen = workspace.onDidOpenTextDocument(async (doc) => await refreshDiagnostics(doc, mdDiagnosticCollection));
+ const didChange = workspace.onDidChangeTextDocument(
+ async (e) => await refreshDiagnostics(e.document, mdDiagnosticCollection)
+ );
const didClose = workspace.onDidCloseTextDocument((doc) => mdDiagnosticCollection.delete(doc.uri));
- window.activeTextEditor && refreshDiagnostics(window.activeTextEditor.document, mdDiagnosticCollection);
+ window.activeTextEditor && (await refreshDiagnostics(window.activeTextEditor.document, mdDiagnosticCollection));
context.subscriptions.push(mdDiagnosticCollection, didOpen, didChange, didClose);
};
-const refreshDiagnostics = (doc: TextDocument, diagnosticCollection: DiagnosticCollection) => {
+const refreshDiagnostics = async (doc: TextDocument, diagnosticCollection: DiagnosticCollection) => {
let diagnostics: Diagnostic[] | undefined = undefined;
- const uri = doc.uri.fsPath;
- if (uri.endsWith(".md") || doc.languageId === LanguageId.md) {
+ const uri = doc.uri;
+ if (uri.path.endsWith(".md") || doc.languageId === LanguageId.md) {
diagnostics = getMdDiagnostics(doc);
- } else if (uri.endsWith(".py") || doc.languageId === LanguageId.py) {
- diagnostics = getPyDiagnostics(doc);
+ } else if (uri.path.endsWith(".py") || doc.languageId === LanguageId.py) {
+ diagnostics = await getPyDiagnostics(doc);
}
- diagnostics && diagnosticCollection.set(doc.uri, diagnostics);
+ diagnostics && diagnosticCollection.set(uri, diagnostics);
};
const getMdDiagnostics = (doc: TextDocument): Diagnostic[] => {
return getSectionDiagnostics({ content: doc.getText() });
};
-const getPyDiagnostics = (doc: TextDocument): Diagnostic[] => {
+const getPyDiagnostics = async (doc: TextDocument): Promise => {
const text = doc.getText();
const d: Diagnostic[] = [];
+ const symbols = (await commands.executeCommand("vscode.executeDocumentSymbolProvider", doc.uri)) as SymbolInformation[];
const quotePositions: Position[] = text.split(/\r?\n/).reduce((obj: Position[], v: string, i: number) => {
- return [...obj, ...[...v.matchAll(new RegExp('"""', "gi"))].map((a) => new Position(i, a.index || 0))];
+ return [...obj, ...Array.from(v.matchAll(new RegExp('"""', "g")), (a) => new Position(i, a.index || 0))];
}, []);
if (quotePositions.length % 2 !== 0) {
return [];
@@ -92,6 +100,7 @@ const getPyDiagnostics = (doc: TextDocument): Diagnostic[] => {
...getSectionDiagnostics({
content: getTextFromPositions(text, quotePositions[i], quotePositions[i + 1]),
initialPosition: quotePositions[i].translate(0, 3),
+ symbols: symbols,
})
);
}
@@ -144,7 +153,8 @@ const getSectionDiagnostics = (diagnosticSection: DiagnosticSection): Diagnostic
const [d, _] = processElement(
elementMatch[1],
new Position(lineCount, line.indexOf(elementMatch[1])),
- initialPosition
+ initialPosition,
+ diagnosticSection.symbols
);
diagnostics.push(...d);
}
@@ -222,7 +232,12 @@ const getSectionDiagnostics = (diagnosticSection: DiagnosticSection): Diagnostic
return diagnostics;
};
-const processElement = (s: string, inlinePosition: Position, initialPosition: Position): [Diagnostic[], TaipyElement] => {
+const processElement = (
+ s: string,
+ inlinePosition: Position,
+ initialPosition: Position,
+ symbols: SymbolInformation[] | undefined = undefined
+): [Diagnostic[], TaipyElement] => {
const d: Diagnostic[] = [];
const fragments = s.split(SPLIT_RE).filter((v) => !!v);
const e = buildEmptyTaipyElement();
@@ -274,6 +289,15 @@ const processElement = (s: string, inlinePosition: Position, initialPosition: Po
)
);
}
+ if (propName.startsWith("on_") && symbols && !symbols.some((s) => s.name === val && s.kind === SymbolKind.Function)) {
+ d.push(
+ createWarningDiagnostic(
+ l10n.t("Function '{0}' in property '{1}' is not available", val, propName),
+ DiagnosticCode.functionNotFound,
+ getRangeFromPosition(initialPosition, getRangeOfStringInline(s, fragment, inlinePosition))
+ )
+ );
+ }
e.properties.push({ name: propName, value: notPrefix ? "False" : val ? val : "True" });
});
e.type = e.type ? e.type : "text";
diff --git a/gui/src/gui/utils.ts b/gui/src/gui/utils.ts
index 571ca1e..e056df1 100644
--- a/gui/src/gui/utils.ts
+++ b/gui/src/gui/utils.ts
@@ -1,5 +1,4 @@
import { DocumentFilter } from "vscode";
-import { LanguageId } from "./constant";
export const countChar = (str: string, char: string): number => {
return str.split(char).length - 1;
@@ -10,6 +9,7 @@ interface ElementProperty {
default_property?: any;
type: string;
doc: string;
+ signature?: [string, string][];
}
interface ElementDetail {
@@ -18,7 +18,7 @@ interface ElementDetail {
}
// visual elements parser
-export const getElementProperties = (visualElements: object): Record> => {
+export const getElementProperties = (visualElements: object): Record> => {
const blocks: Record = (visualElements["blocks" as keyof typeof visualElements] as any).reduce(
(obj: Record, v: any) => {
obj[v[0]] = v[1];
@@ -39,8 +39,8 @@ export const getElementProperties = (visualElements: object): Record);
- const blocksProperties: Record> = {};
- const controlsProperties: Record> = {};
+ const blocksProperties: Record> = {};
+ const controlsProperties: Record> = {};
// handle all blocks object
Object.keys(blocks).forEach((v: string) => {
let elementDetail: ElementDetail = blocks[v];
@@ -65,20 +65,20 @@ export const getElementList = (visualElements: object): string[] => {
return [...getControlElementList(visualElements), ...getBlockElementList(visualElements)];
};
-const parseProperty = (property: ElementProperty): string => {
+export const parseProperty = (property: ElementProperty): string => {
return `[${property.type}]${property.default_property ? "(" + property.default_property.toString() + ")" : ""}: ${
property.doc
}`;
};
-const parsePropertyList = (propertyList: ElementProperty[] | undefined): Record => {
+const parsePropertyList = (propertyList: ElementProperty[] | undefined): Record => {
if (!propertyList) {
return {};
}
- return propertyList.reduce((obj: Record, v: ElementProperty) => {
- obj[v.name] = parseProperty(v);
+ return propertyList.reduce((obj: Record, v: ElementProperty) => {
+ obj[v.name] = v;
return obj;
- }, {} as Record);
+ }, {} as Record);
};
const handleElementDetailInherits = (
@@ -86,8 +86,8 @@ const handleElementDetailInherits = (
blocks: Record,
controls: Record,
undocumented: Record
-): Record => {
- let properties: Record = {};
+): Record => {
+ let properties: Record = {};
if (!inherits) {
return properties;
}
@@ -110,14 +110,14 @@ const getElementDetailProperties = (
blocks: Record,
controls: Record,
undocumented: Record
-): Record => {
+): Record => {
return {
...parsePropertyList(elementDetail.properties),
...handleElementDetailInherits(elementDetail.inherits, blocks, controls, undocumented),
};
};
-export const getOnFunctionList = (elementProperties: Record>): string[] => {
+export const getOnFunctionList = (elementProperties: Record>): string[] => {
const onFunctionList = new Set();
for (const key in elementProperties) {
const elementProperty = elementProperties[key];
@@ -130,5 +130,25 @@ export const getOnFunctionList = (elementProperties: Record>
+): Record => {
+ const onFunctionRecord: Record = {};
+ for (const key in elementProperties) {
+ const elementProperty = elementProperties[key];
+ for (const k in elementProperty) {
+ if (k.startsWith("on_")) {
+ const elements = elementProperty[k];
+ onFunctionRecord[k] = elements["signature"] || [["state", "State"]];
+ }
+ }
+ }
+ return onFunctionRecord;
+};
+
+export const generateOnFunction = (signature: [string, string][], functionName: string): string => {
+ return `def ${functionName}(${signature.map((v) => v[0]).join(", ")}):\n pass\n`;
+};
+
+export const markdownDocumentFilter: DocumentFilter = { language: "markdown" };
+export const pythonDocumentFilter: DocumentFilter = { language: "python" };