Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handles "try" structures wrong syntax (missing "except" or "finally") AND new localisation architecture #322

Merged
merged 18 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
04c6270
Use TigerPython upstream package again
PwtKCL Nov 22, 2024
a1e77a6
It seems we used some old or weird property naming of TigerPython in …
PwtKCL Nov 22, 2024
64529ff
[unrelated]: use the right locale for TigerPython errors.
PwtKCL Nov 22, 2024
e5c0f32
Support frame-related error at parsing *WITHOUT* listing in error co…
PwtKCL Nov 22, 2024
0203f3e
Show frame errors in the error count and handle errors navigation
PwtKCL Nov 22, 2024
edf1764
Navigate to first code (parse) error: updated mechanism from PEA when…
PwtKCL Nov 22, 2024
cad3d6a
Fix small issue with deleting erroneous frame
PwtKCL Nov 22, 2024
76c8123
Indent joint frame triple quote comment with extra indentation for jo…
PwtKCL Nov 24, 2024
415a2cf
Support for new localisation files organisation (sub folder per local…
PwtKCL Nov 25, 2024
5a722a7
Removed TigerPython from package-lock.json to force npm install to re…
PwtKCL Nov 25, 2024
3670f4f
[UNRELATED] fixed an issue with setting state for the Strype microbit…
PwtKCL Nov 25, 2024
0ae4777
Support for default child/join frames in a frame def (I changed the f…
PwtKCL Nov 25, 2024
3a6b127
Define "try" frames to come with default "expect" frame
PwtKCL Nov 25, 2024
1b99063
Revert "Removed TigerPython from package-lock.json to force npm insta…
neilccbrown Nov 25, 2024
8cb7a9b
Updated TigerPython version.
neilccbrown Nov 25, 2024
2c761d1
Removed some tests that don't make sense now that TigerPython treats …
neilccbrown Nov 25, 2024
e4160a3
Merge pull request #1 from neilccbrown/try-struct-error
PwtKCL Nov 26, 2024
d368636
Merge commit 'e4160a3bc16570c9927b78a74ec70789d59acccb' into try-stru…
PwtKCL Nov 26, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"sortablejs": "^1.15.0",
"splitpanes": "^2.4.1",
"textarea-caret": "^3.1.0",
"tigerpython-parser": "github:neilccbrown/TigerPython-Parser#fix-empty-global",
"tigerpython-parser": "github:Tobias-Kohn/TigerPython-Parser",
"ts-node": "^10.9.1",
"ts-node-dev": "^2.0.0",
"v-blur": "^1.0.4",
Expand Down
1 change: 1 addition & 0 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
<div class="row no-gutters" >
<Menu
:id="menuUID"
:ref="menuUID"
@app-showprogress="applyShowAppProgress"
@app-reset-project="resetStrypeProject"
class="noselect no-print"
Expand Down
35 changes: 30 additions & 5 deletions src/components/Frame.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<!-- keep the tabIndex attribute, it is necessary to handle focus with Safari -->
<div
:style="frameStyle"
:class="{frameDiv: true, blockFrameDiv: isBlockFrame && !isJointFrame, statementFrameDiv: !isBlockFrame && !isJointFrame}"
:class="{frameDiv: true, blockFrameDiv: isBlockFrame && !isJointFrame, statementFrameDiv: !isBlockFrame && !isJointFrame, error: hasParsingError}"
:id="UID"
@click="toggleCaret($event)"
@contextmenu="handleClick($event)"
Expand Down Expand Up @@ -37,15 +37,16 @@
:frameAllowChildren="allowChildren"
:erroneous="hasRuntimeError"
:wasLastRuntimeError="wasLastRuntimeError"
:onFocus="showFrameParseErrorPopupOnHeaderFocus"
/>
<b-popover
v-if="hasRuntimeError || wasLastRuntimeError"
v-if="hasRuntimeError || wasLastRuntimeError || hasParsingError"
ref="errorPopover"
:target="frameHeaderId"
:title="$t((hasRuntimeError) ? 'PEA.runtimeErrorConsole' : 'errorMessage.pastFrameErrTitle')"
:title="errorPopupTitle"
triggers="hover"
:content="(hasRuntimeError) ? runTimeErrorMessage : runtimeErrorAtLastRunMsg"
:custom-class="(hasRuntimeError) ? 'error-popover modified-title-popover': 'error-popover'"
:content="errorPopupContent"
:custom-class="(hasRuntimeError || hasParsingError) ? 'error-popover modified-title-popover': 'error-popover'"
placement="left"
>
</b-popover>
Expand Down Expand Up @@ -208,6 +209,14 @@ export default Vue.extend({
return this.isBeingDragged || !!this.appStore.frameObjects[this.frameId].isBeingDragged;
},

parsingErrorMessage(): string {
return this.appStore.frameObjects[this.frameId].atParsingError ?? "";
},

hasParsingError(): boolean {
return this.parsingErrorMessage.length > 0;
},

runTimeErrorMessage(): string {
return this.appStore.frameObjects[this.frameId].runTimeError ?? "";
},
Expand All @@ -220,6 +229,14 @@ export default Vue.extend({
return this.appStore.wasLastRuntimeErrorFrameId == this.frameId;
},

errorPopupTitle(): string {
return this.$t((this.hasParsingError) ? "errorMessage.errorTitle" : ((this.hasRuntimeError) ? "PEA.runtimeErrorConsole" : "errorMessage.pastFrameErrTitle")) as string;
},

errorPopupContent(): string {
return (this.hasParsingError) ? this.parsingErrorMessage : ((this.hasRuntimeError) ? this.runTimeErrorMessage : this.runtimeErrorAtLastRunMsg);
},

deletableFrame(): boolean{
return (this.appStore.potentialDeleteFrameIds?.includes(this.frameId)) ?? false;
},
Expand Down Expand Up @@ -1051,6 +1068,14 @@ export default Vue.extend({
deleteOuter(): void {
this.appStore.deleteOuterFrames(this.frameId);
},

showFrameParseErrorPopupOnHeaderFocus(isFocusing: boolean): void{
// We need to be able to show the frame error popup programmatically
// (if applies) when we navigate to the error - we make sure the frame still exists.
if(this.appStore.frameObjects[this.frameId] && this.hasParsingError){
(this.$refs.errorPopover as InstanceType<typeof BPopover>).$emit((isFocusing) ? "open" : "close");
}
},
},
});
</script>
Expand Down
3 changes: 2 additions & 1 deletion src/components/FrameHeader.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<div>
<div tabindex="-1" @focus="onFocus(true)" @blur="onFocus(false)" style="outline: none;">
<div class="frame-header-div">
<div
class="next-to-eachother label-slots-struct-wrapper"
Expand Down Expand Up @@ -56,6 +56,7 @@ export default Vue.extend({
frameAllowChildren: Boolean,
erroneous: Boolean,
wasLastRuntimeError: Boolean,
onFocus: Function, // Handler for focus/blur the header (see Frame.vue)
},

computed:{
Expand Down
48 changes: 33 additions & 15 deletions src/components/Menu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,8 @@
import Vue from "vue";
import { useStore } from "@/store/store";
import {saveContentToFile, readFileContent, fileNameRegex, strypeFileExtension, isMacOSPlatform} from "@/helpers/common";
import { AppEvent, FormattedMessage, FormattedMessageArgKeyValuePlaceholders, Locale, MessageDefinitions, MIMEDesc, PythonExecRunningState, SaveRequestReason, SlotCoreInfos, SlotCursorInfos, SlotType, StrypeSyncTarget } from "@/types/types";
import { countEditorCodeErrors, CustomEventTypes, fileImportSupportedFormats, getAppSimpleMsgDlgId, getEditorCodeErrorsHTMLElements, getEditorMenuUID, getFrameUID, getLabelSlotUID, getNearestErrorIndex, getSaveAsProjectModalDlg, isElementEditableLabelSlotInput, isElementUIDFrameHeader, parseFrameHeaderUID, parseLabelSlotUID, setDocumentSelection } from "@/helpers/editor";
import { AppEvent, CaretPosition, FormattedMessage, FormattedMessageArgKeyValuePlaceholders, Locale, MessageDefinitions, MIMEDesc, PythonExecRunningState, SaveRequestReason, SlotCoreInfos, SlotCursorInfos, SlotType, StrypeSyncTarget } from "@/types/types";
import { countEditorCodeErrors, CustomEventTypes, fileImportSupportedFormats, getAppSimpleMsgDlgId, getEditorCodeErrorsHTMLElements, getEditorMenuUID, getFrameHeaderUID, getFrameUID, getLabelSlotUID, getNearestErrorIndex, getSaveAsProjectModalDlg, isElementEditableLabelSlotInput, isElementUIDFrameHeader, isIdAFrameId, parseFrameHeaderUID, parseFrameUID, parseLabelSlotUID, setDocumentSelection } from "@/helpers/editor";
import { Slide } from "vue-burger-menu";
import { mapStores } from "pinia";
import GoogleDrive from "@/components/GoogleDrive.vue";
Expand All @@ -143,6 +143,7 @@ import { watch } from "@vue/composition-api";
import { cloneDeep } from "lodash";
import App from "@/App.vue";
import appPackageJson from "@/../package.json";
import { getAboveFrameCaretPosition } from "@/helpers/storeMethods";

//////////////////////
// Component //
Expand Down Expand Up @@ -789,10 +790,11 @@ export default Vue.extend({
},


goToError(event: MouseEvent, toNext: boolean){
goToError(event: MouseEvent | null, toNext: boolean){
// Move to the next error (if toNext is true) or the previous error (if toNext is false) when the user clicks on the navigation icon.
// If the icon is "disabled" we do nothing.
if(!(event.target as HTMLElement).classList.contains("error-nav-disabled")){
// Note that a null event is set by a programmatical call of this method.
if(event == null || !(event.target as HTMLElement).classList.contains("error-nav-disabled")){
this.$nextTick(() => {
// If we are currently in a slot, we need to make sure that that slot gets notified of the caret lost
if(this.appStore.focusSlotCursorInfos){
Expand All @@ -806,21 +808,37 @@ export default Vue.extend({
const isFullIndex = ((this.currentErrorNavIndex % 1) == 0);
this.currentErrorNavIndex += (((toNext) ? 1 : -1) / ((isFullIndex) ? 1 : 2));
const errorElement = getEditorCodeErrorsHTMLElements()[this.currentErrorNavIndex];
// We focus on the slot of the error -- if the erroneous HTML is a slot, we just give it focus. If the error is at the frame scope
// The error can be in a slot or it can be for a whole frame. By convention, the location for a frame error is the caret above it.
// For errors in a slot: we focus on the slot of the error -- if the erroneous HTML is a slot, we just give it focus. If the error is at the frame scope
// we put the focus in the first slot that is editable.
const errorSlotInfos: SlotCoreInfos = (isElementEditableLabelSlotInput(errorElement))
? parseLabelSlotUID(errorElement.id)
: {frameId: parseFrameHeaderUID(errorElement.id), labelSlotsIndex: 0, slotId: "0", slotType: SlotType.code};
const errorSlotCursorInfos: SlotCursorInfos = {slotInfos: errorSlotInfos, cursorPos: 0};
this.appStore.setSlotTextCursors(errorSlotCursorInfos, errorSlotCursorInfos);
setDocumentSelection(errorSlotCursorInfos, errorSlotCursorInfos);
// It's necessary to programmatically click the slot we gave focus to, so we can toggle the edition mode event chain
if(isElementUIDFrameHeader(errorElement.id)){
document.getElementById(getLabelSlotUID(errorSlotInfos))?.click();
if(isIdAFrameId(errorElement.id)){
// Error on a whole frame - the error message will be on the header so we need to focus it to trigger the popup.
if(this.appStore.isEditing) {
this.appStore.isEditing = false;
this.appStore.setSlotTextCursors(undefined, undefined);
document.getSelection()?.removeAllRanges();
}
const caretPosAboveFrame = getAboveFrameCaretPosition(parseFrameUID(errorElement.id));
this.appStore.setCurrentFrame({id: caretPosAboveFrame.frameId, caretPosition: caretPosAboveFrame.caretPosition as CaretPosition});
document.getElementById(getFrameHeaderUID(parseFrameUID(errorElement.id)))?.focus();
}
else{
errorElement.click();
// Error on a slot
const errorSlotInfos: SlotCoreInfos = (isElementEditableLabelSlotInput(errorElement))
? parseLabelSlotUID(errorElement.id)
: {frameId: parseFrameHeaderUID(errorElement.id), labelSlotsIndex: 0, slotId: "0", slotType: SlotType.code};
const errorSlotCursorInfos: SlotCursorInfos = {slotInfos: errorSlotInfos, cursorPos: 0};
this.appStore.setSlotTextCursors(errorSlotCursorInfos, errorSlotCursorInfos);
setDocumentSelection(errorSlotCursorInfos, errorSlotCursorInfos);
// It's necessary to programmatically click the slot we gave focus to, so we can toggle the edition mode event chain
if(isElementUIDFrameHeader(errorElement.id)){
document.getElementById(getLabelSlotUID(errorSlotInfos))?.click();
}
else{
errorElement.click();
}
}

this.navigateToErrorRequested = false;
});
}
Expand Down
32 changes: 8 additions & 24 deletions src/components/PythonExecutionArea.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
<div class="flex-padding"/>
<button ref="runButton" @click="runClicked" :title="$t((isPythonExecuting) ? 'PEA.stop' : 'PEA.run') + ' (Ctrl+Enter)'">
<img v-if="!isPythonExecuting" src="favicon.png" class="pea-play-img">
<span v-else class="python-running">{{this.runCodeButtonIconText}}</span>
<span>{{this.runCodeButtonLabel}}</span>
<span v-else class="python-running">{{runCodeButtonIconText}}</span>
<span>{{runCodeButtonLabel}}</span>
</button>
</div>
<div id="tabContentContainerDiv">
Expand Down Expand Up @@ -46,9 +46,10 @@ import { useStore } from "@/store/store";
import Parser from "@/parser/parser";
import { execPythonCode } from "@/helpers/execPythonCode";
import { mapStores } from "pinia";
import { checkEditorCodeErrors, computeAddFrameCommandContainerHeight, countEditorCodeErrors, CustomEventTypes, getEditorCodeErrorsHTMLElements, getFrameUID, getLabelSlotUID, hasPrecompiledCodeError, isElementEditableLabelSlotInput, isElementUIDFrameHeader, parseFrameHeaderUID, parseLabelSlotUID, resetAddFrameCommandContainerHeight, setDocumentSelection, setPythonExecAreaExpandButtonPos, setPythonExecutionAreaTabsContentMaxHeight } from "@/helpers/editor";
import { checkEditorCodeErrors, computeAddFrameCommandContainerHeight, countEditorCodeErrors, CustomEventTypes, getEditorCodeErrorsHTMLElements, getFrameUID, getMenuLeftPaneUID, hasPrecompiledCodeError, resetAddFrameCommandContainerHeight, setPythonExecAreaExpandButtonPos, setPythonExecutionAreaTabsContentMaxHeight } from "@/helpers/editor";
import i18n from "@/i18n";
import { PythonExecRunningState, SlotCoreInfos, SlotCursorInfos, SlotType } from "@/types/types";
import { PythonExecRunningState } from "@/types/types";
import Menu from "@/components/Menu.vue";

export default Vue.extend({
name: "PythonExecutionArea",
Expand Down Expand Up @@ -411,26 +412,9 @@ export default Vue.extend({
// but for sanity check, we make sure it's still there
const errors = getEditorCodeErrorsHTMLElements();
if(errors && errors.length > 0){
const errorElement = errors[0];
// We focus on the slot of the error -- if the erroneous HTML is a slot, we just give it focus. If the error is at the frame scope
// we put the focus in the first slot that is editable.
// There might be some other UI events that would restore the frame cursors (i.e. after getting input in the textarea console),
// so we give a bit of time before setting the focus.
const errorSlotInfos: SlotCoreInfos = (isElementEditableLabelSlotInput(errorElement))
? parseLabelSlotUID(errorElement.id)
: {frameId: parseFrameHeaderUID(errorElement.id), labelSlotsIndex: 0, slotId: "0", slotType: SlotType.code};
setTimeout(() => {
const errorSlotCursorInfos: SlotCursorInfos = {slotInfos: errorSlotInfos, cursorPos: 0};
this.appStore.setSlotTextCursors(errorSlotCursorInfos, errorSlotCursorInfos);
setDocumentSelection(errorSlotCursorInfos, errorSlotCursorInfos);
// It's necessary to programmatically click the slot we gave focus to, so we can toggle the edition mode event chain
if(isElementUIDFrameHeader(errorElement.id)){
document.getElementById(getLabelSlotUID(errorSlotInfos))?.click();
}
else{
errorElement.click();
}
}, 200);
// The Strype Menu handles already navigation of errors, so we use it to navigate to the first error...
(this.$root.$children[0].$refs[getMenuLeftPaneUID()] as InstanceType<typeof Menu>).currentErrorNavIndex = -1;
(this.$root.$children[0].$refs[getMenuLeftPaneUID()] as InstanceType<typeof Menu>).goToError(null, true);
}
});
},
Expand Down
Loading
Loading