Skip to content

Commit

Permalink
Merge pull request #322 from PwtKCL/try-struct-error
Browse files Browse the repository at this point in the history
Handles "try" structures wrong syntax (missing "except" or "finally") AND new localisation architecture
  • Loading branch information
neilccbrown authored Nov 26, 2024
2 parents c3dbfa7 + d368636 commit a17a3c2
Show file tree
Hide file tree
Showing 26 changed files with 1,670 additions and 1,524 deletions.
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

0 comments on commit a17a3c2

Please sign in to comment.