diff --git a/jest.config.js b/jest.config.js
index e76b70d..7aae693 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -12,6 +12,7 @@ module.exports = {
'^.+\\.ts?$': 'ts-jest',
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
+ modulePathIgnorePatterns: ['utils'],
setupFilesAfterEnv: [
"@testing-library/jest-native/extend-expect",
"./setup-tests.js"
diff --git a/package.json b/package.json
index 897e916..c2aafe1 100644
--- a/package.json
+++ b/package.json
@@ -50,7 +50,7 @@
"react-native-vector-icons": "^8.1.0",
"rn-fetch-blob-v2": "^0.14.1",
"upper-case-first": "^2.0.2",
- "wollok-ts": "^3.0.6"
+ "wollok-ts": "^3.0.8"
},
"devDependencies": {
"@babel/core": "^7.12.9",
diff --git a/src/__tests__/App-test.tsx b/src/__tests__/App.spec.tsx
similarity index 100%
rename from src/__tests__/App-test.tsx
rename to src/__tests__/App.spec.tsx
diff --git a/src/__tests__/Problems.spec.tsx b/src/__tests__/Problems.spec.tsx
new file mode 100644
index 0000000..749efb0
--- /dev/null
+++ b/src/__tests__/Problems.spec.tsx
@@ -0,0 +1,115 @@
+import { render, RenderAPI } from '@testing-library/react-native'
+import React from 'react'
+import { Node, Problem } from 'wollok-ts/dist/model'
+import { ProblemIcon } from '../components/problems/ProblemIcon'
+import { ProblemReporterButton } from '../components/problems/ProblemReporterButton'
+import { ProblemModal } from '../components/problems/ProblemsModal'
+import { methodFQN } from '../utils/wollok-helpers'
+import ProjectProviderMock from './utils/ProjectProviderMock'
+import {
+ clazz,
+ error,
+ field,
+ method,
+ problem,
+ sentence,
+ singleton,
+ test,
+ warning,
+ wReturn,
+} from './utils/wollokProject'
+
+describe('ProblemIcon', () => {
+ it('is red on error', () => {
+ const rendered = render()
+ expectErrorIcon(rendered)
+ })
+
+ it('is yellow on warning', () => {
+ const rendered = render()
+ expectWarningIcon(rendered)
+ })
+})
+
+describe('ProblemReporterButton', () => {
+ function renderProblemReporterButton(aNode: Node, ...problems: Problem[]) {
+ return render(
+
+
+ ,
+ )
+ }
+
+ it('should not be shown if there is no related problems', () => {
+ const rendered = renderProblemReporterButton(singleton)
+ expect(rendered.toJSON()).toBeNull()
+ })
+
+ it('should show error if there is related warnings and problems', () => {
+ const rendered = renderProblemReporterButton(
+ singleton,
+ warning,
+ problem(singleton),
+ )
+ expectErrorIcon(rendered)
+ })
+
+ describe('should render if there is any problem', () => {
+ function iconExistTest(name: string, node: Node, problem: Problem) {
+ it(name, () => {
+ const rendered = renderProblemReporterButton(node, problem)
+ expect(rendered.toJSON()).not.toBeNull()
+ })
+ }
+
+ iconExistTest('with problem node', singleton, warning)
+
+ iconExistTest('inside method body', method, error)
+
+ iconExistTest('inside test body', test, problem(test.sentences()[0]))
+
+ iconExistTest('inside return expression', wReturn, problem(wReturn.value!))
+ })
+})
+
+describe('ProblemModal', () => {
+ function renderProblemModal(...problems: Problem[]) {
+ return render(
+ ,
+ )
+ }
+
+ describe('should render description for', () => {
+ function problemDescriptionTest(
+ name: string,
+ description: string,
+ problem: Problem,
+ ) {
+ it(name, () => {
+ const { getByText } = renderProblemModal(problem)
+ expect(getByText(description)).toBeDefined()
+ })
+ }
+
+ problemDescriptionTest('singleton', singleton.name!, warning)
+ problemDescriptionTest('class', clazz.name!, problem(clazz))
+ problemDescriptionTest('field', field.name, problem(field))
+ problemDescriptionTest('method', methodFQN(method), problem(method))
+ problemDescriptionTest('sentence', methodFQN(method), problem(sentence))
+ })
+})
+
+function expectErrorIcon({ UNSAFE_getByProps }: RenderAPI) {
+ expect(
+ UNSAFE_getByProps({ icon: 'alert-circle', color: 'red' }),
+ ).toBeDefined()
+}
+
+function expectWarningIcon({ UNSAFE_getByProps }: RenderAPI) {
+ expect(UNSAFE_getByProps({ icon: 'alert', color: 'yellow' })).toBeDefined()
+}
diff --git a/src/__tests__/TestItem-test.tsx b/src/__tests__/TestItem.spec.tsx
similarity index 92%
rename from src/__tests__/TestItem-test.tsx
rename to src/__tests__/TestItem.spec.tsx
index f6df70d..0d2051e 100644
--- a/src/__tests__/TestItem-test.tsx
+++ b/src/__tests__/TestItem.spec.tsx
@@ -1,12 +1,12 @@
-import React from 'react'
import { fireEvent, render } from '@testing-library/react-native'
-import TestItem from '../components/tests/TestItem'
-import { Body, Test } from 'wollok-ts/dist/model'
-import { TestRun } from '../utils/wollok-helpers'
+import React from 'react'
import { ActivityIndicator } from 'react-native-paper'
import { act, ReactTestInstance } from 'react-test-renderer'
+import { Body, Test, WollokException } from 'wollok-ts'
+import TestItem from '../components/tests/TestItem'
import { theme } from '../theme'
-import { WollokException } from 'wollok-ts/dist/interpreter/runtimeModel'
+import { TestRun } from '../utils/wollok-helpers'
+import ProjectProviderMock from './utils/ProjectProviderMock'
const testMock = new Test({
name: 'TEST',
@@ -48,12 +48,14 @@ const renderTest = (runner: () => TestRun = jest.fn()) => {
UNSAFE_queryByProps,
queryByText,
} = render(
- ,
+
+
+ ,
)
return {
runIcon: () => UNSAFE_getByProps({ icon: 'play-circle' }),
diff --git a/src/components/ui/Body/sentences/tests/Variable.spec.tsx b/src/__tests__/Variable.spec.tsx
similarity index 92%
rename from src/components/ui/Body/sentences/tests/Variable.spec.tsx
rename to src/__tests__/Variable.spec.tsx
index 96148ce..5c85aaa 100644
--- a/src/components/ui/Body/sentences/tests/Variable.spec.tsx
+++ b/src/__tests__/Variable.spec.tsx
@@ -1,7 +1,7 @@
import React from 'react'
import { Literal, Variable as VariableModel } from 'wollok-ts/dist/model'
-import { renderWithTheme } from '../../../../../utils/test-helpers'
-import { Variable } from '../Variable'
+import { renderWithTheme } from './utils/test-helpers'
+import { Variable } from '../components/Body/sentences/Variable'
function renderVariable(variable: VariableModel) {
const { UNSAFE_queryByProps, getByText } = renderWithTheme(
diff --git a/src/components/ui/Body/sentences/tests/VisualSentence.spec.tsx b/src/__tests__/VisualSentence.spec.tsx
similarity index 80%
rename from src/components/ui/Body/sentences/tests/VisualSentence.spec.tsx
rename to src/__tests__/VisualSentence.spec.tsx
index 0eacd76..4313337 100644
--- a/src/components/ui/Body/sentences/tests/VisualSentence.spec.tsx
+++ b/src/__tests__/VisualSentence.spec.tsx
@@ -9,12 +9,12 @@ import {
Sentence,
Variable as VariableModel,
} from 'wollok-ts/dist/model'
-import { renderWithTheme } from '../../../../../utils/test-helpers'
-import { Assignment } from '../Assignment'
-import { Return } from '../Return'
-import { Send } from '../Send'
-import { Variable } from '../Variable'
-import { VisualSentence } from '../VisualSentence'
+import { Assignment } from '../components/Body/sentences/Assignment'
+import { Return } from '../components/Body/sentences/Return'
+import { Send } from '../components/Body/sentences/Send'
+import { Variable } from '../components/Body/sentences/Variable'
+import { VisualSentence } from '../components/Body/sentences/VisualSentence'
+import { renderWithTheme } from './utils/test-helpers'
describe('matching sentences with components', () => {
it('should match a send sentence', () => {
diff --git a/src/__tests__/utils/ProjectProviderMock.tsx b/src/__tests__/utils/ProjectProviderMock.tsx
new file mode 100644
index 0000000..e2fd7e4
--- /dev/null
+++ b/src/__tests__/utils/ProjectProviderMock.tsx
@@ -0,0 +1,36 @@
+import React from 'react'
+import { Environment, Problem } from 'wollok-ts/dist/model'
+import { ProjectContext } from '../../context/ProjectProvider'
+import { ParentComponentProp } from '../../utils/type-helpers'
+
+export const initialContext = {
+ project: new Environment({ members: [] }),
+ name: 'Project Test',
+ changed: false,
+ problems: [] as Problem[],
+ actions: {
+ addEntity: jest.fn(),
+ addDescribe: jest.fn(),
+ addMember: jest.fn(),
+ changeMember: jest.fn(),
+ rebuildEnvironment: jest.fn(),
+ runTest: jest.fn(),
+ execution: jest.fn(),
+ save: jest.fn(),
+ },
+}
+
+type InitialProject = Partial
+
+const ProjectProviderMock = ({
+ children,
+ ...props
+}: ParentComponentProp) => {
+ return (
+
+ {children}
+
+ )
+}
+
+export default ProjectProviderMock
diff --git a/src/__tests__/utils/test-helpers.tsx b/src/__tests__/utils/test-helpers.tsx
new file mode 100644
index 0000000..65ebec8
--- /dev/null
+++ b/src/__tests__/utils/test-helpers.tsx
@@ -0,0 +1,14 @@
+import { NavigationContainer } from '@react-navigation/native'
+import { render } from '@testing-library/react-native'
+import React from 'react'
+import { theme } from '../../theme'
+import ProjectProviderMock from './ProjectProviderMock'
+import { OneOrMany } from '../../utils/type-helpers'
+
+export function renderWithTheme(children: OneOrMany) {
+ return render(
+
+ {children}
+ ,
+ )
+}
diff --git a/src/__tests__/utils/wollokProject.ts b/src/__tests__/utils/wollokProject.ts
new file mode 100644
index 0000000..59b8d04
--- /dev/null
+++ b/src/__tests__/utils/wollokProject.ts
@@ -0,0 +1,125 @@
+import {
+ Body,
+ Class,
+ Describe,
+ Environment,
+ Expression,
+ Field,
+ Import,
+ link,
+ Literal,
+ Method,
+ Node,
+ Package,
+ Parameter,
+ Problem,
+ Reference,
+ Return,
+ Send,
+ Singleton,
+ Test,
+ WRE,
+} from 'wollok-ts'
+import { fromJSON } from 'wollok-ts/dist/jsonUtils'
+
+const main = new Package({
+ name: 'main',
+ members: [
+ new Singleton({
+ name: 'pepita',
+ members: [
+ new Field({
+ name: 'energia',
+ isConstant: false,
+ isProperty: true,
+ value: new Literal({ value: 100 }),
+ }),
+ new Method({
+ name: 'come',
+ parameters: [
+ new Parameter({
+ name: 'comida',
+ }),
+ ],
+ body: new Body({
+ sentences: [
+ new Send({
+ receiver: new Reference({ name: 'comida' }),
+ message: 'energiaQueAporta',
+ }),
+ ],
+ }),
+ }),
+ ],
+ }),
+ new Class({
+ name: 'Entrenador',
+ }),
+ ],
+})
+
+const tests = new Package({
+ name: 'tests',
+ imports: [
+ new Import({
+ entity: new Reference({ name: 'main' }),
+ isGeneric: true,
+ }),
+ ],
+ members: [
+ new Describe({
+ name: 'Main Describe',
+ members: [
+ new Test({
+ id: 'TEST',
+ name: 'test for testing',
+ body: new Body({
+ sentences: [
+ new Send({
+ receiver: new Reference({ name: 'assert' }),
+ message: 'that',
+ args: [new Literal({ value: true })],
+ }),
+ ],
+ }),
+ }),
+ ],
+ }),
+ ],
+})
+
+export const project = link([main, tests], fromJSON(WRE))
+
+export const singleton = project.getNodeByFQN('main.pepita') as Singleton
+
+export const clazz = project.getNodeByFQN('main.Entrenador') as Singleton
+
+export const describe = project.getNodeByFQN('tests.Main Describe') as Describe
+
+export const field = singleton.members[0] as Field
+
+export const method = singleton.members[1] as Method
+
+export const test = describe.members[0] as Test
+
+export const sentence = method.sentences()[0]
+
+export const wReturn = new Return({ value: sentence as Expression })
+
+// PROBLEMS
+
+export const problem = (node: Node): Problem => ({
+ node,
+ code: 'ERROR',
+ level: 'error',
+ values: [],
+})
+
+export const error = problem(sentence)
+
+export const warning: Problem = {
+ node: singleton,
+ code: 'SINGLETON_WARNING',
+ level: 'warning',
+ values: [],
+}
diff --git a/src/components/ui/Body/AssignmentFormModal.tsx b/src/components/Body/AssignmentFormModal.tsx
similarity index 87%
rename from src/components/ui/Body/AssignmentFormModal.tsx
rename to src/components/Body/AssignmentFormModal.tsx
index cf1f4df..d61176d 100644
--- a/src/components/ui/Body/AssignmentFormModal.tsx
+++ b/src/components/Body/AssignmentFormModal.tsx
@@ -10,10 +10,10 @@ import {
Reference,
Variable,
} from 'wollok-ts/dist/model'
-import { wTranslate } from '../../../utils/translation-helpers'
-import { Referenciable } from '../../../utils/wollok-helpers'
-import ExpressionInput from '../ExpressionInput'
-import FormModal from '../FormModal/FormModal'
+import { wTranslate } from '../../utils/translation-helpers'
+import { Referenciable } from '../../utils/wollok-helpers'
+import ExpressionInput from '../ui/ExpressionInput'
+import FormModal from '../ui/FormModal/FormModal'
type AssignmentFormModalProps = {
variables: Referenciable[]
@@ -62,7 +62,7 @@ export function AssignmentFormModal({
diff --git a/src/components/ui/Body/BodyMaker.tsx b/src/components/Body/BodyMaker.tsx
similarity index 86%
rename from src/components/ui/Body/BodyMaker.tsx
rename to src/components/Body/BodyMaker.tsx
index afcc972..5afb1ed 100644
--- a/src/components/ui/Body/BodyMaker.tsx
+++ b/src/components/Body/BodyMaker.tsx
@@ -2,19 +2,13 @@ import { useNavigation } from '@react-navigation/native'
import React, { useState } from 'react'
import { ScrollView, StyleSheet } from 'react-native'
import { upperCaseFirst } from 'upper-case-first'
-import {
- Body,
- Expression,
- List,
- Name,
- Return,
- Sentence,
-} from 'wollok-ts/dist/model'
-import { ExpressionOnSubmit } from '../../../pages/ExpressionMaker/ExpressionMaker'
-import { wTranslate } from '../../../utils/translation-helpers'
-import { Referenciable } from '../../../utils/wollok-helpers'
-import MultiFabScreen from '../../FabScreens/MultiFabScreen'
-import { SubmitCheckButton } from '../Header'
+import { Body, Expression, Name, Return, Sentence } from 'wollok-ts/dist/model'
+import { List } from 'wollok-ts/dist/extensions'
+import { ExpressionOnSubmit } from '../../pages/ExpressionMaker'
+import { wTranslate } from '../../utils/translation-helpers'
+import { Referenciable } from '../../utils/wollok-helpers'
+import MultiFabScreen from '../FabScreens/MultiFabScreen'
+import { SubmitCheckButton } from '../ui/Header'
import { AssignmentFormModal } from './AssignmentFormModal'
import { returnIcon as returnIconName } from './sentences/Return'
import { VisualSentence } from './sentences/VisualSentence'
diff --git a/src/components/ui/Body/VariableForm.tsx b/src/components/Body/VariableForm.tsx
similarity index 83%
rename from src/components/ui/Body/VariableForm.tsx
rename to src/components/Body/VariableForm.tsx
index eb90d88..9d3520d 100644
--- a/src/components/ui/Body/VariableForm.tsx
+++ b/src/components/Body/VariableForm.tsx
@@ -2,11 +2,11 @@ import React, { useState } from 'react'
import { Text, TextInput } from 'react-native-paper'
import { upperCaseFirst } from 'upper-case-first'
import { Expression, Name, Variable } from 'wollok-ts/dist/model'
-import { wTranslate } from '../../../utils/translation-helpers'
-import CheckIcon from '../CheckIcon'
-import ExpressionInput from '../ExpressionInput'
-import FormModal from '../FormModal/FormModal'
-import { Row } from '../Row'
+import { wTranslate } from '../../utils/translation-helpers'
+import CheckIcon from '../ui/CheckIcon'
+import ExpressionInput from '../ui/ExpressionInput'
+import FormModal from '../ui/FormModal/FormModal'
+import { Row } from '../ui/Row'
type VariableFormModalProps = {
onSubmit: (assignment: Variable) => void
@@ -49,7 +49,7 @@ export function VariableFormModal({
diff --git a/src/components/ui/Body/sentences/Assignment.tsx b/src/components/Body/sentences/Assignment.tsx
similarity index 58%
rename from src/components/ui/Body/sentences/Assignment.tsx
rename to src/components/Body/sentences/Assignment.tsx
index 04459d5..7fc2ada 100644
--- a/src/components/ui/Body/sentences/Assignment.tsx
+++ b/src/components/Body/sentences/Assignment.tsx
@@ -1,13 +1,15 @@
import React from 'react'
import { Assignment as AssignmentModel } from 'wollok-ts/dist/model'
-import { ReferenceSegment } from '../../../expressions/expression-segment'
-import { display } from '../../../expressions/ExpressionDisplay'
-import { Row } from '../../Row'
+import { ReferenceSegment } from '../../expressions/expression-segment'
+import { display } from '../../expressions/ExpressionDisplay'
+import { ProblemReporterButton } from '../../problems/ProblemReporterButton'
+import { Row } from '../../ui/Row'
import { ReferenceExpression } from './ReferenceExpression'
export const Assignment = ({ assignment }: { assignment: AssignmentModel }) => {
return (
+
diff --git a/src/components/ui/Body/sentences/ReferenceExpression.tsx b/src/components/Body/sentences/ReferenceExpression.tsx
similarity index 65%
rename from src/components/ui/Body/sentences/ReferenceExpression.tsx
rename to src/components/Body/sentences/ReferenceExpression.tsx
index 14168fd..67d20a6 100644
--- a/src/components/ui/Body/sentences/ReferenceExpression.tsx
+++ b/src/components/Body/sentences/ReferenceExpression.tsx
@@ -1,8 +1,8 @@
import React from 'react'
import { IconButton } from 'react-native-paper'
-import { Expression } from 'wollok-ts'
-import { ExpressionDisplay } from '../../../expressions/ExpressionDisplay'
-import { Row } from '../../Row'
+import { Expression } from 'wollok-ts/dist/model'
+import { ExpressionDisplay } from '../../expressions/ExpressionDisplay'
+import { Row } from '../../ui/Row'
export const ReferenceExpression = (props: { expression: Expression }) => {
return (
diff --git a/src/components/ui/Body/sentences/Return.tsx b/src/components/Body/sentences/Return.tsx
similarity index 62%
rename from src/components/ui/Body/sentences/Return.tsx
rename to src/components/Body/sentences/Return.tsx
index 573b79a..df5b6f3 100644
--- a/src/components/ui/Body/sentences/Return.tsx
+++ b/src/components/Body/sentences/Return.tsx
@@ -1,9 +1,10 @@
import React from 'react'
import { IconButton } from 'react-native-paper'
import { Return as ReturnModel } from 'wollok-ts/dist/model'
-import { useTheme } from '../../../../theme'
-import { ExpressionDisplay } from '../../../expressions/ExpressionDisplay'
-import { Row } from '../../Row'
+import { useTheme } from '../../../theme'
+import { ExpressionDisplay } from '../../expressions/ExpressionDisplay'
+import { ProblemReporterButton } from '../../problems/ProblemReporterButton'
+import { Row } from '../../ui/Row'
export const returnIcon = 'arrow-expand-up'
@@ -11,6 +12,7 @@ export function Return(props: { returnSentence: ReturnModel }) {
const theme = useTheme()
return (
+
{
+ return (
+
+
+
+
+ )
+}
diff --git a/src/components/ui/Body/sentences/Variable.tsx b/src/components/Body/sentences/Variable.tsx
similarity index 64%
rename from src/components/ui/Body/sentences/Variable.tsx
rename to src/components/Body/sentences/Variable.tsx
index 7b99bdd..094a747 100644
--- a/src/components/ui/Body/sentences/Variable.tsx
+++ b/src/components/Body/sentences/Variable.tsx
@@ -1,16 +1,18 @@
import React from 'react'
import { IconButton, Text } from 'react-native-paper'
import { Variable as VariableModel } from 'wollok-ts/dist/model'
-import { useTheme } from '../../../../theme'
-import { isNullExpression } from '../../../../utils/wollok-helpers'
-import { ConstantVariableIcon } from '../../ConstantVariableIcon'
-import { Row } from '../../Row'
+import { useTheme } from '../../../theme'
+import { isNullExpression } from '../../../utils/wollok-helpers'
+import { ProblemReporterButton } from '../../problems/ProblemReporterButton'
+import { ConstantVariableIcon } from '../../ui/ConstantVariableIcon'
+import { Row } from '../../ui/Row'
import { ReferenceExpression } from './ReferenceExpression'
export const Variable = (props: { variable: VariableModel }) => {
const theme = useTheme()
return (
+
{props.variable.name}
diff --git a/src/components/ui/Body/sentences/VisualSentence.tsx b/src/components/Body/sentences/VisualSentence.tsx
similarity index 88%
rename from src/components/ui/Body/sentences/VisualSentence.tsx
rename to src/components/Body/sentences/VisualSentence.tsx
index aed8690..b88be21 100644
--- a/src/components/ui/Body/sentences/VisualSentence.tsx
+++ b/src/components/Body/sentences/VisualSentence.tsx
@@ -1,12 +1,13 @@
import React from 'react'
import { Text } from 'react-native-paper'
import { Sentence } from 'wollok-ts/dist/model'
-import { wTranslate } from '../../../../utils/translation-helpers'
-import { Row } from '../../Row'
+import { wTranslate } from '../../../utils/translation-helpers'
+import { Row } from '../../ui/Row'
import { Assignment } from './Assignment'
import { Return } from './Return'
import { Send } from './Send'
import { Variable } from './Variable'
+
export function VisualSentence({ sentence }: { sentence: Sentence }) {
switch (sentence.kind) {
case 'Send':
diff --git a/src/components/entities/Entity/Entity.tsx b/src/components/entities/Entity/Entity.tsx
index 01e9b87..52260a5 100644
--- a/src/components/entities/Entity/Entity.tsx
+++ b/src/components/entities/Entity/Entity.tsx
@@ -1,34 +1,31 @@
-import { useNavigation } from '@react-navigation/native'
import React from 'react'
import { List, withTheme } from 'react-native-paper'
import { Module } from 'wollok-ts/dist/model'
-import { HomeScreenNavigationProp } from '../../../pages/Home'
+import { useNodeNavigation } from '../../../context/NodeNavigation'
import { Theme } from '../../../theme'
+import { ProblemReporterButton } from '../../problems/ProblemReporterButton'
import { EntityKindIcon } from '../EntityKindIcon'
import { stylesheet } from './styles'
-type Props = {
+type EntityComponentProps = {
entity: Module
theme: Theme
}
-function EntityComponent(props: Props) {
- const styles = stylesheet(props.theme)
- const navigation = useNavigation()
- const goToEntityDetails = () => {
- navigation.navigate('EntityStack', {
- entityFQN: props.entity.fullyQualifiedName(),
- })
- }
+function EntityComponent({ entity, theme }: EntityComponentProps) {
+ const styles = stylesheet(theme)
+ const { goToNode } = useNodeNavigation()
+ const goToEntityDetails = () => goToNode(entity)
return (
}
+ title={entity.name}
+ left={() => }
+ right={() => }
/>
)
}
diff --git a/src/components/entity-detail/AccordionList.tsx b/src/components/entity-detail/AccordionList.tsx
index eb86ad3..8fdceba 100644
--- a/src/components/entity-detail/AccordionList.tsx
+++ b/src/components/entity-detail/AccordionList.tsx
@@ -2,7 +2,7 @@ import { Theme, useTheme } from '@react-navigation/native'
import React, { Key, useState } from 'react'
import { StyleSheet, View } from 'react-native'
import { Divider, List as ListComponent } from 'react-native-paper'
-import { List } from 'wollok-ts/dist/model'
+import { List } from 'wollok-ts/dist/extensions'
type Props- = {
title: string
diff --git a/src/components/entity-detail/AttributeItem/AttributeItem.tsx b/src/components/entity-detail/AttributeItem/AttributeItem.tsx
index 9b8d6d6..8c85481 100644
--- a/src/components/entity-detail/AttributeItem/AttributeItem.tsx
+++ b/src/components/entity-detail/AttributeItem/AttributeItem.tsx
@@ -3,14 +3,14 @@ import { List, withTheme } from 'react-native-paper'
import { Field } from 'wollok-ts/dist/model'
import { Theme } from '../../../theme'
import { ExpressionDisplay } from '../../expressions/ExpressionDisplay'
+import { ProblemReporterButton } from '../../problems/ProblemReporterButton'
import { ATTRIBUTE_ICONS } from '../attribute-icons'
import styles from './styles'
function AttributeItem(props: { attribute: Field; theme: Theme }) {
- const {
- attribute: { isProperty, isConstant, name, value },
- theme,
- } = props
+ const { attribute, theme } = props
+
+ const { isProperty, isConstant, name, value } = attribute
const icons = [
{
@@ -26,6 +26,7 @@ function AttributeItem(props: { attribute: Field; theme: Theme }) {
return (
}
description={() => value && }
right={() =>
icons
diff --git a/src/pages/EntityDetails/EntityDetails.tsx b/src/components/entity-detail/ModuleDetails.tsx
similarity index 58%
rename from src/pages/EntityDetails/EntityDetails.tsx
rename to src/components/entity-detail/ModuleDetails.tsx
index a7c1e1e..8ff4795 100644
--- a/src/pages/EntityDetails/EntityDetails.tsx
+++ b/src/components/entity-detail/ModuleDetails.tsx
@@ -3,21 +3,28 @@ import React, { useState } from 'react'
import { ScrollView } from 'react-native-gesture-handler'
import { List } from 'react-native-paper'
import { upperCaseFirst } from 'upper-case-first'
-import { Field, is, Method } from 'wollok-ts/dist/model'
-import { AccordionList } from '../../components/entity-detail/AccordionList'
-import AttributeItemComponent from '../../components/entity-detail/AttributeItem/AttributeItem'
-import NewAttributeModal from '../../components/entity-detail/new-attribute-modal/NewAttributeModal'
-import NewMethodModal from '../../components/entity-detail/new-method-modal/NewMethodModal'
-import MultiFabScreen from '../../components/FabScreens/MultiFabScreen'
-import { useEntity } from '../../context/EntityProvider'
+import { Describe, Field, is, Method, Module } from 'wollok-ts/dist/model'
+import { AccordionList } from './AccordionList'
+import AttributeItemComponent from './AttributeItem/AttributeItem'
+import NewAttributeModal from './new-attribute-modal/NewAttributeModal'
+import NewMethodModal from './new-method-modal/NewMethodModal'
+import MultiFabScreen from '../FabScreens/MultiFabScreen'
+import { ProblemReporterButton } from '../problems/ProblemReporterButton'
+import { useProject } from '../../context/ProjectProvider'
import { wTranslate } from '../../utils/translation-helpers'
import { methodFQN, methodLabel } from '../../utils/wollok-helpers'
-import { EntityMemberScreenNavigationProp } from '../EntityMemberDetail'
+import { EditorScreenNavigationProp } from '../../pages/Editor'
-export const EntityDetails = function () {
+export type ModuleDetailsProps = {
+ module: Exclude
+}
+
+export const ModuleDetails = function ({ module }: ModuleDetailsProps) {
const [methodModalVisible, setMethodModalVisible] = useState(false)
const [attributeModalVisible, setAttributeModalVisible] = useState(false)
- const { entity } = useEntity()
+ const {
+ actions: { addMember },
+ } = useProject()
return (
title={wTranslate('entityDetails.attributes').toUpperCase()}
- items={entity.members.filter(is('Field')) as Field[]}
+ items={module.members.filter(is('Field')) as Field[]}
VisualItem={AttributeItem}
initialExpanded={true}
/>
title={wTranslate('entityDetails.methods').toUpperCase()}
- items={entity.members.filter(is('Method')) as Method[]}
+ items={module.members.filter(is('Method')) as Method[]}
VisualItem={MethodItem}
initialExpanded={true}
/>
@@ -52,10 +59,13 @@ export const EntityDetails = function () {
)
@@ -66,17 +76,18 @@ function AttributeItem({ item: attribute }: { item: Field }) {
}
function MethodItem({ item: method }: { item: Method }) {
- const navigator = useNavigation()
+ const navigator = useNavigation()
+ function gotoMethod() {
+ navigator.navigate('Editor', {
+ fqn: methodFQN(method),
+ })
+ }
return (
- navigator.navigate('EntityMemberDetails', {
- entityMember: method,
- fqn: methodFQN(method),
- })
- }
+ left={() => }
+ onPress={gotoMethod}
/>
)
}
diff --git a/src/components/entity-detail/new-attribute-modal/NewAttributeModal.tsx b/src/components/entity-detail/new-attribute-modal/NewAttributeModal.tsx
index 416c883..698fe28 100644
--- a/src/components/entity-detail/new-attribute-modal/NewAttributeModal.tsx
+++ b/src/components/entity-detail/new-attribute-modal/NewAttributeModal.tsx
@@ -1,34 +1,30 @@
import React, { useState } from 'react'
import { StyleSheet, View } from 'react-native'
-import { Text, TextInput, withTheme } from 'react-native-paper'
+import { Text, TextInput } from 'react-native-paper'
import { upperCaseFirst } from 'upper-case-first'
import { Expression, Field } from 'wollok-ts/dist/model'
-import { useEntity } from '../../../context/EntityProvider'
-import { Theme } from '../../../theme'
import { wTranslate } from '../../../utils/translation-helpers'
+import { Visible } from '../../../utils/type-helpers'
import CheckIcon from '../../ui/CheckIcon'
import ExpressionInput from '../../ui/ExpressionInput'
import FormModal from '../../ui/FormModal/FormModal'
import { ATTRIBUTE_ICONS } from '../attribute-icons'
-type Props = {
- visible: boolean
- setVisible: (visible: boolean) => void
- theme: Theme
+type AttributeFormModalProps = Visible & {
+ addNewField: (f: Field) => void
+ contextFQN: string
}
-const AttributeFormModal = (props: Props) => {
- const {
- actions: { addMember },
- entity,
- } = useEntity()
+const AttributeFormModal = ({
+ visible,
+ setVisible,
+ addNewField,
+ contextFQN,
+}: AttributeFormModalProps) => {
const [name, setName] = useState('')
const [isConstant, setConstant] = useState(false)
const [isProperty, setProperty] = useState(false)
const [initialValue, setInitialValue] = useState()
- const { visible, setVisible } = props
-
- const styles = getStyles(props.theme)
const checkboxes = [
{
@@ -68,7 +64,7 @@ const AttributeFormModal = (props: Props) => {
{
}
function newAttribute() {
- addMember(new Field({ name, isConstant, isProperty, value: initialValue }))
+ addNewField(
+ new Field({ name, isConstant, isProperty, value: initialValue }),
+ )
}
}
-const getStyles = (_theme: Theme) =>
- StyleSheet.create({
- checkbox: { flexDirection: 'row', alignItems: 'center', marginVertical: 5 },
- constName: { fontSize: 16 },
- })
+const styles = StyleSheet.create({
+ checkbox: { flexDirection: 'row', alignItems: 'center', marginVertical: 5 },
+ constName: { fontSize: 16 },
+})
-export default withTheme(AttributeFormModal)
+export default AttributeFormModal
diff --git a/src/components/entity-detail/new-method-modal/NewMethodModal.tsx b/src/components/entity-detail/new-method-modal/NewMethodModal.tsx
index febce88..b97cd7e 100644
--- a/src/components/entity-detail/new-method-modal/NewMethodModal.tsx
+++ b/src/components/entity-detail/new-method-modal/NewMethodModal.tsx
@@ -1,22 +1,20 @@
import React, { useState } from 'react'
import { StyleSheet } from 'react-native'
-import { Text, TextInput, withTheme } from 'react-native-paper'
+import { Text, TextInput } from 'react-native-paper'
import { upperCaseFirst } from 'upper-case-first'
import { Body, Method, Parameter } from 'wollok-ts/dist/model'
-import { useEntity } from '../../../context/EntityProvider'
-import { Theme } from '../../../theme'
import { wTranslate } from '../../../utils/translation-helpers'
+import { Visible } from '../../../utils/type-helpers'
import FormModal from '../../ui/FormModal/FormModal'
import ParameterInput from './ParameterInput'
-const NewMethodModal = (props: {
- visible: boolean
- setVisible: (value: boolean) => void
- theme: Theme
-}) => {
- const {
- actions: { addMember },
- } = useEntity()
+type NewMethodModalProps = Visible & { addNewMethod: (m: Method) => void }
+
+const NewMethodModal = ({
+ visible,
+ setVisible,
+ addNewMethod,
+}: NewMethodModalProps) => {
const [name, setName] = useState('')
const [parameters, setParameters] = useState([])
const [nextParameter, setNextParameter] = useState('')
@@ -26,8 +24,8 @@ const NewMethodModal = (props: {
title={wTranslate('entityDetails.methodModal.newMethod')}
resetForm={reset}
onSubmit={newMethod}
- setVisible={props.setVisible}
- visible={props.visible}>
+ setVisible={setVisible}
+ visible={visible}>
void
+}
+
+export function ProblemIcon({ problem, onPress }: ProblemIconProps) {
+ return problem.level === 'error' ? (
+
+ ) : (
+
+ )
+}
diff --git a/src/components/problems/ProblemReporterButton.tsx b/src/components/problems/ProblemReporterButton.tsx
new file mode 100644
index 0000000..1bd1474
--- /dev/null
+++ b/src/components/problems/ProblemReporterButton.tsx
@@ -0,0 +1,52 @@
+import React, { useState } from 'react'
+import { Node, Problem } from 'wollok-ts/dist/model'
+import { useProject } from '../../context/ProjectProvider'
+import { isError } from '../../utils/wollok-helpers'
+import { ProblemIcon } from './ProblemIcon'
+import { ProblemModal } from './ProblemsModal'
+
+interface ProblemReporterButtonProps {
+ node: Node
+ icon?: JSX.Element
+}
+
+export function ProblemReporterButton({
+ node,
+ icon,
+}: ProblemReporterButtonProps) {
+ const [showProblems, setShowProblems] = useState(false)
+ const { problems } = useProject()
+
+ const belongsTo = (problem: Problem): boolean =>
+ node.match({
+ Method: m => problem.node.ancestors().includes(m),
+ Test: t => problem.node.ancestors().includes(t),
+ Return: r => r.id === problem.node.id || r.value?.id === problem.node.id,
+ Node: n => n.id === problem.node.id,
+ })
+
+ const nodeProblems = problems.filter(belongsTo)
+
+ if (!nodeProblems.length) {
+ return null
+ }
+
+ const maybeError = nodeProblems.find(isError)
+
+ return (
+ <>
+ {icon || (
+ setShowProblems(true)}
+ />
+ )}
+
+
+ >
+ )
+}
diff --git a/src/components/problems/ProblemsModal.tsx b/src/components/problems/ProblemsModal.tsx
new file mode 100644
index 0000000..58a3328
--- /dev/null
+++ b/src/components/problems/ProblemsModal.tsx
@@ -0,0 +1,50 @@
+import React from 'react'
+import { ScrollView } from 'react-native'
+import { List } from 'react-native-paper'
+import { Node, Problem } from 'wollok-ts/dist/model'
+import { wTranslate } from '../../utils/translation-helpers'
+import { methodFQN } from '../../utils/wollok-helpers'
+import FormModal, { FormModalProps } from '../ui/FormModal/FormModal'
+import { ProblemIcon } from './ProblemIcon'
+
+interface ProblemsModalProp {
+ problems: Problem[]
+ onSelect?: (p: Problem) => void
+}
+
+export function ProblemModal({
+ problems,
+ onSelect,
+ visible,
+ setVisible,
+}: ProblemsModalProp & Pick) {
+ const nodeDescription = (n: Node): string | undefined =>
+ n.match({
+ Entity: s => s.name,
+ Field: f => f.name,
+ Method: methodFQN,
+ Test: t => t.name,
+ Body: b => nodeDescription(b.parent),
+ Sentence: a => nodeDescription(a.parent),
+ })
+
+ return (
+ setVisible(false)}>
+
+ {problems.map((problem, i) => (
+ onSelect && onSelect(problem)}
+ title={wTranslate(`problem.${problem.code}`)}
+ titleNumberOfLines={2}
+ left={() => }
+ description={nodeDescription(problem.node)}
+ />
+ ))}
+
+
+ )
+}
diff --git a/src/components/select-project/NewProjectModal.tsx b/src/components/projects/NewProjectModal.tsx
similarity index 100%
rename from src/components/select-project/NewProjectModal.tsx
rename to src/components/projects/NewProjectModal.tsx
diff --git a/src/components/projects/ProjectHeader.tsx b/src/components/projects/ProjectHeader.tsx
new file mode 100644
index 0000000..8f7af33
--- /dev/null
+++ b/src/components/projects/ProjectHeader.tsx
@@ -0,0 +1,52 @@
+import React, { useState } from 'react'
+import { Badge, IconButton } from 'react-native-paper'
+import { Node } from 'wollok-ts/dist/model'
+import { useNodeNavigation } from '../../context/NodeNavigation'
+import { useProject } from '../../context/ProjectProvider'
+import { ProblemModal } from '../problems/ProblemsModal'
+import { Row } from '../ui/Row'
+
+interface ProjectHeaderProp {
+ pushMessage: (tag: string) => void
+}
+
+export function ProjectHeader({ pushMessage }: ProjectHeaderProp) {
+ const [showProblems, setShowProblems] = useState(false)
+ const {
+ changed,
+ problems,
+ actions: { save },
+ } = useProject()
+
+ const { goToNode } = useNodeNavigation()
+
+ const goto = (n: Node): void => {
+ goToNode(n)
+ setShowProblems(false)
+ }
+
+ return (
+
+ {
+ setShowProblems(true)
+ }}
+ />
+ {problems.length}
+ save().then(() => pushMessage('saved'))}
+ />
+
+ goto(p.node)}
+ />
+
+ )
+}
diff --git a/src/components/tests/DescribeItem.tsx b/src/components/tests/DescribeItem.tsx
index ba4133b..d34e1ab 100644
--- a/src/components/tests/DescribeItem.tsx
+++ b/src/components/tests/DescribeItem.tsx
@@ -1,7 +1,7 @@
-import { useNavigation } from '@react-navigation/native'
import React from 'react'
import { List, withTheme } from 'react-native-paper'
import { Describe } from 'wollok-ts/dist/model'
+import { useNodeNavigation } from '../../context/NodeNavigation'
import { Theme } from '../../theme'
import { stylesheet } from '../entities/Entity/styles'
import IconImage from '../ui/IconImage'
@@ -15,12 +15,8 @@ type Props = {
// TODO: Merge with Entity component
function DescribeItem({ describe, theme }: Props) {
const styles = stylesheet(theme)
- const navigation = useNavigation()
- const goToEntityDetails = () => {
- navigation.navigate('EntityStack', {
- entityFQN: describe.fullyQualifiedName(),
- })
- }
+ const { goToNode } = useNodeNavigation()
+ const goToEntityDetails = () => goToNode(describe)
return (
void
-}) {
- const {
- actions: { addMember },
- } = useEntity()
+type NewTestModalProps = Pick & {
+ addNewTest: (t: Test) => void
+}
+
+function NewTestModal({ visible, setVisible, addNewTest }: NewTestModalProps) {
const [name, setName] = useState(initialName)
return (
+ setVisible={setVisible}
+ visible={visible}>
}
+ left={() => (
+ <>
+
+
+ >
+ )}
right={() => (
<>
{testRun?.exception?.message && (
diff --git a/src/components/tests/Tests.tsx b/src/components/tests/Tests.tsx
index 51494ef..71568af 100644
--- a/src/components/tests/Tests.tsx
+++ b/src/components/tests/Tests.tsx
@@ -2,27 +2,28 @@ import { useNavigation } from '@react-navigation/native'
import React, { useState } from 'react'
import { ScrollView } from 'react-native-gesture-handler'
import { upperCaseFirst } from 'upper-case-first'
-import { is, Test } from 'wollok-ts/dist/model'
-import { useEntity } from '../../context/EntityProvider'
+import { Describe, is, Test } from 'wollok-ts/dist/model'
import { useProject } from '../../context/ProjectProvider'
-import { EntityMemberScreenNavigationProp } from '../../pages/EntityMemberDetail'
+import { EditorScreenNavigationProp } from '../../pages/Editor'
import { wTranslate } from '../../utils/translation-helpers'
import MultiFabScreen from '../FabScreens/MultiFabScreen'
import NewTestModal from './NewTestModal'
import TestItem from './TestItem'
-export const Tests = function () {
+export type TestsProps = {
+ describe: Describe
+}
+
+export const Tests = function ({ describe }: TestsProps) {
const [testNewModalVisible, setTestNewModalVisible] = useState(false)
const {
- actions: { runTest },
+ actions: { runTest, addMember },
} = useProject()
- const { entity } = useEntity()
- const tests = entity.members.filter(is('Test')) as Test[]
+ const tests = describe.members.filter(is('Test')) as Test[]
- const navigator = useNavigation()
+ const navigator = useNavigation()
function navigateTo(test: Test) {
- navigator.navigate('EntityMemberDetails', {
- entityMember: test,
+ navigator.navigate('Editor', {
fqn: test.fullyQualifiedName(),
})
}
@@ -63,6 +64,7 @@ export const Tests = function () {
)
diff --git a/src/components/ui/Body/sentences/Send.tsx b/src/components/ui/Body/sentences/Send.tsx
deleted file mode 100644
index 1e709b5..0000000
--- a/src/components/ui/Body/sentences/Send.tsx
+++ /dev/null
@@ -1,7 +0,0 @@
-import React from 'react'
-import { Send as SendModel } from 'wollok-ts/dist/model'
-import { ExpressionDisplay } from '../../../expressions/ExpressionDisplay'
-
-export const Send = ({ send }: { send: SendModel }) => {
- return
-}
diff --git a/src/components/ui/ConstantVariableIcon.tsx b/src/components/ui/ConstantVariableIcon.tsx
index b5e096e..26824de 100644
--- a/src/components/ui/ConstantVariableIcon.tsx
+++ b/src/components/ui/ConstantVariableIcon.tsx
@@ -1,6 +1,6 @@
import React from 'react'
import { IconButton } from 'react-native-paper'
-import { Variable } from 'wollok-ts'
+import { Variable } from 'wollok-ts/dist/model'
import { useTheme } from '../../theme'
export const ConstantVariableIcon = (props: { variable: Variable }) => {
diff --git a/src/components/ui/ExpressionInput.tsx b/src/components/ui/ExpressionInput.tsx
index b665c1e..99eba36 100644
--- a/src/components/ui/ExpressionInput.tsx
+++ b/src/components/ui/ExpressionInput.tsx
@@ -3,7 +3,7 @@ import React from 'react'
import { StyleSheet, View } from 'react-native'
import { IconButton, Text, withTheme } from 'react-native-paper'
import { Expression, Name } from 'wollok-ts/dist/model'
-import { ExpressionMakerScreenProp } from '../../pages/ExpressionMaker/ExpressionMaker'
+import { ExpressionMakerScreenProp } from '../../pages/ExpressionMaker'
import { Theme } from '../../theme'
import { ExpressionDisplay } from '../expressions/ExpressionDisplay'
@@ -11,7 +11,7 @@ type Props = {
value?: Expression
setValue: (expression?: Expression) => void
theme: Theme
- fqn: Name
+ contextFQN: Name
inputPlaceholder: string
}
@@ -21,7 +21,7 @@ const ExpressionInput = (props: Props) => {
const goToExpressionMaker = () => {
navigation.push('ExpressionMaker', {
onSubmit: setValue,
- contextFQN: props.fqn,
+ contextFQN: props.contextFQN,
initialExpression: value,
})
}
diff --git a/src/components/ui/FormModal/FormModal.tsx b/src/components/ui/FormModal/FormModal.tsx
index 9775eb7..e926404 100644
--- a/src/components/ui/FormModal/FormModal.tsx
+++ b/src/components/ui/FormModal/FormModal.tsx
@@ -9,20 +9,20 @@ import {
} from 'react-native-paper'
import { Theme } from '../../../theme'
import { wTranslate } from '../../../utils/translation-helpers'
-import { ParentComponentProp } from '../../../utils/type-helpers'
+import { ParentComponentProp, Visible } from '../../../utils/type-helpers'
import { stylesheet } from './styles'
-function FormModal(
- props: ParentComponentProp<{
- visible: boolean
- setVisible: (value: boolean) => void
+export type FormModalProps = ParentComponentProp<
+ Visible & {
onSubmit: () => void
resetForm?: () => void
title?: string
valid?: boolean
theme: Theme
- }>,
-) {
+ }
+>
+
+function FormModal(props: FormModalProps) {
const styles = stylesheet(props.theme)
const disabledSubmit = props.valid === undefined ? false : !props.valid
diff --git a/src/context/EntityProvider.tsx b/src/context/EntityProvider.tsx
deleted file mode 100644
index 0596244..0000000
--- a/src/context/EntityProvider.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-import React, { createContext } from 'react'
-import { Module } from 'wollok-ts/dist/model'
-import { ParentComponentProp } from '../utils/type-helpers'
-import { EntityMember } from '../utils/wollok-helpers'
-import { createContextHook } from './create-context-hook'
-import { useProject } from './ProjectProvider'
-
-export const EntityContext = createContext<{
- entity: Module
- actions: Actions
-} | null>(null)
-
-type Actions = {
- addMember: (newMember: EntityMember) => void
- changeMember: (oldMember: EntityMember, newMember: EntityMember) => void
-}
-
-export function EntityProvider(
- props: ParentComponentProp<{
- entity: Module
- }>,
-) {
- const { children, entity } = props
- const {
- actions: { rebuildEnvironment },
- } = useProject()
-
- const addMember = (newMember: EntityMember) => {
- rebuildEnvironment(
- entity.copy({
- members: [...entity.members, newMember],
- }) as Module,
- )
- }
-
- const changeMember = (oldMember: EntityMember, newMember: EntityMember) => {
- rebuildEnvironment(
- entity.copy({
- members: [...entity.members.filter(m => m !== oldMember), newMember],
- }) as Module,
- )
- }
-
- const initialContext = {
- entity: entity,
- actions: { addMember, changeMember },
- }
- return (
-
- {children}
-
- )
-}
-
-export const useEntity = createContextHook(EntityContext, {
- hookName: 'useEntity',
- contextName: 'EntityProvider',
-})
diff --git a/src/context/ExpressionContextProvider.tsx b/src/context/ExpressionContextProvider.tsx
index 19a1d64..fff5446 100644
--- a/src/context/ExpressionContextProvider.tsx
+++ b/src/context/ExpressionContextProvider.tsx
@@ -1,5 +1,6 @@
import React, { createContext, useState } from 'react'
-import { List, Method, Module, Name, Test } from 'wollok-ts/dist/model'
+import { List } from 'wollok-ts/dist/extensions'
+import { Method, Module, Name, Test } from 'wollok-ts/dist/model'
import { ParentComponentProp } from '../utils/type-helpers'
import { Named } from '../utils/wollok-helpers'
import { createContextHook } from './create-context-hook'
diff --git a/src/context/NodeNavigation.tsx b/src/context/NodeNavigation.tsx
new file mode 100644
index 0000000..452a496
--- /dev/null
+++ b/src/context/NodeNavigation.tsx
@@ -0,0 +1,52 @@
+import { useNavigation } from '@react-navigation/native'
+import React, { createContext } from 'react'
+import { Entity, Method, Module, Node, Test } from 'wollok-ts/dist/model'
+import { HomeScreenNavigationProp } from '../pages/Home'
+import { ParentComponentProp } from '../utils/type-helpers'
+import { entityMemberFQN, EntityMemberWithBody } from '../utils/wollok-helpers'
+import { createContextHook } from './create-context-hook'
+
+export type Context = Module | Method | Test
+
+export const ContextContext = createContext<{
+ goToNode: (n: Node) => void
+} | null>(null)
+
+export function NodeNavigationProvider({ children }: ParentComponentProp) {
+ const navigation = useNavigation()
+
+ const goToEntityDetails = (entity: Entity) => {
+ navigation.navigate('EntityDetails', {
+ entityFQN: entity.fullyQualifiedName(),
+ })
+ }
+ const goToEditor = (entityMember: EntityMemberWithBody) => {
+ navigation.navigate('Editor', {
+ fqn: entityMemberFQN(entityMember),
+ })
+ }
+
+ const goToNode = (n: Node): void => {
+ n.match({
+ Method: goToEditor,
+ Test: goToEditor,
+ Describe: goToEntityDetails,
+ Module: goToEntityDetails,
+ Field: f => goToEntityDetails(f.parent),
+ Body: b => goToNode(b.parent),
+ Sentence: e => goToNode(e.parent),
+ Expression: e => goToNode(e.parent),
+ })
+ }
+
+ const init = { goToNode }
+
+ return (
+ {children}
+ )
+}
+
+export const useNodeNavigation = createContextHook(ContextContext, {
+ contextName: 'NodeNavigationProvider',
+ hookName: 'useNodeNavigation',
+})
diff --git a/src/context/ProjectProvider.tsx b/src/context/ProjectProvider.tsx
index 80592ff..164a3ba 100644
--- a/src/context/ProjectProvider.tsx
+++ b/src/context/ProjectProvider.tsx
@@ -10,27 +10,43 @@ import {
Module,
Name,
Package,
+ Problem,
Reference,
Test,
} from 'wollok-ts/dist/model'
+import validate from 'wollok-ts/dist/validator'
+import { saveProject } from '../services/persistance.service'
import { ParentComponentProp } from '../utils/type-helpers'
-import { executionFor, interpretTest, TestRun } from '../utils/wollok-helpers'
+import {
+ EntityMember,
+ executionFor,
+ interpretTest,
+ TestRun,
+} from '../utils/wollok-helpers'
import { createContextHook } from './create-context-hook'
export const mainPackageName = 'main'
+export const testsPackageName = 'tests'
export const ProjectContext = createContext<{
project: Environment
name: string
+ changed: boolean
+ problems: Problem[]
actions: Actions
} | null>(null)
type Actions = {
+ rebuildEnvironment: (entity: Entity) => void
addEntity: (module: Module) => void
addDescribe: (test: Describe) => void
- rebuildEnvironment: (entity: Entity) => void
+ addMember: (parent: Module) => (newMember: EntityMember) => void
+ changeMember: (
+ parent: Module,
+ ) => (oldMember: EntityMember, newMember: EntityMember) => void
runTest: (test: Test) => TestRun
execution: (test: Test) => ExecutionDirector
+ save: () => Promise
}
export function ProjectProvider(
@@ -42,6 +58,12 @@ export function ProjectProvider(
const [project, setProject] = useState(
link(props.initialProject.members),
)
+ const [changed, setChanged] = useState(false)
+ const [problems, setProblems] = useState(
+ validateProject(project) as Problem[],
+ )
+
+ /////////////////////////////////// BUILD //////////////////////////////////
function buildEnvironment(
name: Name,
@@ -61,6 +83,31 @@ export function ProjectProvider(
return link([pack], base ?? project)
}
+ function rebuildEnvironment(entity: Entity) {
+ const packageName = entity.is('Describe')
+ ? testsPackageName
+ : mainPackageName
+ const newProject = buildEnvironment(packageName, [entity])
+ setProject(newProject)
+ setChanged(true)
+ setProblems(validateProject(newProject) as Problem[])
+ }
+
+ function validateProject(_project: Environment) {
+ const targetPackages = [
+ _project.getNodeByFQN(mainPackageName),
+ _project.getNodeByFQN(testsPackageName),
+ ]
+ const belongsToTargetProject = (p: Problem) =>
+ targetPackages.some(target => p.node.ancestors().includes(target))
+
+ return validate(_project).filter(belongsToTargetProject)
+ }
+
+ /////////////////////////////////// BUILD //////////////////////////////////
+
+ /////////////////////////////////// ENTITIES //////////////////////////////////
+
function addEntity(newEntity: Module) {
rebuildEnvironment(newEntity)
}
@@ -69,12 +116,27 @@ export function ProjectProvider(
rebuildEnvironment(newDescribe)
}
- function rebuildEnvironment(entity: Entity) {
- const packageName = entity.is('Describe') ? 'tests' : mainPackageName
- setProject(buildEnvironment(packageName, [entity]))
- //TODO: Run validations
+ const addMember = (entity: Module) => (newMember: EntityMember) => {
+ rebuildEnvironment(
+ entity.copy({
+ members: [...entity.members, newMember],
+ }) as Module,
+ )
}
+ const changeMember =
+ (entity: Module) => (oldMember: EntityMember, newMember: EntityMember) => {
+ rebuildEnvironment(
+ entity.copy({
+ members: [...entity.members.filter(m => m !== oldMember), newMember],
+ }) as Module,
+ )
+ }
+
+ /////////////////////////////////// ENTITIES //////////////////////////////////
+
+ /////////////////////////////////// EXECUTION //////////////////////////////////
+
function runTest(test: Test) {
return interpretTest(test, project)
}
@@ -83,10 +145,28 @@ export function ProjectProvider(
return executionFor(test, project)
}
+ /////////////////////////////////// EXECUTION //////////////////////////////////
+
+ async function save() {
+ await saveProject(props.projectName, project)
+ setChanged(false)
+ }
+
const initialContext = {
project,
name: props.projectName,
- actions: { addEntity, addDescribe, rebuildEnvironment, runTest, execution },
+ changed,
+ problems,
+ actions: {
+ addEntity,
+ addDescribe,
+ addMember,
+ changeMember,
+ rebuildEnvironment,
+ runTest,
+ execution,
+ save,
+ },
}
return (
diff --git a/src/context/initialProject.ts b/src/context/initialProject.ts
index 6385e6a..b2512e1 100644
--- a/src/context/initialProject.ts
+++ b/src/context/initialProject.ts
@@ -5,7 +5,6 @@ import {
Describe,
Environment,
Field,
- fromJSON,
Import,
Literal,
Method,
@@ -16,6 +15,7 @@ import {
Singleton,
Test,
} from 'wollok-ts/dist/model'
+import { fromJSON } from 'wollok-ts/dist/jsonUtils'
import WRE from 'wollok-ts/dist/wre/wre.json'
import { mainPackageName } from './ProjectProvider'
diff --git a/src/pages/NewMessageCall.tsx b/src/pages/ArgumentsMaker.tsx
similarity index 86%
rename from src/pages/NewMessageCall.tsx
rename to src/pages/ArgumentsMaker.tsx
index dd7b9f2..93137fd 100644
--- a/src/pages/NewMessageCall.tsx
+++ b/src/pages/ArgumentsMaker.tsx
@@ -7,14 +7,15 @@ import { Expression, Send } from 'wollok-ts/dist/model'
import ExpressionInput from '../components/ui/ExpressionInput'
import { SubmitCheckButton } from '../components/ui/Header'
import { wTranslate } from '../utils/translation-helpers'
-import { EntityStackParamList } from './EntityStack'
+import { methodLabel } from '../utils/wollok-helpers'
+import { ProjectStackParamList } from './ProjectNavigator'
-export function NewMessageCall({
+export function ArgumentsMaker({
route: {
params: { method, receiver, contextFQN, onSubmit },
},
}: {
- route: RouteProp
+ route: RouteProp
}) {
const [args, setArguments] = useState<(Expression | undefined)[]>(
method.parameters.map(() => undefined),
@@ -24,6 +25,7 @@ export function NewMessageCall({
React.useLayoutEffect(() => {
navigation.setOptions({
+ title: methodLabel(method), //TODO: Show receiver?
headerRight: () => (
a === undefined)}
@@ -53,7 +55,7 @@ export function NewMessageCall({
{_.name}
setParameter(i, expression)}
value={args[i]}
inputPlaceholder={upperCaseFirst(
diff --git a/src/pages/Editor.tsx b/src/pages/Editor.tsx
new file mode 100644
index 0000000..43ee6eb
--- /dev/null
+++ b/src/pages/Editor.tsx
@@ -0,0 +1,56 @@
+import { RouteProp, useNavigation } from '@react-navigation/native'
+import { StackNavigationProp } from '@react-navigation/stack'
+import React from 'react'
+import { Body } from 'wollok-ts/dist/model'
+import { BodyMaker } from '../components/Body/BodyMaker'
+import { useProject } from '../context/ProjectProvider'
+import {
+ allScopedVariables,
+ entityMemberByFQN,
+ EntityMemberWithBody,
+} from '../utils/wollok-helpers'
+import { ProjectStackParamList } from './ProjectNavigator'
+
+export type EditorScreenNavigationProp = StackNavigationProp<
+ ProjectStackParamList,
+ 'Editor'
+>
+
+type Route = RouteProp
+
+export const Editor = ({
+ route: {
+ params: { fqn },
+ },
+}: {
+ route: Route
+}) => {
+ const {
+ project,
+ actions: { changeMember },
+ } = useProject()
+ const entity = entityMemberByFQN(project, fqn)
+ const parent = entity.parent
+
+ const navigation = useNavigation()
+ React.useLayoutEffect(() => {
+ navigation.setOptions({
+ title: entity.name,
+ headerTitleAlign: 'center',
+ animationEnabled: false,
+ })
+ }, [navigation, entity])
+
+ function setBody(body: Body) {
+ changeMember(parent)(entity, entity.copy({ body }) as EntityMemberWithBody)
+ }
+
+ return (
+
+ )
+}
diff --git a/src/pages/EntityDetails.tsx b/src/pages/EntityDetails.tsx
new file mode 100644
index 0000000..ef0fb15
--- /dev/null
+++ b/src/pages/EntityDetails.tsx
@@ -0,0 +1,33 @@
+import { RouteProp, useNavigation } from '@react-navigation/native'
+import React from 'react'
+import { Module } from 'wollok-ts/dist/model'
+import { ModuleDetails } from '../components/entity-detail/ModuleDetails'
+import { Tests } from '../components/tests/Tests'
+import { useProject } from '../context/ProjectProvider'
+import { ProjectStackParamList } from './ProjectNavigator'
+
+export type EntityDetailsRoute = RouteProp<
+ ProjectStackParamList,
+ 'EntityDetails'
+>
+
+function EntityDetails(props: { route: EntityDetailsRoute }) {
+ const { project } = useProject()
+ const entity = project.getNodeByFQN(props.route.params.entityFQN)
+
+ const navigation = useNavigation()
+ React.useLayoutEffect(() => {
+ navigation.setOptions({
+ title: entity.name,
+ headerTitleAlign: 'center',
+ })
+ }, [navigation, entity])
+
+ return entity.is('Describe') ? (
+
+ ) : (
+
+ )
+}
+
+export default EntityDetails
diff --git a/src/pages/EntityMemberDetail.tsx b/src/pages/EntityMemberDetail.tsx
deleted file mode 100644
index afb6a92..0000000
--- a/src/pages/EntityMemberDetail.tsx
+++ /dev/null
@@ -1,46 +0,0 @@
-import { RouteProp } from '@react-navigation/native'
-import { StackNavigationProp } from '@react-navigation/stack'
-import React from 'react'
-import { Body } from 'wollok-ts/dist/model'
-import { BodyMaker } from '../components/ui/Body/BodyMaker'
-import { useEntity } from '../context/EntityProvider'
-import {
- allScopedVariables,
- EntityMemberWithBody,
-} from '../utils/wollok-helpers'
-import { EntityStackParamList } from './EntityStack'
-
-export type EntityMemberScreenNavigationProp = StackNavigationProp<
- EntityStackParamList,
- 'EntityMemberDetails'
->
-
-type Route = RouteProp
-
-export const EntityMemberDetail = ({
- route: {
- params: { entityMember, fqn },
- },
-}: {
- route: Route
-}) => {
- const {
- actions: { changeMember },
- } = useEntity()
-
- function setBody(body: Body) {
- changeMember(
- entityMember,
- entityMember.copy({ body }) as EntityMemberWithBody,
- )
- }
-
- return (
-
- )
-}
diff --git a/src/pages/EntityStack.tsx b/src/pages/EntityStack.tsx
deleted file mode 100644
index 5799353..0000000
--- a/src/pages/EntityStack.tsx
+++ /dev/null
@@ -1,95 +0,0 @@
-import { RouteProp } from '@react-navigation/native'
-import { createStackNavigator } from '@react-navigation/stack'
-import React from 'react'
-import { Expression, Method, Module, Name, Send } from 'wollok-ts/dist/model'
-import { Tests } from '../components/tests/Tests'
-import { EntityProvider } from '../context/EntityProvider'
-import { useProject } from '../context/ProjectProvider'
-import { wTranslate } from '../utils/translation-helpers'
-import {
- entityMemberLabel,
- EntityMemberWithBody,
-} from '../utils/wollok-helpers'
-import { EntityDetails } from './EntityDetails/EntityDetails'
-import { EntityMemberDetail } from './EntityMemberDetail'
-import ExpressionMaker, {
- ExpressionOnSubmit,
-} from './ExpressionMaker/ExpressionMaker'
-import { NewMessageCall } from './NewMessageCall'
-import { ProjectStackParamList } from './ProjectNavigator'
-
-export type EntityStackRoute = RouteProp
-
-export type EntityStackParamList = {
- EntityDetails: undefined
- Tests: undefined
- EntityMemberDetails: {
- entityMember: EntityMemberWithBody
- fqn: Name
- }
- ExpressionMaker: {
- onSubmit: ExpressionOnSubmit
- contextFQN: Name
- initialExpression?: Expression
- }
- NewMessageSend: {
- receiver: Expression
- method: Method
- contextFQN: Name
- onSubmit: (s: Send) => void
- }
-}
-
-export default function (props: { route: EntityStackRoute }) {
- const { project } = useProject()
- const Stack = createStackNavigator()
- const entity = project.getNodeByFQN(props.route.params.entityFQN)
- return (
-
-
- {entity.is('Describe') ? (
-
- ) : (
-
- )}
- ({
- title: entityMemberLabel(methodRoute.params.entityMember),
- })}
- />
-
- ({ title: route.route.params.method.name })}
- />
-
-
- )
-}
diff --git a/src/pages/ExpressionMaker/ExpressionMaker.tsx b/src/pages/ExpressionMaker.tsx
similarity index 72%
rename from src/pages/ExpressionMaker/ExpressionMaker.tsx
rename to src/pages/ExpressionMaker.tsx
index 33b120b..66e9e6c 100644
--- a/src/pages/ExpressionMaker/ExpressionMaker.tsx
+++ b/src/pages/ExpressionMaker.tsx
@@ -4,30 +4,25 @@ import React, { useState } from 'react'
import { StyleSheet, View } from 'react-native'
import { ScrollView } from 'react-native-gesture-handler'
import { Button, List, TextInput } from 'react-native-paper'
-import { Expression, Module } from 'wollok-ts/dist/model'
-import { ListLiterals } from '../../components/expressions/expression-lists/literals-list'
-import { ListMessages } from '../../components/expressions/expression-lists/messages-list'
-import { ListSingletons } from '../../components/expressions/expression-lists/singletons-list'
-import { ListVariables } from '../../components/expressions/expression-lists/variables-list'
-import { ExpressionDisplay } from '../../components/expressions/ExpressionDisplay'
-import { SubmitCheckButton } from '../../components/ui/Header'
+import { Expression } from 'wollok-ts/dist/model'
+import { ListLiterals } from '../components/expressions/expression-lists/literals-list'
+import { ListMessages } from '../components/expressions/expression-lists/messages-list'
+import { ListSingletons } from '../components/expressions/expression-lists/singletons-list'
+import { ListVariables } from '../components/expressions/expression-lists/variables-list'
+import { ExpressionDisplay } from '../components/expressions/ExpressionDisplay'
+import { SubmitCheckButton } from '../components/ui/Header'
import {
Context,
ExpressionContextProvider,
useExpressionContext,
-} from '../../context/ExpressionContextProvider'
-import { useProject } from '../../context/ProjectProvider'
-import { wTranslate } from '../../utils/translation-helpers'
-import { isMethodFQN, methodByFQN } from '../../utils/wollok-helpers'
-import { EntityStackParamList } from '../EntityStack'
-
-export type ExpressionMakerProp = RouteProp<
- EntityStackParamList,
- 'ExpressionMaker'
->
+} from '../context/ExpressionContextProvider'
+import { useProject } from '../context/ProjectProvider'
+import { wTranslate } from '../utils/translation-helpers'
+import { entityMemberByFQN } from '../utils/wollok-helpers'
+import { ProjectStackParamList } from './ProjectNavigator'
export type ExpressionMakerScreenProp = StackNavigationProp<
- EntityStackParamList,
+ ProjectStackParamList,
'ExpressionMaker'
>
@@ -55,6 +50,9 @@ function ExpressionMaker(props: {
const navigation = useNavigation()
React.useLayoutEffect(() => {
navigation.setOptions({
+ title: wTranslate('expression.title'),
+ headerTitleAlign: 'center',
+ animationEnabled: false,
headerRight: () => (
+
const styles = StyleSheet.create({
view: { display: 'flex', maxHeight: '85%' },
})
@@ -124,12 +127,11 @@ export default function ({
params: { contextFQN, onSubmit, initialExpression },
},
}: {
- route: RouteProp
+ route: ExpressionMakerRouteProp
}) {
const { project } = useProject()
- const context: Context = isMethodFQN(contextFQN)
- ? methodByFQN(project, contextFQN)
- : project.getNodeByFQN(contextFQN)
+ const context: Context = entityMemberByFQN(project, contextFQN)
+
return (
{
navigation.setOptions({
title: name,
headerTitleAlign: 'center',
- headerRight: () => (
- saveProject(name, project).then(() => setSaved(true))}
- />
- ),
+ headerRight: () => ,
})
}, [navigation, project, name])
@@ -42,7 +43,7 @@ export function Home() {
}}
/>
{
- setSaved(false)
+ setShowMessage(false)
}}
duration={2000}
wrapperStyle={{ marginBottom: '20%' }}>
- {wTranslate('project.saved')}
+ {wTranslate(`project.${message}`)}
>
)
diff --git a/src/pages/ProjectNavigator.tsx b/src/pages/ProjectNavigator.tsx
index 25d60ee..c46e715 100644
--- a/src/pages/ProjectNavigator.tsx
+++ b/src/pages/ProjectNavigator.tsx
@@ -1,15 +1,36 @@
import { RouteProp } from '@react-navigation/core'
-import { createStackNavigator } from '@react-navigation/stack'
+import {
+ createStackNavigator,
+ StackNavigationProp,
+} from '@react-navigation/stack'
import React from 'react'
-import { Name } from 'wollok-ts/dist/model'
+import { Expression, Method, Name, Send } from 'wollok-ts/dist/model'
import { RootStackParamList } from '../App'
+import { NodeNavigationProvider } from '../context/NodeNavigation'
import { ProjectProvider } from '../context/ProjectProvider'
-import EntityStack from './EntityStack'
+import { ArgumentsMaker } from './ArgumentsMaker'
+import { Editor } from './Editor'
+import EntityDetails from './EntityDetails'
+import ExpressionMaker, { ExpressionOnSubmit } from './ExpressionMaker'
import { Home } from './Home'
export type ProjectStackParamList = {
Home: undefined
- EntityStack: { entityFQN: Name }
+ EntityDetails: { entityFQN: Name }
+ Editor: {
+ fqn: Name
+ }
+ ExpressionMaker: {
+ onSubmit: ExpressionOnSubmit
+ contextFQN: Name
+ initialExpression?: Expression
+ }
+ ArgumentsMaker: {
+ receiver: Expression
+ method: Method
+ contextFQN: Name
+ onSubmit: (s: Send) => void
+ }
}
const Stack = createStackNavigator()
@@ -19,20 +40,26 @@ export type ProjectStackRoute = RouteProp<
'ProjectNavigator'
>
+export type ProjectScreenNavigationProp = StackNavigationProp<
+ RootStackParamList,
+ 'ProjectNavigator'
+>
+
export function ProjectNavigator({ route }: { route: ProjectStackRoute }) {
return (
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
)
}
diff --git a/src/pages/SelectProject.tsx b/src/pages/SelectProject.tsx
index 10d3cf7..64fb4b5 100644
--- a/src/pages/SelectProject.tsx
+++ b/src/pages/SelectProject.tsx
@@ -5,7 +5,7 @@ import { List } from 'react-native-paper'
import { Environment } from 'wollok-ts/dist/model'
import { stylesheet } from '../components/entities/Entity/styles'
import FabAddScreen from '../components/FabScreens/FabAddScreen'
-import { NewProjectModal } from '../components/select-project/NewProjectModal'
+import { NewProjectModal } from '../components/projects/NewProjectModal'
import { templateProject } from '../context/initialProject'
import {
loadProject,
@@ -13,12 +13,13 @@ import {
saveProject,
} from '../services/persistance.service'
import { useTheme } from '../theme'
+import { ProjectScreenNavigationProp } from './ProjectNavigator'
export function SelectProject() {
const [projects, setProjects] = useState([])
const [showNewProjectModal, setShowNewProjectModal] = useState(false)
const focused = useIsFocused()
- const navigation = useNavigation()
+ const navigation = useNavigation()
const theme = useTheme()
diff --git a/src/pages/Describes.tsx b/src/pages/tabs/Describes.tsx
similarity index 73%
rename from src/pages/Describes.tsx
rename to src/pages/tabs/Describes.tsx
index 4493ce6..4100bee 100644
--- a/src/pages/Describes.tsx
+++ b/src/pages/tabs/Describes.tsx
@@ -1,10 +1,10 @@
import React, { useState } from 'react'
import { ScrollView } from 'react-native-gesture-handler'
import { Describe, Package } from 'wollok-ts/dist/model'
-import FabAddScreen from '../components/FabScreens/FabAddScreen'
-import NewDescribeModal from '../components/tests/NewDescribeModal'
-import DescribeItem from '../components/tests/DescribeItem'
-import { useProject } from '../context/ProjectProvider'
+import FabAddScreen from '../../components/FabScreens/FabAddScreen'
+import NewDescribeModal from '../../components/tests/NewDescribeModal'
+import DescribeItem from '../../components/tests/DescribeItem'
+import { useProject } from '../../context/ProjectProvider'
export function Describes() {
const {
diff --git a/src/pages/Entities/Entities.tsx b/src/pages/tabs/Modules.tsx
similarity index 97%
rename from src/pages/Entities/Entities.tsx
rename to src/pages/tabs/Modules.tsx
index a9262a4..5c72f03 100644
--- a/src/pages/Entities/Entities.tsx
+++ b/src/pages/tabs/Modules.tsx
@@ -6,7 +6,7 @@ import NewEntityModal from '../../components/entities/NewEntityModal/NewEntityMo
import FabAddScreen from '../../components/FabScreens/FabAddScreen'
import { mainPackageName, useProject } from '../../context/ProjectProvider'
-export function Entities() {
+export function Modules() {
const {
project,
actions: { addEntity },
diff --git a/src/services/persistance.service.ts b/src/services/persistance.service.ts
index 370098a..5ae87be 100644
--- a/src/services/persistance.service.ts
+++ b/src/services/persistance.service.ts
@@ -5,7 +5,8 @@ import {
writeFile,
} from 'react-native-fs'
import RNFetchBlob from 'rn-fetch-blob-v2'
-import { Environment, fromJSON } from 'wollok-ts/dist/model'
+import { Environment } from 'wollok-ts/dist/model'
+import { fromJSON } from 'wollok-ts/dist/jsonUtils'
import { projectToJSON } from '../utils/wollok-helpers'
const projectsFolder = 'projects'
diff --git a/src/utils/test-helpers.tsx b/src/utils/test-helpers.tsx
deleted file mode 100644
index fb9afa8..0000000
--- a/src/utils/test-helpers.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import { NavigationContainer } from '@react-navigation/native'
-import { render } from '@testing-library/react-native'
-import React from 'react'
-import { theme } from '../theme'
-import { OneOrMany } from './type-helpers'
-
-export function renderWithTheme(children: OneOrMany) {
- return render(
- {children},
- )
-}
diff --git a/src/utils/type-helpers.ts b/src/utils/type-helpers.ts
index fadabfe..174fd7d 100644
--- a/src/utils/type-helpers.ts
+++ b/src/utils/type-helpers.ts
@@ -9,3 +9,8 @@ export type Maybe = T | undefined
export type ParentComponentProp = T & {
children: OneOrMany
}
+
+export type Visible = {
+ visible: boolean
+ setVisible: (value: boolean) => void
+}
diff --git a/src/utils/wollok-helpers.ts b/src/utils/wollok-helpers.ts
index f209c9f..76e6f63 100644
--- a/src/utils/wollok-helpers.ts
+++ b/src/utils/wollok-helpers.ts
@@ -1,6 +1,7 @@
// TODO: import form Wollok
// All these funtions are duplicated from Wollok
import { upperCaseFirst } from 'upper-case-first'
+import { List } from 'wollok-ts/dist/extensions'
import interpret, {
DirectedInterpreter,
ExecutionDirector,
@@ -14,13 +15,13 @@ import {
Expression,
Field,
is,
- List,
Literal,
Method,
Module,
Name,
Node,
Parameter,
+ Problem,
Singleton,
Test,
Variable,
@@ -56,10 +57,6 @@ export function methodLabel(method: Method): string {
return `${method.name}(${method.parameters.map(_ => _.name).join(',')})`
}
-export function entityMemberLabel(node: EntityMemberWithBody): string {
- return node.is('Method') ? methodLabel(node) : node.name
-}
-
export function literalClassFQN(literal: Literal): Name {
return `wollok.lang.${upperCaseFirst(typeof literal.value)}`
}
@@ -67,15 +64,17 @@ export function literalClassFQN(literal: Literal): Name {
export function allScopedVariables(
node: EntityMemberWithBody,
): Referenciable[] {
- const fields = allFields(node.parent())
+ const fields = allFields(node.parent)
const params = node.is('Method') ? node.parameters : []
const methodVars = allVariables(node)
return [...fields, ...params, ...methodVars]
}
+// METHODS
+
export function methodFQN(method: Method) {
- return `${method.parent().fullyQualifiedName()}.${method.name}/${
+ return `${method.parent.fullyQualifiedName()}.${method.name}/${
method.parameters.length
}`
}
@@ -98,6 +97,31 @@ export function methodByFQN(environment: Environment, fqn: Name): Method {
return entity.lookupMethod(methodName, Number.parseInt(methodArity, 10))!
}
+export function entityMemberLabel(node: EntityMemberWithBody): string {
+ return node.is('Method') ? methodLabel(node) : node.name
+}
+
+export function entityMemberFQN(node: EntityMemberWithBody): string {
+ return node.is('Method') ? methodFQN(node) : node.fullyQualifiedName()
+}
+
+export function entityMemberByFQN(
+ environment: Environment,
+ fqn: Name,
+): EntityMemberWithBody {
+ return isMethodFQN(fqn)
+ ? methodByFQN(environment, fqn)
+ : environment.getNodeByFQN(fqn)
+}
+
+// PROBLEMS
+
+export function isError(problem: Problem): boolean {
+ return problem.level === 'error'
+}
+
+// TESTS
+
export type TestResult = 'Passed' | 'Failure' | 'Error'
export type TestRun = { result: TestResult; exception?: WollokException }
export function interpretTest(test: Test, environment: Environment): TestRun {
diff --git a/translations/en.json b/translations/en.json
index f8707d3..560c921 100644
--- a/translations/en.json
+++ b/translations/en.json
@@ -70,5 +70,10 @@
"selectProject": "Select a project",
"newProject": "New project",
"saved": "Saved!"
+ },
+ "problem": {
+ "shouldInitializeAllAttributes": "All object attributes should be initialized",
+ "nameShouldBeginWithLowercase": "This name should start with lowercase",
+ "shouldNotDefineUnusedVariables": "This variable is never used"
}
}
diff --git a/translations/es.json b/translations/es.json
index 50797fa..70b6c29 100644
--- a/translations/es.json
+++ b/translations/es.json
@@ -71,5 +71,10 @@
"selectProject": "Seleccioná un proyecto",
"newProject": "Proyecto nuevo",
"saved": "¡Guardado!"
+ },
+ "problem": {
+ "shouldInitializeAllAttributes": "Todos los atributos del objeto deben estar inicializados",
+ "nameShouldBeginWithLowercase": "Este nombre debería empezar con minúscula",
+ "shouldNotDefineUnusedVariables": "Esta variable no se está usando"
}
}
diff --git a/yarn.lock b/yarn.lock
index f0d4423..5b9098f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -9544,9 +9544,9 @@ unpipe@~1.0.0:
resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz"
integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=
-unraw@^2.0.0:
+unraw@^2.0.1:
version "2.0.1"
- resolved "https://registry.npmjs.org/unraw/-/unraw-2.0.1.tgz"
+ resolved "https://registry.yarnpkg.com/unraw/-/unraw-2.0.1.tgz#7b51dcdfb1e43d59d5e52cdb44d349d029edbaba"
integrity sha512-tdOvLfRzHolwYcHS6HIX860MkK9LQ4+oLuNwFYL7bpgTEO64PZrcQxkisgwJYCfF8sKiWLwwu1c83DvMkbefIQ==
unset-value@^1.0.0:
@@ -9829,14 +9829,14 @@ widest-line@^2.0.0:
dependencies:
string-width "^2.1.1"
-wollok-ts@^3.0.6:
- version "3.0.6"
- resolved "https://registry.npmjs.org/wollok-ts/-/wollok-ts-3.0.6.tgz"
- integrity sha512-2TgwRhu0+lUgCxULz4ZjMlw8zfv1gKFE7v6eh4fNtVWBdlKozqS6QT3YwNPzbgPolTXW9xrMaAf1lxpsD3ye1Q==
+wollok-ts@^3.0.8:
+ version "3.0.8"
+ resolved "https://registry.yarnpkg.com/wollok-ts/-/wollok-ts-3.0.8.tgz#dd4bcf5ec2fa323a565e8f8688291b3a61f3765e"
+ integrity sha512-oBVEPL+MwCVsfoBnN3bLvYsr9vM9OmPdReNQq+sYM0h/l2cn2iYfD0xQdVC9u0hhrqpzfk+es63fUKAVLEkLJQ==
dependencies:
"@types/parsimmon" "^1.10.6"
parsimmon "^1.18.0"
- unraw "^2.0.0"
+ unraw "^2.0.1"
uuid "8.3.2"
word-wrap@^1.2.3, word-wrap@~1.2.3: