diff options
| author | Elizabeth Hunt <me@liz.coffee> | 2026-02-08 12:33:16 -0800 |
|---|---|---|
| committer | Elizabeth Hunt <me@liz.coffee> | 2026-02-08 13:04:51 -0800 |
| commit | a673b749a5816392652785457dd1cf3603368628 (patch) | |
| tree | 2f9fce694996264e7135c2533c1afc1cca026cb6 /core/src/main | |
| parent | a483f04c16e6b56df24527f6a640e79c86928238 (diff) | |
| download | the-abstraction-engine-java-a673b749a5816392652785457dd1cf3603368628.tar.gz the-abstraction-engine-java-a673b749a5816392652785457dd1cf3603368628.zip | |
chore: Use actors
Diffstat (limited to 'core/src/main')
107 files changed, 4456 insertions, 4381 deletions
diff --git a/core/src/main/java/coffee/liz/abstractionengine/AbstractionEngineGridWorld.java b/core/src/main/java/coffee/liz/abstractionengine/AbstractionEngineGridWorld.java index c6e7a49..806d05e 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/AbstractionEngineGridWorld.java +++ b/core/src/main/java/coffee/liz/abstractionengine/AbstractionEngineGridWorld.java @@ -16,21 +16,21 @@ import java.util.Map; /** Grid world implementation for the abstraction engine. */ @Log4j2 public class AbstractionEngineGridWorld extends DAGWorld<GridInputState> { - /** Initialize world with systems and player. */ - public AbstractionEngineGridWorld(final Vec2<Integer> gridDimensions) { - super(Map.of(GridMovementSystem.class, new GridMovementSystem(), GridPhysicsSystem.class, - new GridPhysicsSystem(), GridIndexSystem.class, new GridIndexSystem(gridDimensions), - GridCollisionPropagatationSystem.class, new GridCollisionPropagatationSystem())); - } + /** Initialize world with systems and player. */ + public AbstractionEngineGridWorld(final Vec2<Integer> gridDimensions) { + super(Map.of(GridMovementSystem.class, new GridMovementSystem(), GridPhysicsSystem.class, + new GridPhysicsSystem(), GridIndexSystem.class, new GridIndexSystem(gridDimensions), + GridCollisionPropagatationSystem.class, new GridCollisionPropagatationSystem())); + } - /** - * Update world with input state. For now our grid world is a "step function", - * so we shouldn't (yet) care about the amount of time between steps. - * - * @param state - * the {@link GridInputState} - */ - public void update(final GridInputState state) { - update(state, Duration.ZERO); - } + /** + * Update world with input state. For now our grid world is a "step function", + * so we shouldn't (yet) care about the amount of time between steps. + * + * @param state + * the {@link GridInputState} + */ + public void update(final GridInputState state) { + update(state, Duration.ZERO); + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/AbstractionEngineGame.java b/core/src/main/java/coffee/liz/abstractionengine/app/AbstractionEngineGame.java index 9aa61ec..d0ba1f3 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/AbstractionEngineGame.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/AbstractionEngineGame.java @@ -13,42 +13,42 @@ import com.badlogic.gdx.graphics.glutils.ShapeRenderer; import com.badlogic.gdx.utils.viewport.FitViewport; public class AbstractionEngineGame extends Game { - public static final Vec2<Float> WORLD_SIZE = Vec2f.builder().x(800f).y(800f).build(); + public static final Vec2<Float> WORLD_SIZE = Vec2f.builder().x(800f).y(800f).build(); - public SpriteBatch batch; - public BitmapFont font; - public FitViewport viewport; - public ShapeRenderer shapeRenderer; - public Settings settings = Settings.builder().build(); + public SpriteBatch batch; + public BitmapFont font; + public FitViewport viewport; + public ShapeRenderer shapeRenderer; + public Settings settings = Settings.builder().build(); - /** - * Game initialization hook. - */ - public void create() { - viewport = new FitViewport(WORLD_SIZE.getX(), WORLD_SIZE.getY()); - batch = new SpriteBatch(); - shapeRenderer = new ShapeRenderer(); - font = FontHelper.initFont(24, FontHelper.MONO, - AbstractionEngineGame.WORLD_SIZE.getY() / Gdx.graphics.getHeight()); + /** + * Game initialization hook. + */ + public void create() { + viewport = new FitViewport(WORLD_SIZE.getX(), WORLD_SIZE.getY()); + batch = new SpriteBatch(); + shapeRenderer = new ShapeRenderer(); + font = FontHelper.initFont(24, FontHelper.MONO, + AbstractionEngineGame.WORLD_SIZE.getY() / Gdx.graphics.getHeight()); - this.setScreen(new ScrollLogo(this)); + this.setScreen(new ScrollLogo(this)); - viewport.update(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), true); - } + viewport.update(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), true); + } - /** - * Game render hook. - */ - public void render() { - super.render(); - } + /** + * Game render hook. + */ + public void render() { + super.render(); + } - /** - * Game cleanup hook. - */ - public void dispose() { - batch.dispose(); - font.dispose(); - shapeRenderer.dispose(); - } + /** + * Game cleanup hook. + */ + public void dispose() { + batch.dispose(); + font.dispose(); + shapeRenderer.dispose(); + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/Theme.java b/core/src/main/java/coffee/liz/abstractionengine/app/Theme.java index bc653fd..1d3f449 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/Theme.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/Theme.java @@ -4,26 +4,26 @@ import com.badlogic.gdx.graphics.Color; // TODO: Make this part of the {@link Settings} public final class Theme { - public static final Color BG = Color.valueOf("2d2523"); - public static final Color BG_PATTERN = Color.valueOf("241e1c"); - public static final Color SURFACE = Color.valueOf("3b312f"); - public static final Color SURFACE_ALT = Color.valueOf("463a37"); + public static final Color BG = Color.valueOf("2d2523"); + public static final Color BG_PATTERN = Color.valueOf("241e1c"); + public static final Color SURFACE = Color.valueOf("3b312f"); + public static final Color SURFACE_ALT = Color.valueOf("463a37"); - public static final Color FG = Color.valueOf("f9efea"); - public static final Color MUTED = Color.valueOf("cbb8b1"); + public static final Color FG = Color.valueOf("f9efea"); + public static final Color MUTED = Color.valueOf("cbb8b1"); - public static final Color BORDER = Color.valueOf("f1e6e0"); - public static final Color BORDER_LIGHT = Color.valueOf("6e5b57"); - public static final Color BORDER_DARK = Color.valueOf("120d0c"); + public static final Color BORDER = Color.valueOf("f1e6e0"); + public static final Color BORDER_LIGHT = Color.valueOf("6e5b57"); + public static final Color BORDER_DARK = Color.valueOf("120d0c"); - public static final Color PRIMARY = Color.valueOf("f06aa6"); - public static final Color PRIMARY_LIGHT = Color.valueOf("ff97c8"); - public static final Color PRIMARY_DARK = Color.valueOf("d94b8e"); + public static final Color PRIMARY = Color.valueOf("f06aa6"); + public static final Color PRIMARY_LIGHT = Color.valueOf("ff97c8"); + public static final Color PRIMARY_DARK = Color.valueOf("d94b8e"); - public static final Color SECONDARY = Color.valueOf("b69cff"); - public static final Color SECONDARY_LIGHT = Color.valueOf("d7c8ff"); - public static final Color SECONDARY_DARK = Color.valueOf("8f78d6"); + public static final Color SECONDARY = Color.valueOf("b69cff"); + public static final Color SECONDARY_LIGHT = Color.valueOf("d7c8ff"); + public static final Color SECONDARY_DARK = Color.valueOf("8f78d6"); - private Theme() { - } + private Theme() { + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/actor/Abstraction.java b/core/src/main/java/coffee/liz/abstractionengine/app/actor/Abstraction.java new file mode 100644 index 0000000..5c964a2 --- /dev/null +++ b/core/src/main/java/coffee/liz/abstractionengine/app/actor/Abstraction.java @@ -0,0 +1,59 @@ +package coffee.liz.abstractionengine.app.actor; + +import coffee.liz.ecs.math.Vec2; +import coffee.liz.ecs.math.Vec2i; +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.graphics.Texture; +import com.badlogic.gdx.graphics.g2d.Animation; +import com.badlogic.gdx.graphics.g2d.Batch; +import com.badlogic.gdx.graphics.g2d.TextureRegion; +import com.badlogic.gdx.scenes.scene2d.Actor; +import lombok.Getter; + +public class Abstraction extends Actor { + private static final Vec2<Integer> ABSTRACTION_FRAMES = Vec2i.builder().x(1).y(4).build(); + + private final Texture texture; + @Getter + private final Animation<TextureRegion> animation; + private float stateTime = 0f; + + /** + * Creates a penguin actor. + */ + public Abstraction() { + this.texture = new Texture(Gdx.files.internal("sprites/abstraction.png")); + final TextureRegion[][] tmp = TextureRegion.split(texture, texture.getWidth() / ABSTRACTION_FRAMES.getX(), + texture.getHeight() / ABSTRACTION_FRAMES.getY()); + final TextureRegion[] anim = { + tmp[0][0], + tmp[1][0], + tmp[2][0], + tmp[3][0] + }; + this.animation = new Animation<>(0.25f, anim); + } + + /** + * Actor draw hook. + * + * @param batch + * parent batch + * @param parentAlpha + * parent alpha multiplier + */ + @Override + public void draw(final Batch batch, final float parentAlpha) { + final TextureRegion frame = this.animation.getKeyFrame(stateTime, true); + final Color c = getColor(); + batch.setColor(c.r, c.g, c.b, c.a * parentAlpha); + batch.draw(frame, getX(), getY(), getWidth(), getHeight()); + } + + @Override + public void act(final float delta) { + super.act(delta); + stateTime += delta; + } +} diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/actor/Button.java b/core/src/main/java/coffee/liz/abstractionengine/app/actor/Button.java index a7416b9..271e40f 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/actor/Button.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/actor/Button.java @@ -17,110 +17,110 @@ import lombok.Setter; @RequiredArgsConstructor public class Button extends Actor { - private static final float ALPHA = 0.85f; - private static final float BORDER_WIDTH = 1f; + private static final float ALPHA = 0.85f; + private static final float BORDER_WIDTH = 1f; - private final ShapeRenderer shapeRenderer; - private final BitmapFont font; - private final String text; + private final ShapeRenderer shapeRenderer; + private final BitmapFont font; + private final String text; - @Setter - private Runnable onClick; + @Setter + private Runnable onClick; - @Getter - private boolean hovered = false; - @Getter - private boolean pressed = false; + @Getter + private boolean hovered = false; + @Getter + private boolean pressed = false; - private final GlyphLayout layout = new GlyphLayout(); - private final Color bgColor = new Color(); - private final Color borderColor = new Color(); + private final GlyphLayout layout = new GlyphLayout(); + private final Color bgColor = new Color(); + private final Color borderColor = new Color(); - /** - * Actor draw hook. - * - * @param batch - * parent batch - * @param parentAlpha - * parent alpha multiplier - */ - @Override - public void draw(final Batch batch, final float parentAlpha) { - batch.end(); + /** + * Actor draw hook. + * + * @param batch + * parent batch + * @param parentAlpha + * parent alpha multiplier + */ + @Override + public void draw(final Batch batch, final float parentAlpha) { + batch.end(); - Gdx.gl.glEnable(GL20.GL_BLEND); - Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA); + Gdx.gl.glEnable(GL20.GL_BLEND); + Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA); - shapeRenderer.setProjectionMatrix(batch.getProjectionMatrix()); - shapeRenderer.setTransformMatrix(batch.getTransformMatrix()); + shapeRenderer.setProjectionMatrix(batch.getProjectionMatrix()); + shapeRenderer.setTransformMatrix(batch.getTransformMatrix()); - final float effectiveAlpha = parentAlpha * ALPHA * getColor().a; - computeColors(effectiveAlpha); + final float effectiveAlpha = parentAlpha * ALPHA * getColor().a; + computeColors(effectiveAlpha); - shapeRenderer.begin(ShapeRenderer.ShapeType.Filled); - shapeRenderer.setColor(borderColor); - shapeRenderer.rect(getX(), getY(), getWidth(), getHeight()); - shapeRenderer.setColor(bgColor); - shapeRenderer.rect(getX() + BORDER_WIDTH, getY() + BORDER_WIDTH, getWidth() - BORDER_WIDTH * 2, - getHeight() - BORDER_WIDTH * 2); - shapeRenderer.end(); + shapeRenderer.begin(ShapeRenderer.ShapeType.Filled); + shapeRenderer.setColor(borderColor); + shapeRenderer.rect(getX(), getY(), getWidth(), getHeight()); + shapeRenderer.setColor(bgColor); + shapeRenderer.rect(getX() + BORDER_WIDTH, getY() + BORDER_WIDTH, getWidth() - BORDER_WIDTH * 2, + getHeight() - BORDER_WIDTH * 2); + shapeRenderer.end(); - Gdx.gl.glDisable(GL20.GL_BLEND); + Gdx.gl.glDisable(GL20.GL_BLEND); - batch.begin(); + batch.begin(); - layout.setText(font, text); - final float textX = getX() + (getWidth() - layout.width) / 2f; - final float textY = getY() + (getHeight() + layout.height) / 2f; + layout.setText(font, text); + final float textX = getX() + (getWidth() - layout.width) / 2f; + final float textY = getY() + (getHeight() + layout.height) / 2f; - final Color textColor = hovered ? Theme.FG : Theme.MUTED; - font.setColor(textColor.r, textColor.g, textColor.b, effectiveAlpha); - font.draw(batch, text, textX, textY); - } + final Color textColor = hovered ? Theme.FG : Theme.MUTED; + font.setColor(textColor.r, textColor.g, textColor.b, effectiveAlpha); + font.draw(batch, text, textX, textY); + } - private void computeColors(final float alpha) { - if (hovered) { - bgColor.set(Theme.SURFACE_ALT).a = alpha; - borderColor.set(Theme.PRIMARY).a = alpha; - } else { - bgColor.set(Theme.SURFACE).a = alpha; - borderColor.set(Theme.BORDER_LIGHT).a = alpha; - } - } + private void computeColors(final float alpha) { + if (hovered) { + bgColor.set(Theme.SURFACE_ALT).a = alpha; + borderColor.set(Theme.PRIMARY).a = alpha; + } else { + bgColor.set(Theme.SURFACE).a = alpha; + borderColor.set(Theme.BORDER_LIGHT).a = alpha; + } + } - /** - * Enables input handling for the button. - */ - public void addButtonListener() { - addListener(new InputListener() { - @Override - public boolean touchDown(final InputEvent event, final float x, final float y, final int pointer, - final int button) { - pressed = true; - return true; - } + /** + * Enables input handling for the button. + */ + public void addButtonListener() { + addListener(new InputListener() { + @Override + public boolean touchDown(final InputEvent event, final float x, final float y, final int pointer, + final int button) { + pressed = true; + return true; + } - @Override - public void touchUp(final InputEvent event, final float x, final float y, final int pointer, - final int button) { - if (pressed && hovered && onClick != null) { - onClick.run(); - } - pressed = false; - } + @Override + public void touchUp(final InputEvent event, final float x, final float y, final int pointer, + final int button) { + if (pressed && hovered && onClick != null) { + onClick.run(); + } + pressed = false; + } - @Override - public void enter(final InputEvent event, final float x, final float y, final int pointer, - final Actor fromActor) { - hovered = true; - } + @Override + public void enter(final InputEvent event, final float x, final float y, final int pointer, + final Actor fromActor) { + hovered = true; + } - @Override - public void exit(final InputEvent event, final float x, final float y, final int pointer, - final Actor toActor) { - hovered = false; - pressed = false; - } - }); - } + @Override + public void exit(final InputEvent event, final float x, final float y, final int pointer, + final Actor toActor) { + hovered = false; + pressed = false; + } + }); + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/actor/GridBoardActor.java b/core/src/main/java/coffee/liz/abstractionengine/app/actor/GridBoardActor.java deleted file mode 100644 index a4317e0..0000000 --- a/core/src/main/java/coffee/liz/abstractionengine/app/actor/GridBoardActor.java +++ /dev/null @@ -1,68 +0,0 @@ -package coffee.liz.abstractionengine.app.actor; - -import coffee.liz.abstractionengine.app.Theme; -import coffee.liz.abstractionengine.entity.EntityType; -import coffee.liz.ecs.math.Mat2; -import coffee.liz.ecs.math.Vec2; -import coffee.liz.ecs.math.Vec2f; -import com.badlogic.gdx.graphics.Color; -import com.badlogic.gdx.graphics.g2d.Batch; -import com.badlogic.gdx.graphics.glutils.ShapeRenderer; -import com.badlogic.gdx.scenes.scene2d.Actor; -import lombok.RequiredArgsConstructor; -import lombok.Setter; - -import java.util.List; - -@RequiredArgsConstructor -public class GridBoardActor extends Actor { - private final ShapeRenderer shapeRenderer; - private final Vec2<Integer> gridSize; - - @Setter - private Vec2<Float> cellSize; - - @Setter - private List<RenderableEntity> entities = List.of(); - - @Override - public void draw(final Batch batch, final float parentAlpha) { - batch.end(); - - shapeRenderer.setProjectionMatrix(batch.getProjectionMatrix()); - shapeRenderer.setTransformMatrix(batch.getTransformMatrix()); - shapeRenderer.begin(ShapeRenderer.ShapeType.Filled); - - if (cellSize != null) { - final float dotRadius = Math.min(cellSize.getX(), cellSize.getY()) * 0.07f; - shapeRenderer.setColor(Theme.BORDER); - Mat2.init(gridSize, pos -> { - final Vec2<Float> center = pos.floatValue().plus(Vec2f.builder().x(0.5f).y(0.5f).build()) - .scale(cellSize); - shapeRenderer.circle(center.getX(), center.getY(), dotRadius); - return center; - }); - } - - for (final RenderableEntity entity : entities) { - shapeRenderer.setColor(getEntityColor(entity.entityType())); - shapeRenderer.rect(entity.position().getX(), entity.position().getY(), entity.size().getX(), - entity.size().getY()); - } - - shapeRenderer.end(); - batch.begin(); - } - - private static Color getEntityColor(final EntityType type) { - return switch (type) { - case WALL -> Theme.MUTED; - case PLAYER -> Theme.PRIMARY; - case ABSTRACTION -> Theme.SECONDARY; - default -> Color.GREEN; - }; - } - - public record RenderableEntity(EntityType entityType, Vec2<Float> position, Vec2<Float> size) { - } -} diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/actor/LifeGridActor.java b/core/src/main/java/coffee/liz/abstractionengine/app/actor/LifeGridActor.java index ab3fcbb..c4bb5e3 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/actor/LifeGridActor.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/actor/LifeGridActor.java @@ -28,92 +28,92 @@ import static com.badlogic.gdx.math.MathUtils.clamp; @RequiredArgsConstructor public class LifeGridActor extends Actor { - private final ShapeRenderer shapeRenderer; - private final World<LifeInput> world; - private final Vec2<Integer> gridDimensions; - private final Settings settings; - private final Color cellColor = new Color(); + private final ShapeRenderer shapeRenderer; + private final World<LifeInput> world; + private final Vec2<Integer> gridDimensions; + private final Settings settings; + private final Color cellColor = new Color(); - @Setter - private Viewport viewport; - @Setter - private Vec2<Float> parallaxOffset = Vec2f.ZERO; + @Setter + private Viewport viewport; + @Setter + private Vec2<Float> parallaxOffset = Vec2f.ZERO; - /** - * Actor update hook. - * - * @param delta - * time since last frame - */ - @Override - public void act(final float delta) { - super.act(delta); - if (viewport == null) { - return; - } + /** + * Actor update hook. + * + * @param delta + * time since last frame + */ + @Override + public void act(final float delta) { + super.act(delta); + if (viewport == null) { + return; + } - final Vec2<Float> cellSize = computeCellSize(); - final Set<Vec2<Integer>> forcedAlive = computeForcedAlive(cellSize); - world.update(new LifeInput(forcedAlive, settings), Duration.ofMillis((int) (delta * 1000))); - } + final Vec2<Float> cellSize = computeCellSize(); + final Set<Vec2<Integer>> forcedAlive = computeForcedAlive(cellSize); + world.update(new LifeInput(forcedAlive, settings), Duration.ofMillis((int) (delta * 1000))); + } - /** - * Actor draw hook. - * - * @param batch - * parent batch - * @param parentAlpha - * parent alpha multiplier - */ - @Override - public void draw(final Batch batch, final float parentAlpha) { - if (viewport == null) { - return; - } - batch.end(); + /** + * Actor draw hook. + * + * @param batch + * parent batch + * @param parentAlpha + * parent alpha multiplier + */ + @Override + public void draw(final Batch batch, final float parentAlpha) { + if (viewport == null) { + return; + } + batch.end(); - shapeRenderer.setProjectionMatrix(batch.getProjectionMatrix()); - shapeRenderer.setTransformMatrix(batch.getTransformMatrix()); - shapeRenderer.begin(ShapeRenderer.ShapeType.Filled); - drawCells(computeCellSize()); - shapeRenderer.end(); + shapeRenderer.setProjectionMatrix(batch.getProjectionMatrix()); + shapeRenderer.setTransformMatrix(batch.getTransformMatrix()); + shapeRenderer.begin(ShapeRenderer.ShapeType.Filled); + drawCells(computeCellSize()); + shapeRenderer.end(); - batch.begin(); - } + batch.begin(); + } - private Vec2<Float> computeCellSize() { - return Vec2f.builder().x(viewport.getWorldWidth() / gridDimensions.getX()) - .y(viewport.getWorldHeight() / gridDimensions.getY()).build(); - } + private Vec2<Float> computeCellSize() { + return Vec2f.builder().x(viewport.getWorldWidth() / gridDimensions.getX()) + .y(viewport.getWorldHeight() / gridDimensions.getY()).build(); + } - private Set<Vec2<Integer>> computeForcedAlive(final Vec2<Float> cellSize) { - final Vector3 cursorPosition = viewport.unproject(new Vector3(Gdx.input.getX(), Gdx.input.getY(), 0)); - final Vec2<Integer> gridPosition = Vec2f.builder().x(cursorPosition.x).y(cursorPosition.y).build() - .transform(x -> clamp(x, 0.0f, viewport.getWorldWidth() - 0.001f), - y -> clamp(y, 0.0f, viewport.getWorldHeight() - 0.001f)) - .scale(cellSize.transform(x -> 1 / x, y -> 1 / y)).intValue(); - return Stream.of(gridPosition) - .flatMap(pos -> Stream.of(Vec2i.ZERO, Vec2i.EAST, Vec2i.WEST, Vec2i.NORTH, Vec2i.SOUTH).map(pos::plus)) - .collect(Collectors.toSet()); - } + private Set<Vec2<Integer>> computeForcedAlive(final Vec2<Float> cellSize) { + final Vector3 cursorPosition = viewport.unproject(new Vector3(Gdx.input.getX(), Gdx.input.getY(), 0)); + final Vec2<Integer> gridPosition = Vec2f.builder().x(cursorPosition.x).y(cursorPosition.y).build() + .transform(x -> clamp(x, 0.0f, viewport.getWorldWidth() - 0.001f), + y -> clamp(y, 0.0f, viewport.getWorldHeight() - 0.001f)) + .scale(cellSize.transform(x -> 1 / x, y -> 1 / y)).intValue(); + return Stream.of(gridPosition) + .flatMap(pos -> Stream.of(Vec2i.ZERO, Vec2i.EAST, Vec2i.WEST, Vec2i.NORTH, Vec2i.SOUTH).map(pos::plus)) + .collect(Collectors.toSet()); + } - private void drawCells(final Vec2<Float> cellSize) { - world.query(Set.of(GridPosition.class, CellState.class)).forEach(entity -> { - final CellState state = entity.get(CellState.class); - final Vec2<Integer> gridPos = entity.get(GridPosition.class).getPosition(); - final Vec2<Float> drawPos = gridPos.floatValue().scale(cellSize).plus(parallaxOffset); + private void drawCells(final Vec2<Float> cellSize) { + world.query(Set.of(GridPosition.class, CellState.class)).forEach(entity -> { + final CellState state = entity.get(CellState.class); + final Vec2<Integer> gridPos = entity.get(GridPosition.class).getPosition(); + final Vec2<Float> drawPos = gridPos.floatValue().scale(cellSize).plus(parallaxOffset); - final float alivePercentage = state.getAlivePercentage(); - if (alivePercentage <= 0.0f) { - return; - } - shapeRenderer.setColor(interpolateColor(alivePercentage)); - shapeRenderer.rect(drawPos.getX(), drawPos.getY(), cellSize.getX(), cellSize.getY()); - }); - } + final float alivePercentage = state.getAlivePercentage(); + if (alivePercentage <= 0.0f) { + return; + } + shapeRenderer.setColor(interpolateColor(alivePercentage)); + shapeRenderer.rect(drawPos.getX(), drawPos.getY(), cellSize.getX(), cellSize.getY()); + }); + } - private Color interpolateColor(final float alivePercentage) { - final float interpolation = alivePercentage * alivePercentage * alivePercentage; - return cellColor.set(Theme.BG_PATTERN).lerp(Theme.FG, interpolation); - } + private Color interpolateColor(final float alivePercentage) { + final float interpolation = alivePercentage * alivePercentage * alivePercentage; + return cellColor.set(Theme.BG_PATTERN).lerp(Theme.FG, interpolation); + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/actor/Logo.java b/core/src/main/java/coffee/liz/abstractionengine/app/actor/Logo.java index e8bf025..80f04cb 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/actor/Logo.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/actor/Logo.java @@ -8,36 +8,36 @@ import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.utils.Disposable; public class Logo extends Actor implements Disposable { - private final Texture texture; + private final Texture texture; - /** - * Creates a logo actor. - */ - public Logo() { - this.texture = new Texture(Gdx.files.internal("sprites/logo.png")); - setSize(texture.getWidth(), texture.getHeight()); - } + /** + * Creates a logo actor. + */ + public Logo() { + this.texture = new Texture(Gdx.files.internal("sprites/logo.png")); + setSize(texture.getWidth(), texture.getHeight()); + } - /** - * Actor draw hook. - * - * @param batch - * parent batch - * @param parentAlpha - * parent alpha multiplier - */ - @Override - public void draw(final Batch batch, final float parentAlpha) { - final Color c = getColor(); - batch.setColor(c.r, c.g, c.b, c.a * parentAlpha); - batch.draw(texture, getX(), getY(), getWidth(), getHeight()); - } + /** + * Actor draw hook. + * + * @param batch + * parent batch + * @param parentAlpha + * parent alpha multiplier + */ + @Override + public void draw(final Batch batch, final float parentAlpha) { + final Color c = getColor(); + batch.setColor(c.r, c.g, c.b, c.a * parentAlpha); + batch.draw(texture, getX(), getY(), getWidth(), getHeight()); + } - /** - * Releases actor resources. - */ - @Override - public void dispose() { - texture.dispose(); - } + /** + * Releases actor resources. + */ + @Override + public void dispose() { + texture.dispose(); + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/actor/Penguin.java b/core/src/main/java/coffee/liz/abstractionengine/app/actor/Penguin.java index 4a557e7..932ac41 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/actor/Penguin.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/actor/Penguin.java @@ -13,95 +13,95 @@ import com.badlogic.gdx.utils.Disposable; import lombok.Getter; public class Penguin extends Actor implements Disposable { - private static final Vec2<Integer> PENGUIN_FRAMES = Vec2i.builder().x(8).y(4).build(); + private static final Vec2<Integer> PENGUIN_FRAMES = Vec2i.builder().x(8).y(4).build(); - private final Texture texture; - @Getter - private final Animation<TextureRegion> tobaganning; - @Getter - private final Animation<TextureRegion> eepy; - @Getter - private final Animation<TextureRegion> eeping; - private Animation<TextureRegion> currentAnimation; - private float stateTime = 0f; + private final Texture texture; + @Getter + private final Animation<TextureRegion> tobaganning; + @Getter + private final Animation<TextureRegion> eepy; + @Getter + private final Animation<TextureRegion> eeping; + private Animation<TextureRegion> currentAnimation; + private float stateTime = 0f; - /** - * Creates a penguin actor. - */ - public Penguin() { - this.texture = new Texture(Gdx.files.internal("sprites/penguins.png")); - final TextureRegion[][] tmp = TextureRegion.split(texture, texture.getWidth() / PENGUIN_FRAMES.getX(), - texture.getHeight() / PENGUIN_FRAMES.getY()); + /** + * Creates a penguin actor. + */ + public Penguin() { + this.texture = new Texture(Gdx.files.internal("sprites/penguins.png")); + final TextureRegion[][] tmp = TextureRegion.split(texture, texture.getWidth() / PENGUIN_FRAMES.getX(), + texture.getHeight() / PENGUIN_FRAMES.getY()); - final TextureRegion[] tobaggonTextureRegion = new TextureRegion[2]; - tobaggonTextureRegion[0] = tmp[2][7]; - tobaggonTextureRegion[1] = tmp[3][6]; + final TextureRegion[] tobaggonTextureRegion = new TextureRegion[2]; + tobaggonTextureRegion[0] = tmp[2][7]; + tobaggonTextureRegion[1] = tmp[3][6]; - final TextureRegion[] eepyTextureRegion = new TextureRegion[2]; - eepyTextureRegion[0] = tmp[2][3]; - eepyTextureRegion[1] = tmp[3][3]; + final TextureRegion[] eepyTextureRegion = new TextureRegion[2]; + eepyTextureRegion[0] = tmp[2][3]; + eepyTextureRegion[1] = tmp[3][3]; - final TextureRegion[] eepingTextureRegion = new TextureRegion[2]; - eepingTextureRegion[0] = tmp[0][2]; - eepingTextureRegion[1] = tmp[1][2]; + final TextureRegion[] eepingTextureRegion = new TextureRegion[2]; + eepingTextureRegion[0] = tmp[0][2]; + eepingTextureRegion[1] = tmp[1][2]; - this.tobaganning = new Animation<>(0.25f, tobaggonTextureRegion); - this.eepy = new Animation<>(0.45f, eepyTextureRegion); - this.eeping = new Animation<>(0.45f, eepingTextureRegion); - this.currentAnimation = tobaganning; - } + this.tobaganning = new Animation<>(0.25f, tobaggonTextureRegion); + this.eepy = new Animation<>(0.45f, eepyTextureRegion); + this.eeping = new Animation<>(0.45f, eepingTextureRegion); + this.currentAnimation = tobaganning; + } - /** - * Actor update hook. - * - * @param delta - * time since last frame - */ - @Override - public void act(final float delta) { - super.act(delta); - stateTime += delta; - } + /** + * Actor update hook. + * + * @param delta + * time since last frame + */ + @Override + public void act(final float delta) { + super.act(delta); + stateTime += delta; + } - /** - * Actor draw hook. - * - * @param batch - * parent batch - * @param parentAlpha - * parent alpha multiplier - */ - @Override - public void draw(final Batch batch, final float parentAlpha) { - final TextureRegion frame = currentAnimation.getKeyFrame(stateTime, true); - final Color c = getColor(); - batch.setColor(c.r, c.g, c.b, c.a * parentAlpha); - batch.draw(frame, getX(), getY(), getWidth(), getHeight()); - } + /** + * Actor draw hook. + * + * @param batch + * parent batch + * @param parentAlpha + * parent alpha multiplier + */ + @Override + public void draw(final Batch batch, final float parentAlpha) { + final TextureRegion frame = currentAnimation.getKeyFrame(stateTime, true); + final Color c = getColor(); + batch.setColor(c.r, c.g, c.b, c.a * parentAlpha); + batch.draw(frame, getX(), getY(), getWidth(), getHeight()); + } - /** - * Sets the current animation state. - * - * @param state - * new state - */ - public void setState(final State state) { - this.currentAnimation = switch (state) { - case EEPY -> eepy; - case EEPING -> eeping; - case TOBAGGON -> tobaganning; - }; - } + /** + * Sets the current animation state. + * + * @param state + * new state + */ + public void setState(final State state) { + this.currentAnimation = switch (state) { + case EEPY -> eepy; + case EEPING -> eeping; + case TOBAGGON -> tobaganning; + }; + } - /** - * Releases actor resources. - */ - @Override - public void dispose() { - texture.dispose(); - } + /** + * Releases actor resources. + */ + @Override + public void dispose() { + texture.dispose(); + } - public enum State { - EEPY, EEPING, TOBAGGON - } + public enum State { + EEPY, EEPING, TOBAGGON + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/actor/Player.java b/core/src/main/java/coffee/liz/abstractionengine/app/actor/Player.java new file mode 100644 index 0000000..fb63b0d --- /dev/null +++ b/core/src/main/java/coffee/liz/abstractionengine/app/actor/Player.java @@ -0,0 +1,22 @@ +package coffee.liz.abstractionengine.app.actor; + +import com.badlogic.gdx.graphics.g2d.Batch; +import com.badlogic.gdx.scenes.scene2d.Actor; +import com.badlogic.gdx.scenes.scene2d.InputListener; +import com.badlogic.gdx.scenes.scene2d.Stage; + +import lombok.AllArgsConstructor; + +@AllArgsConstructor +public class Player extends Actor { + private Stage stage; + + public Player() { + addListener(new InputListener()); + } + + @Override + public void draw(final Batch batch, final float parentAlpha) { + // batch. + } +} diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/actor/Wall.java b/core/src/main/java/coffee/liz/abstractionengine/app/actor/Wall.java new file mode 100644 index 0000000..2848a8e --- /dev/null +++ b/core/src/main/java/coffee/liz/abstractionengine/app/actor/Wall.java @@ -0,0 +1,22 @@ +package coffee.liz.abstractionengine.app.actor; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.graphics.Texture; +import com.badlogic.gdx.graphics.g2d.Batch; +import com.badlogic.gdx.scenes.scene2d.Actor; + +public class Wall extends Actor { + private final Texture texture; + + public Wall() { + this.texture = new Texture(Gdx.files.internal("sprites/wall.png")); + } + + @Override + public void draw(final Batch batch, final float parentAlpha) { + final Color c = getColor(); + batch.setColor(c.r, c.g, c.b, c.a * parentAlpha); + batch.draw(texture, getX(), getY(), getWidth(), getHeight()); + } +} diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/config/KeyBinds.java b/core/src/main/java/coffee/liz/abstractionengine/app/config/KeyBinds.java index 598e1a8..06ae70d 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/config/KeyBinds.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/config/KeyBinds.java @@ -17,27 +17,27 @@ import java.util.function.Predicate; @RequiredArgsConstructor @Getter public class KeyBinds { - @Builder.Default - private Set<Integer> moveUpKeys = Set.of(Input.Keys.K, Input.Keys.UP, Input.Keys.W); - @Builder.Default - private Set<Integer> moveDownKeys = Set.of(Input.Keys.J, Input.Keys.DOWN, Input.Keys.S); - @Builder.Default - private Set<Integer> moveLeftKeys = Set.of(Input.Keys.H, Input.Keys.LEFT, Input.Keys.A); - @Builder.Default - private Set<Integer> moveRightKeys = Set.of(Input.Keys.L, Input.Keys.RIGHT, Input.Keys.D); + @Builder.Default + private Set<Integer> moveUpKeys = Set.of(Input.Keys.K, Input.Keys.UP, Input.Keys.W); + @Builder.Default + private Set<Integer> moveDownKeys = Set.of(Input.Keys.J, Input.Keys.DOWN, Input.Keys.S); + @Builder.Default + private Set<Integer> moveLeftKeys = Set.of(Input.Keys.H, Input.Keys.LEFT, Input.Keys.A); + @Builder.Default + private Set<Integer> moveRightKeys = Set.of(Input.Keys.L, Input.Keys.RIGHT, Input.Keys.D); - public Set<Action> filterActiveActions(final Predicate<Integer> isDown) { - final Set<Action> actions = new HashSet<>(); - Map.of(moveUpKeys, Action.MOVE_UP, moveDownKeys, Action.MOVE_DOWN, moveRightKeys, Action.MOVE_RIGHT, - moveLeftKeys, Action.MOVE_LEFT).forEach((keys, action) -> { - if (keys.stream().anyMatch(isDown)) { - actions.add(action); - } - }); - return actions; - } + public Set<Action> filterActiveActions(final Predicate<Integer> isDown) { + final Set<Action> actions = new HashSet<>(); + Map.of(moveUpKeys, Action.MOVE_UP, moveDownKeys, Action.MOVE_DOWN, moveRightKeys, Action.MOVE_RIGHT, + moveLeftKeys, Action.MOVE_LEFT).forEach((keys, action) -> { + if (keys.stream().anyMatch(isDown)) { + actions.add(action); + } + }); + return actions; + } - public enum Action { - MOVE_UP, MOVE_LEFT, MOVE_DOWN, MOVE_RIGHT; - } + public enum Action { + MOVE_UP, MOVE_LEFT, MOVE_DOWN, MOVE_RIGHT; + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/config/Settings.java b/core/src/main/java/coffee/liz/abstractionengine/app/config/Settings.java index dd09c21..7662700 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/config/Settings.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/config/Settings.java @@ -8,13 +8,13 @@ import lombok.Getter; @AllArgsConstructor @Getter public class Settings { - @Builder.Default - private boolean skipIntro = true; - @Builder.Default - private boolean playMusic = true; - @Builder.Default - private boolean vimMode = true; + @Builder.Default + private boolean skipIntro = true; + @Builder.Default + private boolean playMusic = true; + @Builder.Default + private boolean vimMode = true; - @Builder.Default - private KeyBinds keyBinds = KeyBinds.builder().build(); + @Builder.Default + private KeyBinds keyBinds = KeyBinds.builder().build(); } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/life/CellState.java b/core/src/main/java/coffee/liz/abstractionengine/app/life/CellState.java index 025cde0..bd07629 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/life/CellState.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/life/CellState.java @@ -5,36 +5,36 @@ import lombok.Getter; @Getter public class CellState implements Component { - private static final float EPS = 1.0e-4f; + private static final float EPS = 1.0e-4f; - public static final CellState LIVE = new CellState(1.0f); - public static final CellState DEAD = new CellState(0.0f); + public static final CellState LIVE = new CellState(1.0f); + public static final CellState DEAD = new CellState(0.0f); - private final float alivePercentage; + private final float alivePercentage; - /** - * Creates a cell state with a normalized alive percentage. - * - * @param alivePercentage - * alive value in the range [0, 1] - */ - public CellState(final float alivePercentage) { - this.alivePercentage = clamp(alivePercentage); - } + /** + * Creates a cell state with a normalized alive percentage. + * + * @param alivePercentage + * alive value in the range [0, 1] + */ + public CellState(final float alivePercentage) { + this.alivePercentage = clamp(alivePercentage); + } - /** - * Returns true when the cell is considered alive. - * - * @return whether the cell is alive - */ - public boolean isAlive() { - return alivePercentage >= (1.0f - EPS); - } + /** + * Returns true when the cell is considered alive. + * + * @return whether the cell is alive + */ + public boolean isAlive() { + return alivePercentage >= (1.0f - EPS); + } - private static float clamp(final float value) { - if (value <= 0.0f) { - return 0.0f; - } - return Math.min(value, 1.0f); - } + private static float clamp(final float value) { + if (value <= 0.0f) { + return 0.0f; + } + return Math.min(value, 1.0f); + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/life/EntropyAudioSystem.java b/core/src/main/java/coffee/liz/abstractionengine/app/life/EntropyAudioSystem.java index b57db71..5660ff3 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/life/EntropyAudioSystem.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/life/EntropyAudioSystem.java @@ -17,142 +17,142 @@ import java.util.Set; import java.util.stream.IntStream; public class EntropyAudioSystem implements System<LifeInput> { - private static final int SAMPLE_RATE = 44100; - private static final int CHUNKS_X = 4; - private static final int CHUNKS_Y = 4; - private static final int NUM_CHUNKS = CHUNKS_X * CHUNKS_Y; - private static final Duration AUDIO_THREAD_TIMEOUT = Duration.ofMillis(20); + private static final int SAMPLE_RATE = 44100; + private static final int CHUNKS_X = 4; + private static final int CHUNKS_Y = 4; + private static final int NUM_CHUNKS = CHUNKS_X * CHUNKS_Y; + private static final Duration AUDIO_THREAD_TIMEOUT = Duration.ofMillis(20); - private static final int[] PENTATONIC_INTERVALS = {0, 3, 5, 7, 10}; - private static final float BASE_FREQUENCY = 110f; - private static final float[] FREQUENCIES = new float[NUM_CHUNKS]; - static { - for (int i = 0; i < NUM_CHUNKS; i++) { - final int octave = i / PENTATONIC_INTERVALS.length; - final int degree = i % PENTATONIC_INTERVALS.length; - final int semitones = octave * 12 + PENTATONIC_INTERVALS[degree]; - FREQUENCIES[i] = BASE_FREQUENCY * (float) Math.pow(2.0, semitones / 12.0); - } - } + private static final int[] PENTATONIC_INTERVALS = {0, 3, 5, 7, 10}; + private static final float BASE_FREQUENCY = 110f; + private static final float[] FREQUENCIES = new float[NUM_CHUNKS]; + static { + for (int i = 0; i < NUM_CHUNKS; i++) { + final int octave = i / PENTATONIC_INTERVALS.length; + final int degree = i % PENTATONIC_INTERVALS.length; + final int semitones = octave * 12 + PENTATONIC_INTERVALS[degree]; + FREQUENCIES[i] = BASE_FREQUENCY * (float) Math.pow(2.0, semitones / 12.0); + } + } - private static final float MIN_PULSE_RATE = 1.0f; - private static final float MAX_PULSE_RATE = 8.0f; - private static final float MASTER_VOLUME = 0.15f; - private static final float SMOOTHING_FACTOR = 0.0005f; + private static final float MIN_PULSE_RATE = 1.0f; + private static final float MAX_PULSE_RATE = 8.0f; + private static final float MASTER_VOLUME = 0.15f; + private static final float SMOOTHING_FACTOR = 0.0005f; - private final AudioDevice device = Gdx.audio.newAudioDevice(SAMPLE_RATE, true); - private final float[] targetEntropies = new float[NUM_CHUNKS]; - private final float[] smoothedEntropies = new float[NUM_CHUNKS]; - private final float[] phases = new float[NUM_CHUNKS]; - private final float[] pulsePhases = new float[NUM_CHUNKS]; + private final AudioDevice device = Gdx.audio.newAudioDevice(SAMPLE_RATE, true); + private final float[] targetEntropies = new float[NUM_CHUNKS]; + private final float[] smoothedEntropies = new float[NUM_CHUNKS]; + private final float[] phases = new float[NUM_CHUNKS]; + private final float[] pulsePhases = new float[NUM_CHUNKS]; - private final Thread audioThread; - private volatile boolean running = true; + private final Thread audioThread; + private volatile boolean running = true; - public EntropyAudioSystem() { - audioThread = new Thread(this::audioLoop, "EntropyAudio"); - audioThread.setDaemon(true); - } + public EntropyAudioSystem() { + audioThread = new Thread(this::audioLoop, "EntropyAudio"); + audioThread.setDaemon(true); + } - @Override - public Collection<Class<? extends System<LifeInput>>> getDependencies() { - return Set.of(LifeSystem.class); - } + @Override + public Collection<Class<? extends System<LifeInput>>> getDependencies() { + return Set.of(LifeSystem.class); + } - @Override - public void update(final World<LifeInput> world, final LifeInput state, final Duration dt) { - if (!state.getGameInstructions().isPlayMusic()) { - running = false; - return; - } + @Override + public void update(final World<LifeInput> world, final LifeInput state, final Duration dt) { + if (!state.getGameInstructions().isPlayMusic()) { + running = false; + return; + } - if (!audioThread.isAlive()) { - audioThread.start(); - running = true; - } + if (!audioThread.isAlive()) { + audioThread.start(); + running = true; + } - final LifeSystem lifeSystem = world.getSystem(LifeSystem.class); - final Vec2<Integer> gridDimensions = lifeSystem.getDimensions(); - final List<List<Set<Entity>>> rows = lifeSystem.getRows(); + final LifeSystem lifeSystem = world.getSystem(LifeSystem.class); + final Vec2<Integer> gridDimensions = lifeSystem.getDimensions(); + final List<List<Set<Entity>>> rows = lifeSystem.getRows(); - final Vec2<Integer> chunkDimensions = Vec2i.builder().x(gridDimensions.getX() / CHUNKS_X) - .y(gridDimensions.getY() / CHUNKS_Y).build(); + final Vec2<Integer> chunkDimensions = Vec2i.builder().x(gridDimensions.getX() / CHUNKS_X) + .y(gridDimensions.getY() / CHUNKS_Y).build(); - for (int cy = 0; cy < CHUNKS_Y; cy++) { - for (int cx = 0; cx < CHUNKS_X; cx++) { - final int chunkIndex = cy * CHUNKS_X + cx; - final Vec2<Integer> chunk = Vec2i.builder().x(cx).y(cy).build(); - final Vec2<Integer> bottomLeft = chunkDimensions.scale(chunk); - final Vec2<Integer> topRight = bottomLeft.plus(chunkDimensions); + for (int cy = 0; cy < CHUNKS_Y; cy++) { + for (int cx = 0; cx < CHUNKS_X; cx++) { + final int chunkIndex = cy * CHUNKS_X + cx; + final Vec2<Integer> chunk = Vec2i.builder().x(cx).y(cy).build(); + final Vec2<Integer> bottomLeft = chunkDimensions.scale(chunk); + final Vec2<Integer> topRight = bottomLeft.plus(chunkDimensions); - final long alive = IntStream.range(bottomLeft.getY(), topRight.getY()) - .mapToLong(y -> IntStream.range(bottomLeft.getX(), topRight.getX()).mapToLong( - x -> rows.get(y).get(x).stream().filter(e -> e.get(CellState.class).isAlive()).count()) - .sum()) - .sum(); - targetEntropies[chunkIndex] = computeShannonEntropy( - alive / (float) (chunkDimensions.getY() * chunkDimensions.getX())); - } - } - } + final long alive = IntStream.range(bottomLeft.getY(), topRight.getY()) + .mapToLong(y -> IntStream.range(bottomLeft.getX(), topRight.getX()).mapToLong( + x -> rows.get(y).get(x).stream().filter(e -> e.get(CellState.class).isAlive()).count()) + .sum()) + .sum(); + targetEntropies[chunkIndex] = computeShannonEntropy( + alive / (float) (chunkDimensions.getY() * chunkDimensions.getX())); + } + } + } - private float computeShannonEntropy(final float p) { - if (p <= 0.0f || p >= 1.0f) { - return 0.0f; - } + private float computeShannonEntropy(final float p) { + if (p <= 0.0f || p >= 1.0f) { + return 0.0f; + } - final float entropy = -p * MathUtils.log2(p) - (1 - p) * MathUtils.log2(1 - p); - return MathUtils.clamp(entropy, 0f, 1f); - } + final float entropy = -p * MathUtils.log2(p) - (1 - p) * MathUtils.log2(1 - p); + return MathUtils.clamp(entropy, 0f, 1f); + } - private void audioLoop() { - final int bufferSize = 2048; - final float[] buffer = new float[bufferSize]; - final float dtPerSample = 1.0f / SAMPLE_RATE; + private void audioLoop() { + final int bufferSize = 2048; + final float[] buffer = new float[bufferSize]; + final float dtPerSample = 1.0f / SAMPLE_RATE; - while (running) { - for (int i = 0; i < bufferSize; i++) { - float sample = 0.0f; + while (running) { + for (int i = 0; i < bufferSize; i++) { + float sample = 0.0f; - for (int c = 0; c < NUM_CHUNKS; c++) { - final float target = targetEntropies[c]; - smoothedEntropies[c] += (target - smoothedEntropies[c]) * SMOOTHING_FACTOR; - final float entropy = smoothedEntropies[c]; + for (int c = 0; c < NUM_CHUNKS; c++) { + final float target = targetEntropies[c]; + smoothedEntropies[c] += (target - smoothedEntropies[c]) * SMOOTHING_FACTOR; + final float entropy = smoothedEntropies[c]; - if (entropy < 0.01f) { - continue; - } + if (entropy < 0.01f) { + continue; + } - final float frequency = FREQUENCIES[c]; - final float pulseRate = MIN_PULSE_RATE + entropy * (MAX_PULSE_RATE - MIN_PULSE_RATE); - final float volume = entropy * MASTER_VOLUME; + final float frequency = FREQUENCIES[c]; + final float pulseRate = MIN_PULSE_RATE + entropy * (MAX_PULSE_RATE - MIN_PULSE_RATE); + final float volume = entropy * MASTER_VOLUME; - phases[c] += frequency * dtPerSample; - if (phases[c] > 1.0f) { - phases[c] -= 1.0f; - } + phases[c] += frequency * dtPerSample; + if (phases[c] > 1.0f) { + phases[c] -= 1.0f; + } - pulsePhases[c] += pulseRate * dtPerSample; - if (pulsePhases[c] > 1.0f) { - pulsePhases[c] -= 1.0f; - } + pulsePhases[c] += pulseRate * dtPerSample; + if (pulsePhases[c] > 1.0f) { + pulsePhases[c] -= 1.0f; + } - final float wave = (float) Math.sin(phases[c] * 2 * Math.PI); - final float pulse = 0.5f + 0.5f * (float) Math.sin(pulsePhases[c] * 2 * Math.PI); - sample += wave * volume * pulse; - } + final float wave = (float) Math.sin(phases[c] * 2 * Math.PI); + final float pulse = 0.5f + 0.5f * (float) Math.sin(pulsePhases[c] * 2 * Math.PI); + sample += wave * volume * pulse; + } - buffer[i] = Math.max(-1.0f, Math.min(1.0f, sample)); - } + buffer[i] = Math.max(-1.0f, Math.min(1.0f, sample)); + } - device.writeSamples(buffer, 0, bufferSize); - } - } + device.writeSamples(buffer, 0, bufferSize); + } + } - @Override - public void close() { - running = false; - FunctionUtils.wrapCheckedException(() -> audioThread.join(AUDIO_THREAD_TIMEOUT)); - device.dispose(); - } + @Override + public void close() { + running = false; + FunctionUtils.wrapCheckedException(() -> audioThread.join(AUDIO_THREAD_TIMEOUT)); + device.dispose(); + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/life/LifeInput.java b/core/src/main/java/coffee/liz/abstractionengine/app/life/LifeInput.java index 36a1276..4007274 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/life/LifeInput.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/life/LifeInput.java @@ -10,6 +10,6 @@ import java.util.Set; @RequiredArgsConstructor @Data public class LifeInput { - private final Set<Vec2<Integer>> forceAliveCells; - private final Settings gameInstructions; + private final Set<Vec2<Integer>> forceAliveCells; + private final Settings gameInstructions; } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/life/LifeSystem.java b/core/src/main/java/coffee/liz/abstractionengine/app/life/LifeSystem.java index aea42c9..dcb6fb9 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/life/LifeSystem.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/life/LifeSystem.java @@ -12,74 +12,74 @@ import java.util.Set; /** Conway's game of life */ public class LifeSystem extends BaseGridIndexSystem<LifeInput> { - public final static int MAX_DECAY_STEPS = 12; - private final static float DECAY_STEP = 1.0f / MAX_DECAY_STEPS; - private final static Vec2<Integer> CONVOLUTION_RADIUS = Vec2i.builder().x(1).y(1).build(); + public final static int MAX_DECAY_STEPS = 12; + private final static float DECAY_STEP = 1.0f / MAX_DECAY_STEPS; + private final static Vec2<Integer> CONVOLUTION_RADIUS = Vec2i.builder().x(1).y(1).build(); - private final static Duration BETWEEN_UPDATES = Duration.ofMillis(200); + private final static Duration BETWEEN_UPDATES = Duration.ofMillis(200); - private Duration sinceUpdate = Duration.ZERO; + private Duration sinceUpdate = Duration.ZERO; - /** - * Creates a life system. - * - * @param dimensions - * grid size - */ - public LifeSystem(final Vec2<Integer> dimensions) { - super(dimensions); - } + /** + * Creates a life system. + * + * @param dimensions + * grid size + */ + public LifeSystem(final Vec2<Integer> dimensions) { + super(dimensions); + } - /** - * Advances the simulation. - * - * @param world - * world to update - * @param state - * input state for this tick - * @param dt - * time step - */ - @Override - public void update(final World<LifeInput> world, final LifeInput state, final Duration dt) { - super.update(world, state, dt); + /** + * Advances the simulation. + * + * @param world + * world to update + * @param state + * input state for this tick + * @param dt + * time step + */ + @Override + public void update(final World<LifeInput> world, final LifeInput state, final Duration dt) { + super.update(world, state, dt); - sinceUpdate = sinceUpdate.plus(dt); - if (BETWEEN_UPDATES.compareTo(sinceUpdate) <= 0) { - Mat2.convolveTorus(this.rows, CONVOLUTION_RADIUS, () -> 0, (entities, rel, prev) -> { - if (rel.equals(Vec2i.ZERO)) { - return prev; - } - return entities.stream().findFirst().map(entity -> entity.get(CellState.class)) - .map(cellState -> prev + (cellState.isAlive() ? 1 : 0)).orElse(prev); - }, (entities, neighboringAliveCells) -> entities.stream().findFirst().map(entity -> { - final CellState cellState = entity.get(CellState.class); - final float decay = cellState.getAlivePercentage(); + sinceUpdate = sinceUpdate.plus(dt); + if (BETWEEN_UPDATES.compareTo(sinceUpdate) <= 0) { + Mat2.convolveTorus(this.rows, CONVOLUTION_RADIUS, () -> 0, (entities, rel, prev) -> { + if (rel.equals(Vec2i.ZERO)) { + return prev; + } + return entities.stream().findFirst().map(entity -> entity.get(CellState.class)) + .map(cellState -> prev + (cellState.isAlive() ? 1 : 0)).orElse(prev); + }, (entities, neighboringAliveCells) -> entities.stream().findFirst().map(entity -> { + final CellState cellState = entity.get(CellState.class); + final float decay = cellState.getAlivePercentage(); - final boolean alive = cellState.isAlive(); - final boolean diesNow = alive && (neighboringAliveCells < 2 || neighboringAliveCells > 3); - final boolean spawnsNow = !alive && neighboringAliveCells == 3; - final boolean stillAlive = alive && !diesNow; + final boolean alive = cellState.isAlive(); + final boolean diesNow = alive && (neighboringAliveCells < 2 || neighboringAliveCells > 3); + final boolean spawnsNow = !alive && neighboringAliveCells == 3; + final boolean stillAlive = alive && !diesNow; - final CellState nextState; - if (spawnsNow || stillAlive) { - nextState = CellState.LIVE; - } else { - nextState = new CellState(decay - DECAY_STEP); - } + final CellState nextState; + if (spawnsNow || stillAlive) { + nextState = CellState.LIVE; + } else { + nextState = new CellState(decay - DECAY_STEP); + } - entity.add(nextState); - return entity; - })); - sinceUpdate = Duration.ZERO; - } + entity.add(nextState); + return entity; + })); + sinceUpdate = Duration.ZERO; + } - state.getForceAliveCells().forEach(cell -> { - if (!inBounds(cell)) { - return; - } - final Set<Entity> entities = rows.get(cell.getY()).get(cell.getX()); - entities.forEach(e -> e.add(CellState.LIVE)); - }); - } + state.getForceAliveCells().forEach(cell -> { + if (!inBounds(cell)) { + return; + } + final Set<Entity> entities = rows.get(cell.getY()).get(cell.getX()); + entities.forEach(e -> e.add(CellState.LIVE)); + }); + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/screen/GameScreen.java b/core/src/main/java/coffee/liz/abstractionengine/app/screen/GameScreen.java index cbdf51a..70bb973 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/screen/GameScreen.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/screen/GameScreen.java @@ -3,7 +3,7 @@ package coffee.liz.abstractionengine.app.screen; import coffee.liz.abstractionengine.app.AbstractionEngineGame; import coffee.liz.abstractionengine.app.screen.game.FrameState; import coffee.liz.abstractionengine.app.screen.game.GameScreenWorld; -import coffee.liz.abstractionengine.app.screen.game.system.GameWorldManagementSystem; +import coffee.liz.abstractionengine.app.screen.game.system.GridWorldBridgeSystem; import coffee.liz.abstractionengine.app.screen.game.system.RenderSystem; import coffee.liz.abstractionengine.app.utils.FunctionUtils; import coffee.liz.ecs.math.Vec2; @@ -17,54 +17,54 @@ import java.time.Duration; @RequiredArgsConstructor public class GameScreen implements Screen { - private final AbstractionEngineGame game; - private static final Vec2<Integer> GAME_GRID_SIZE = Vec2i.builder().x(20).y(20).build(); + private final AbstractionEngineGame game; + private static final Vec2<Integer> GAME_GRID_SIZE = Vec2i.builder().x(20).y(20).build(); - private GameScreenWorld outerWorld; - private FitViewport viewport; - private RenderSystem renderSystem; + private GameScreenWorld outerWorld; + private FitViewport viewport; + private RenderSystem renderSystem; - @Override - public void show() { - viewport = new FitViewport(AbstractionEngineGame.WORLD_SIZE.getX(), AbstractionEngineGame.WORLD_SIZE.getY()); - outerWorld = new GameScreenWorld(GAME_GRID_SIZE, viewport, game.settings); - outerWorld.getSystem(GameWorldManagementSystem.class).populateLevel(); - renderSystem = outerWorld.getSystem(RenderSystem.class); - Gdx.input.setInputProcessor(renderSystem.getInputProcessor()); - viewport.update(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), true); - renderSystem.resize(Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); - render(0); - } + @Override + public void show() { + viewport = new FitViewport(AbstractionEngineGame.WORLD_SIZE.getX(), AbstractionEngineGame.WORLD_SIZE.getY()); + outerWorld = new GameScreenWorld(GAME_GRID_SIZE, viewport, game.settings); + outerWorld.getSystem(GridWorldBridgeSystem.class).initLevel(); + renderSystem = outerWorld.getSystem(RenderSystem.class); + Gdx.input.setInputProcessor(renderSystem.getInputProcessor()); + viewport.update(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), true); + renderSystem.resize(Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); + render(0); + } - @Override - public void render(final float delta) { - viewport.apply(); - final FrameState state = FrameState.builder().settings(game.settings).isKeyPressed(Gdx.input::isKeyPressed) - .build(); - outerWorld.update(state, Duration.ofMillis((long) (delta * 1000))); - } + @Override + public void render(final float delta) { + viewport.apply(); + final FrameState state = FrameState.builder().settings(game.settings).isKeyPressed(Gdx.input::isKeyPressed) + .build(); + outerWorld.update(state, Duration.ofMillis((long) (delta * 1000))); + } - @Override - public void resize(final int width, final int height) { - viewport.update(width, height, true); - renderSystem.resize(width, height); - } + @Override + public void resize(final int width, final int height) { + viewport.update(width, height, true); + renderSystem.resize(width, height); + } - @Override - public void pause() { - } + @Override + public void pause() { + } - @Override - public void resume() { - } + @Override + public void resume() { + } - @Override - public void hide() { - dispose(); - } + @Override + public void hide() { + dispose(); + } - @Override - public void dispose() { - FunctionUtils.wrapCheckedException(outerWorld::close); - } + @Override + public void dispose() { + FunctionUtils.wrapCheckedException(outerWorld::close); + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/screen/MainMenu.java b/core/src/main/java/coffee/liz/abstractionengine/app/screen/MainMenu.java index 30cc01f..46b5192 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/screen/MainMenu.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/screen/MainMenu.java @@ -29,140 +29,140 @@ import java.util.Set; @RequiredArgsConstructor public class MainMenu implements Screen { - public static final Vec2<Integer> GRID_DIMENSIONS = Vec2i.builder().x(50).y(50).build(); + public static final Vec2<Integer> GRID_DIMENSIONS = Vec2i.builder().x(50).y(50).build(); - private static final Vec2<Float> BUTTON_SIZE = AbstractionEngineGame.WORLD_SIZE.scale(1 / 4f, 1 / 15f); - private static final float PARALLAX_STRENGTH = 3f; + private static final Vec2<Float> BUTTON_SIZE = AbstractionEngineGame.WORLD_SIZE.scale(1 / 4f, 1 / 15f); + private static final float PARALLAX_STRENGTH = 3f; - private final World<LifeInput> world = new DAGWorld<>(Map.of(LifeSystem.class, new LifeSystem(GRID_DIMENSIONS), - EntropyAudioSystem.class, new EntropyAudioSystem())); - private final AbstractionEngineGame game; + private final World<LifeInput> world = new DAGWorld<>(Map.of(LifeSystem.class, new LifeSystem(GRID_DIMENSIONS), + EntropyAudioSystem.class, new EntropyAudioSystem())); + private final AbstractionEngineGame game; - private final Set<Vec2<Integer>> initialAlive = createGliderPattern(); + private final Set<Vec2<Integer>> initialAlive = createGliderPattern(); - private Stage stage; - private LifeGridActor lifeGridActor; + private Stage stage; + private LifeGridActor lifeGridActor; - /** - * Screen lifecycle hook. - */ - @Override - public void show() { - Mat2.init(GRID_DIMENSIONS, pos -> world.createEntity().add(new GridPosition(pos)) - .add(initialAlive.contains(pos) ? CellState.LIVE : CellState.DEAD)); + /** + * Screen lifecycle hook. + */ + @Override + public void show() { + Mat2.init(GRID_DIMENSIONS, pos -> world.createEntity().add(new GridPosition(pos)) + .add(initialAlive.contains(pos) ? CellState.LIVE : CellState.DEAD)); - stage = new Stage(game.viewport, game.batch); - lifeGridActor = new LifeGridActor(game.shapeRenderer, world, GRID_DIMENSIONS, game.settings); - lifeGridActor.setViewport(game.viewport); - stage.addActor(lifeGridActor); + stage = new Stage(game.viewport, game.batch); + lifeGridActor = new LifeGridActor(game.shapeRenderer, world, GRID_DIMENSIONS, game.settings); + lifeGridActor.setViewport(game.viewport); + stage.addActor(lifeGridActor); - final Button playButton = createButton("play", (game.viewport.getWorldWidth() - BUTTON_SIZE.getX()) / 2f, - (game.viewport.getWorldHeight() - BUTTON_SIZE.getY()) / 2f); - playButton.setOnClick(() -> game.setScreen(new GameScreen(game))); - stage.addActor(playButton); + final Button playButton = createButton("play", (game.viewport.getWorldWidth() - BUTTON_SIZE.getX()) / 2f, + (game.viewport.getWorldHeight() - BUTTON_SIZE.getY()) / 2f); + playButton.setOnClick(() -> game.setScreen(new GameScreen(game))); + stage.addActor(playButton); - Gdx.input.setInputProcessor(stage); - } + Gdx.input.setInputProcessor(stage); + } - private Button createButton(final String text, final float x, final float y) { - final Button button = new Button(game.shapeRenderer, game.font, text); - button.setPosition(x, y); - button.setSize(BUTTON_SIZE.getX(), BUTTON_SIZE.getY()); - button.addButtonListener(); - return button; - } + private Button createButton(final String text, final float x, final float y) { + final Button button = new Button(game.shapeRenderer, game.font, text); + button.setPosition(x, y); + button.setSize(BUTTON_SIZE.getX(), BUTTON_SIZE.getY()); + button.addButtonListener(); + return button; + } - private static Set<Vec2<Integer>> createGliderPattern() { - final int[][] downRight = {{0, 1, 0}, {0, 1, 1}, {1, 0, 1}}; - final int[][] downLeft = {{0, 1, 0}, {1, 1, 0}, {1, 0, 1}}; - final int[][] upRight = {{1, 0, 1}, {0, 1, 1}, {0, 1, 0}}; - final int[][] upLeft = {{1, 0, 1}, {1, 1, 0}, {0, 1, 0}}; + private static Set<Vec2<Integer>> createGliderPattern() { + final int[][] downRight = {{0, 1, 0}, {0, 1, 1}, {1, 0, 1}}; + final int[][] downLeft = {{0, 1, 0}, {1, 1, 0}, {1, 0, 1}}; + final int[][] upRight = {{1, 0, 1}, {0, 1, 1}, {0, 1, 0}}; + final int[][] upLeft = {{1, 0, 1}, {1, 1, 0}, {0, 1, 0}}; - final Set<Vec2<Integer>> pattern = new HashSet<>(); - stamp(pattern, upRight, 2, 2); - stamp(pattern, upLeft, GRID_DIMENSIONS.getX() - 5, 2); - stamp(pattern, downRight, 2, GRID_DIMENSIONS.getY() - 5); - stamp(pattern, downLeft, GRID_DIMENSIONS.getX() - 5, GRID_DIMENSIONS.getY() - 5); - return pattern; - } + final Set<Vec2<Integer>> pattern = new HashSet<>(); + stamp(pattern, upRight, 2, 2); + stamp(pattern, upLeft, GRID_DIMENSIONS.getX() - 5, 2); + stamp(pattern, downRight, 2, GRID_DIMENSIONS.getY() - 5); + stamp(pattern, downLeft, GRID_DIMENSIONS.getX() - 5, GRID_DIMENSIONS.getY() - 5); + return pattern; + } - private static void stamp(final Set<Vec2<Integer>> cells, final int[][] pattern, final int offsetX, - final int offsetY) { - for (int y = 0; y < pattern.length; y++) - for (int x = 0; x < pattern[y].length; x++) - if (pattern[y][x] == 1) - cells.add(Vec2i.builder().x(offsetX + x).y(offsetY + y).build()); - } + private static void stamp(final Set<Vec2<Integer>> cells, final int[][] pattern, final int offsetX, + final int offsetY) { + for (int y = 0; y < pattern.length; y++) + for (int x = 0; x < pattern[y].length; x++) + if (pattern[y][x] == 1) + cells.add(Vec2i.builder().x(offsetX + x).y(offsetY + y).build()); + } - /** - * Screen render hook. - * - * @param delta - * time since last frame - */ - @Override - public void render(final float delta) { - game.viewport.apply(); + /** + * Screen render hook. + * + * @param delta + * time since last frame + */ + @Override + public void render(final float delta) { + game.viewport.apply(); - ScreenUtils.clear(Theme.BG); + ScreenUtils.clear(Theme.BG); - updateParallax(); - stage.act(delta); + updateParallax(); + stage.act(delta); - stage.draw(); - } + stage.draw(); + } - private void updateParallax() { - final Vector3 cursor = game.viewport.unproject(new Vector3(Gdx.input.getX(), Gdx.input.getY(), 0)); - final Vec2<Float> worldSize = Vec2f.builder().x(game.viewport.getWorldWidth()).y(game.viewport.getWorldHeight()) - .build(); - final Vec2<Float> normalized = Vec2f.builder().x(cursor.x).y(cursor.y).build() - .scale(worldSize.transform(x -> 1f / x, y -> 1f / y)).transform(x -> x - 0.5f, y -> y - 0.5f); - final Vec2<Float> parallax = normalized.scale(-PARALLAX_STRENGTH, -PARALLAX_STRENGTH); - lifeGridActor.setParallaxOffset(parallax); - } + private void updateParallax() { + final Vector3 cursor = game.viewport.unproject(new Vector3(Gdx.input.getX(), Gdx.input.getY(), 0)); + final Vec2<Float> worldSize = Vec2f.builder().x(game.viewport.getWorldWidth()).y(game.viewport.getWorldHeight()) + .build(); + final Vec2<Float> normalized = Vec2f.builder().x(cursor.x).y(cursor.y).build() + .scale(worldSize.transform(x -> 1f / x, y -> 1f / y)).transform(x -> x - 0.5f, y -> y - 0.5f); + final Vec2<Float> parallax = normalized.scale(-PARALLAX_STRENGTH, -PARALLAX_STRENGTH); + lifeGridActor.setParallaxOffset(parallax); + } - /** - * Screen resize hook. - * - * @param width - * new width in pixels - * @param height - * new height in pixels - */ - @Override - public void resize(final int width, final int height) { - game.viewport.update(width, height, true); - } + /** + * Screen resize hook. + * + * @param width + * new width in pixels + * @param height + * new height in pixels + */ + @Override + public void resize(final int width, final int height) { + game.viewport.update(width, height, true); + } - /** - * Screen lifecycle hook. - */ - @Override - public void pause() { - } + /** + * Screen lifecycle hook. + */ + @Override + public void pause() { + } - /** - * Screen lifecycle hook. - */ - @Override - public void resume() { - } + /** + * Screen lifecycle hook. + */ + @Override + public void resume() { + } - /** - * Screen lifecycle hook. - */ - @Override - public void hide() { - dispose(); - } + /** + * Screen lifecycle hook. + */ + @Override + public void hide() { + dispose(); + } - /** - * Screen cleanup hook. - */ - @Override - public void dispose() { - FunctionUtils.wrapCheckedException(world::close); - stage.dispose(); - } + /** + * Screen cleanup hook. + */ + @Override + public void dispose() { + FunctionUtils.wrapCheckedException(world::close); + stage.dispose(); + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/screen/ScrollLogo.java b/core/src/main/java/coffee/liz/abstractionengine/app/screen/ScrollLogo.java index bb8aef2..c0183d3 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/screen/ScrollLogo.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/screen/ScrollLogo.java @@ -15,138 +15,138 @@ import java.time.Duration; @RequiredArgsConstructor public class ScrollLogo implements Screen { - private static final Duration SHOW_FOR = Duration.ofSeconds(7); - private static final float LOGO_HEIGHT = 70f; - private static final float PENGUIN_SIZE = 20f; - private static final float SCROLL_PERCENT = 0.4f; - private static final float EEPY_PERCENT = 0.3f; - private static final float EEP_PERCENT = 0.15f; - private static final float FADE_PERCENT = 0.15f; + private static final Duration SHOW_FOR = Duration.ofSeconds(7); + private static final float LOGO_HEIGHT = 70f; + private static final float PENGUIN_SIZE = 20f; + private static final float SCROLL_PERCENT = 0.4f; + private static final float EEPY_PERCENT = 0.3f; + private static final float EEP_PERCENT = 0.15f; + private static final float FADE_PERCENT = 0.15f; - private final AbstractionEngineGame game; + private final AbstractionEngineGame game; - private Stage stage; - private Logo logo; - private Penguin penguin; - private Group animationGroup; + private Stage stage; + private Logo logo; + private Penguin penguin; + private Group animationGroup; - /** - * Screen lifecycle hook. - */ - @Override - public void show() { - stage = new Stage(game.viewport, game.batch); - logo = new Logo(); - penguin = new Penguin(); - animationGroup = new Group(); + /** + * Screen lifecycle hook. + */ + @Override + public void show() { + stage = new Stage(game.viewport, game.batch); + logo = new Logo(); + penguin = new Penguin(); + animationGroup = new Group(); - final float worldWidth = game.viewport.getWorldWidth(); - final float worldHeight = game.viewport.getWorldHeight(); - final float logoWidth = LOGO_HEIGHT * ((float) logo.getWidth() / logo.getHeight()); + final float worldWidth = game.viewport.getWorldWidth(); + final float worldHeight = game.viewport.getWorldHeight(); + final float logoWidth = LOGO_HEIGHT * ((float) logo.getWidth() / logo.getHeight()); - logo.setSize(logoWidth, LOGO_HEIGHT); - logo.setPosition(0, 0); + logo.setSize(logoWidth, LOGO_HEIGHT); + logo.setPosition(0, 0); - penguin.setSize(PENGUIN_SIZE, PENGUIN_SIZE); - penguin.setPosition((logoWidth - PENGUIN_SIZE) / 2f, -PENGUIN_SIZE - 6f); - penguin.setState(Penguin.State.TOBAGGON); + penguin.setSize(PENGUIN_SIZE, PENGUIN_SIZE); + penguin.setPosition((logoWidth - PENGUIN_SIZE) / 2f, -PENGUIN_SIZE - 6f); + penguin.setState(Penguin.State.TOBAGGON); - animationGroup.addActor(logo); - animationGroup.addActor(penguin); - animationGroup.setPosition((worldWidth - logoWidth) / 2f, worldHeight); + animationGroup.addActor(logo); + animationGroup.addActor(penguin); + animationGroup.setPosition((worldWidth - logoWidth) / 2f, worldHeight); - stage.addActor(animationGroup); + stage.addActor(animationGroup); - setupAnimations(worldWidth, worldHeight, logoWidth); - } + setupAnimations(worldWidth, worldHeight, logoWidth); + } - private void setupAnimations(final float worldWidth, final float worldHeight, final float logoWidth) { - final float totalSeconds = SHOW_FOR.toMillis() / 1000f; - final float scrollDuration = totalSeconds * SCROLL_PERCENT; - final float eepyDuration = totalSeconds * EEPY_PERCENT; - final float eepDuration = totalSeconds * EEP_PERCENT; - final float fadeDuration = totalSeconds * FADE_PERCENT; + private void setupAnimations(final float worldWidth, final float worldHeight, final float logoWidth) { + final float totalSeconds = SHOW_FOR.toMillis() / 1000f; + final float scrollDuration = totalSeconds * SCROLL_PERCENT; + final float eepyDuration = totalSeconds * EEPY_PERCENT; + final float eepDuration = totalSeconds * EEP_PERCENT; + final float fadeDuration = totalSeconds * FADE_PERCENT; - final float targetY = (worldHeight - LOGO_HEIGHT) / 2f; + final float targetY = (worldHeight - LOGO_HEIGHT) / 2f; - animationGroup - .addAction(Actions.sequence(Actions.moveTo((worldWidth - logoWidth) / 2f, targetY, scrollDuration), - Actions.run(() -> penguin.setState(Penguin.State.EEPY)), Actions.delay(eepyDuration), - Actions.run(() -> penguin.setState(Penguin.State.EEPING)), Actions.delay(eepDuration), - Actions.fadeOut(fadeDuration), Actions.run(this::requestTransition))); - } + animationGroup + .addAction(Actions.sequence(Actions.moveTo((worldWidth - logoWidth) / 2f, targetY, scrollDuration), + Actions.run(() -> penguin.setState(Penguin.State.EEPY)), Actions.delay(eepyDuration), + Actions.run(() -> penguin.setState(Penguin.State.EEPING)), Actions.delay(eepDuration), + Actions.fadeOut(fadeDuration), Actions.run(this::requestTransition))); + } - /** - * Screen render hook. - * - * @param delta - * time since last frame - */ - @Override - public void render(final float delta) { - game.viewport.apply(); - ScreenUtils.clear(Theme.BG); - if (game.settings.isSkipIntro()) { - requestTransition(); - return; - } + /** + * Screen render hook. + * + * @param delta + * time since last frame + */ + @Override + public void render(final float delta) { + game.viewport.apply(); + ScreenUtils.clear(Theme.BG); + if (game.settings.isSkipIntro()) { + requestTransition(); + return; + } - stage.act(delta); - stage.draw(); - } + stage.act(delta); + stage.draw(); + } - /** - * Screen resize hook. - * - * @param width - * new width in pixels - * @param height - * new height in pixels - */ - @Override - public void resize(final int width, final int height) { - game.viewport.update(width, height, true); - } + /** + * Screen resize hook. + * + * @param width + * new width in pixels + * @param height + * new height in pixels + */ + @Override + public void resize(final int width, final int height) { + game.viewport.update(width, height, true); + } - /** - * Screen lifecycle hook. - */ - @Override - public void pause() { - } + /** + * Screen lifecycle hook. + */ + @Override + public void pause() { + } - /** - * Screen lifecycle hook. - */ - @Override - public void resume() { - } + /** + * Screen lifecycle hook. + */ + @Override + public void resume() { + } - /** - * Screen lifecycle hook. - */ - @Override - public void hide() { - dispose(); - } + /** + * Screen lifecycle hook. + */ + @Override + public void hide() { + dispose(); + } - /** - * Screen cleanup hook. - */ - @Override - public void dispose() { - if (logo != null) { - logo.dispose(); - } - if (penguin != null) { - penguin.dispose(); - } - if (stage != null) { - stage.dispose(); - } - } + /** + * Screen cleanup hook. + */ + @Override + public void dispose() { + if (logo != null) { + logo.dispose(); + } + if (penguin != null) { + penguin.dispose(); + } + if (stage != null) { + stage.dispose(); + } + } - private void requestTransition() { - game.setScreen(new MainMenu(game)); - } + private void requestTransition() { + game.setScreen(new MainMenu(game)); + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/FrameState.java b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/FrameState.java index 3f31530..275771f 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/FrameState.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/FrameState.java @@ -11,6 +11,6 @@ import java.util.function.Predicate; @Getter @RequiredArgsConstructor public final class FrameState { - private final Settings settings; - private final Predicate<Integer> isKeyPressed; + private final Settings settings; + private final Predicate<Integer> isKeyPressed; } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/GameScreenWorld.java b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/GameScreenWorld.java index a0c0b5b..137a5b0 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/GameScreenWorld.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/GameScreenWorld.java @@ -1,7 +1,7 @@ package coffee.liz.abstractionengine.app.screen.game; import coffee.liz.abstractionengine.app.config.Settings; -import coffee.liz.abstractionengine.app.screen.game.system.GameWorldManagementSystem; +import coffee.liz.abstractionengine.app.screen.game.system.GridWorldBridgeSystem; import coffee.liz.abstractionengine.app.screen.game.system.GridInterpolationSystem; import coffee.liz.abstractionengine.app.screen.game.system.InputSystem; import coffee.liz.abstractionengine.app.screen.game.system.RenderSystem; @@ -12,9 +12,9 @@ import com.badlogic.gdx.utils.viewport.FitViewport; import java.util.Map; public class GameScreenWorld extends DAGWorld<FrameState> { - public GameScreenWorld(final Vec2<Integer> gridSize, final FitViewport viewport, final Settings settings) { - super(Map.of(InputSystem.class, new InputSystem(), GameWorldManagementSystem.class, - new GameWorldManagementSystem(gridSize), GridInterpolationSystem.class, new GridInterpolationSystem(), - RenderSystem.class, new RenderSystem(viewport, gridSize, settings))); - } + public GameScreenWorld(final Vec2<Integer> gridSize, final FitViewport viewport, final Settings settings) { + super(Map.of(InputSystem.class, new InputSystem(), GridWorldBridgeSystem.class, + new GridWorldBridgeSystem(gridSize), GridInterpolationSystem.class, new GridInterpolationSystem(), + RenderSystem.class, new RenderSystem(viewport, gridSize, settings))); + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/component/GridEntityRef.java b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/component/ActorHolder.java index 788d530..ad9c432 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/component/GridEntityRef.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/component/ActorHolder.java @@ -1,11 +1,12 @@ package coffee.liz.abstractionengine.app.screen.game.component; import coffee.liz.ecs.model.Component; +import com.badlogic.gdx.scenes.scene2d.Actor; import lombok.Getter; import lombok.RequiredArgsConstructor; -@RequiredArgsConstructor @Getter -public final class GridEntityRef implements Component { - private final int gridEntityId; +@RequiredArgsConstructor +public class ActorHolder implements Component { + private final Actor actor; } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/component/ExternalWorldEntityRef.java b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/component/ExternalWorldEntityRef.java new file mode 100644 index 0000000..a544a70 --- /dev/null +++ b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/component/ExternalWorldEntityRef.java @@ -0,0 +1,12 @@ +package coffee.liz.abstractionengine.app.screen.game.component; + +import coffee.liz.ecs.model.Component; +import coffee.liz.ecs.model.Entity; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Getter +public final class ExternalWorldEntityRef implements Component { + private final Entity entity; +} diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/component/InterpolatedPosition.java b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/component/InterpolatedPosition.java deleted file mode 100644 index 6700575..0000000 --- a/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/component/InterpolatedPosition.java +++ /dev/null @@ -1,18 +0,0 @@ -package coffee.liz.abstractionengine.app.screen.game.component; - -import coffee.liz.ecs.math.Vec2; -import coffee.liz.ecs.model.Component; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -@Getter -public final class InterpolatedPosition implements Component { - private final Vec2<Float> from; - private final Vec2<Float> to; - private final Vec2<Float> current; - - public static InterpolatedPosition atRest(final Vec2<Float> position) { - return new InterpolatedPosition(position, position, position); - } -} diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/GameWorldManagementSystem.java b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/GameWorldManagementSystem.java deleted file mode 100644 index 74355d1..0000000 --- a/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/GameWorldManagementSystem.java +++ /dev/null @@ -1,100 +0,0 @@ -package coffee.liz.abstractionengine.app.screen.game.system; - -import coffee.liz.abstractionengine.AbstractionEngineGridWorld; -import coffee.liz.abstractionengine.app.screen.game.FrameState; -import coffee.liz.abstractionengine.app.screen.game.component.GridEntityRef; -import coffee.liz.abstractionengine.app.screen.game.component.InterpolatedPosition; -import coffee.liz.abstractionengine.app.utils.FunctionUtils; -import coffee.liz.abstractionengine.entity.EntityFactory; -import coffee.liz.abstractionengine.entity.EntityType; -import coffee.liz.abstractionengine.grid.component.GridInputState; -import coffee.liz.abstractionengine.grid.component.GridPosition; -import coffee.liz.ecs.math.Vec2; -import coffee.liz.ecs.math.Vec2i; -import coffee.liz.ecs.model.Entity; -import coffee.liz.ecs.model.System; -import coffee.liz.ecs.model.World; -import lombok.Getter; - -import java.time.Duration; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -public class GameWorldManagementSystem implements System<FrameState> { - private final AbstractionEngineGridWorld gridWorld; - private final Vec2<Integer> gridSize; - private final Map<Integer, Entity> gridToOuterMap = new HashMap<>(); - - @Getter - private boolean stepped = false; - - public GameWorldManagementSystem(final Vec2<Integer> gridSize) { - this.gridSize = gridSize; - this.gridWorld = new AbstractionEngineGridWorld(gridSize); - } - - public void populateLevel() { - EntityFactory.populateRect(gridSize, gridWorld, EntityFactory.Wall::addToWorld); - EntityFactory.Player.addToWorld(gridWorld, Vec2i.builder().y(10).x(10).build()); - EntityFactory.Abstraction.addToWorld(gridWorld, Vec2i.builder().y(10).x(12).build()); - EntityFactory.Abstraction.addToWorld(gridWorld, Vec2i.builder().y(10).x(13).build()); - gridWorld.update(GridInputState.builder().movement(Vec2i.ZERO).build()); - } - - @Override - public Collection<Class<? extends System<FrameState>>> getDependencies() { - return Set.of(InputSystem.class); - } - - @Override - public void update(final World<FrameState> world, final FrameState state, final Duration dt) { - final InputSystem inputSystem = world.getSystem(InputSystem.class); - final Vec2<Integer> movement = inputSystem.getStepMovement(); - - stepped = !movement.equals(Vec2i.ZERO); - if (stepped) { - final GridInputState gridInput = GridInputState.builder().movement(movement).build(); - gridWorld.update(gridInput); - } - - syncEntities(world); - } - - private void syncEntities(final World<FrameState> outerWorld) { - final Set<Entity> gridEntities = gridWorld.query(Set.of(GridPosition.class, EntityType.class)); - final Set<Integer> seenGridIds = new HashSet<>(); - - for (final Entity gridEntity : gridEntities) { - seenGridIds.add(gridEntity.getId()); - - if (!gridToOuterMap.containsKey(gridEntity.getId())) { - final Entity outer = outerWorld.createEntity(); - final Vec2<Float> pos = gridEntity.get(GridPosition.class).getPosition().floatValue(); - outer.add(new GridEntityRef(gridEntity.getId())); - outer.add(gridEntity.get(EntityType.class)); - outer.add(InterpolatedPosition.atRest(pos)); - gridToOuterMap.put(gridEntity.getId(), outer); - } else if (stepped) { - final Entity outer = gridToOuterMap.get(gridEntity.getId()); - final InterpolatedPosition current = outer.get(InterpolatedPosition.class); - final Vec2<Float> newTarget = gridEntity.get(GridPosition.class).getPosition().floatValue(); - outer.add(new InterpolatedPosition(current.getCurrent(), newTarget, current.getCurrent())); - } - } - - final Set<Integer> removedIds = new HashSet<>(gridToOuterMap.keySet()); - removedIds.removeAll(seenGridIds); - for (final int removedId : removedIds) { - final Entity outer = gridToOuterMap.remove(removedId); - outerWorld.removeEntity(outer); - } - } - - @Override - public void close() { - FunctionUtils.wrapCheckedException(gridWorld::close); - } -} diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/GridInterpolationSystem.java b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/GridInterpolationSystem.java index 2e4826d..0d2d34b 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/GridInterpolationSystem.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/GridInterpolationSystem.java @@ -1,9 +1,13 @@ package coffee.liz.abstractionengine.app.screen.game.system; import coffee.liz.abstractionengine.app.screen.game.FrameState; -import coffee.liz.abstractionengine.app.screen.game.component.InterpolatedPosition; +import coffee.liz.abstractionengine.app.screen.game.component.ActorHolder; +import coffee.liz.abstractionengine.app.screen.game.component.ExternalWorldEntityRef; +import coffee.liz.abstractionengine.entity.EntityType; +import coffee.liz.abstractionengine.grid.component.GridPosition; import coffee.liz.ecs.math.Vec2; import coffee.liz.ecs.math.Vec2f; +import coffee.liz.ecs.model.Entity; import coffee.liz.ecs.model.System; import coffee.liz.ecs.model.World; @@ -11,36 +15,56 @@ import java.time.Duration; import java.util.Collection; import java.util.Set; +import com.badlogic.gdx.math.Interpolation; +import com.badlogic.gdx.scenes.scene2d.Actor; +import com.badlogic.gdx.scenes.scene2d.actions.MoveToAction; +import lombok.extern.log4j.Log4j2; + +@Log4j2 public class GridInterpolationSystem implements System<FrameState> { - private static final Duration INTERPOLATION_TIME = Duration.ofMillis(50); + private static final Duration INTERPOLATION_TIME = Duration.ofMillis(60); - private Duration elapsed = Duration.ZERO; + @Override + public Collection<Class<? extends System<FrameState>>> getDependencies() { + return Set.of(GridWorldBridgeSystem.class); + } - @Override - public Collection<Class<? extends System<FrameState>>> getDependencies() { - return Set.of(GameWorldManagementSystem.class); - } + @Override + public void update(final World<FrameState> world, final FrameState state, final Duration dt) { + final GridWorldBridgeSystem bridgeSystem = world.getSystem(GridWorldBridgeSystem.class); - @Override - public void update(final World<FrameState> world, final FrameState state, final Duration dt) { - final GameWorldManagementSystem gameWorldSystem = world.getSystem(GameWorldManagementSystem.class); + world.query(Set.of(ExternalWorldEntityRef.class, ActorHolder.class)).forEach(entity -> { + final Actor actor = entity.get(ActorHolder.class).getActor(); + final boolean isAlreadyInterpolating = !actor.getActions().isEmpty(); + if (isAlreadyInterpolating) { + return; + } - if (gameWorldSystem.isStepped()) { - elapsed = Duration.ZERO; - } + final Entity ref = entity.get(ExternalWorldEntityRef.class).getEntity(); + if (!ref.has(GridPosition.class)) { + return; + } - elapsed = elapsed.plus(dt); - final float t = Math.min(1.0f, (float) elapsed.toMillis() / INTERPOLATION_TIME.toMillis()); + final Vec2<Float> position = Vec2f.builder().x(actor.getX()).y(actor.getY()).build(); + final GridPosition gridPosition = ref.get(GridPosition.class); + final Vec2<Float> target = gridPosition.getPosition().floatValue().scale(bridgeSystem.getGridToWorld()); + final boolean isAtTarget = target.minus(position).length() < 0.05; + if (isAtTarget) { + return; + } - world.query(Set.of(InterpolatedPosition.class)).forEach(entity -> { - final InterpolatedPosition pos = entity.get(InterpolatedPosition.class); - final Vec2<Float> interpolated = lerp(pos.getFrom(), pos.getTo(), t); - entity.add(new InterpolatedPosition(pos.getFrom(), pos.getTo(), interpolated)); - }); - } + final MoveToAction momentumAction = new MoveToAction(); + momentumAction.setInterpolation(interpolationType(ref.get(EntityType.class))); + momentumAction.setPosition(target.getX(), target.getY()); + momentumAction.setDuration(INTERPOLATION_TIME.toMillis() / 1000f); + actor.addAction(momentumAction); + }); + } - private static Vec2<Float> lerp(final Vec2<Float> from, final Vec2<Float> to, final float t) { - return Vec2f.builder().x(from.getX() + (to.getX() - from.getX()) * t) - .y(from.getY() + (to.getY() - from.getY()) * t).build(); - } + private Interpolation interpolationType(final EntityType type) { + return switch(type) { + case PLAYER -> Interpolation.pow3; + default -> new Interpolation.Swing(1.15f); + }; + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/GridWorldBridgeSystem.java b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/GridWorldBridgeSystem.java new file mode 100644 index 0000000..52a21a6 --- /dev/null +++ b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/GridWorldBridgeSystem.java @@ -0,0 +1,119 @@ +package coffee.liz.abstractionengine.app.screen.game.system; + +import coffee.liz.abstractionengine.AbstractionEngineGridWorld; +import coffee.liz.abstractionengine.app.AbstractionEngineGame; +import coffee.liz.abstractionengine.app.actor.Abstraction; +import coffee.liz.abstractionengine.app.actor.Penguin; +import coffee.liz.abstractionengine.app.actor.Player; +import coffee.liz.abstractionengine.app.actor.Wall; +import coffee.liz.abstractionengine.app.screen.game.FrameState; +import coffee.liz.abstractionengine.app.screen.game.component.ActorHolder; +import coffee.liz.abstractionengine.app.screen.game.component.ExternalWorldEntityRef; +import coffee.liz.abstractionengine.app.utils.FunctionUtils; +import coffee.liz.abstractionengine.entity.EntityFactory; +import coffee.liz.abstractionengine.entity.EntityType; +import coffee.liz.abstractionengine.grid.component.GridInputState; +import coffee.liz.abstractionengine.grid.component.GridPosition; +import coffee.liz.ecs.math.Vec2; +import coffee.liz.ecs.math.Vec2i; +import coffee.liz.ecs.model.Entity; +import coffee.liz.ecs.model.System; +import coffee.liz.ecs.model.World; +import lombok.Getter; + +import java.time.Duration; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import com.badlogic.gdx.scenes.scene2d.Actor; +import com.badlogic.gdx.scenes.scene2d.Stage; + +public class GridWorldBridgeSystem implements System<FrameState> { + private final Map<Integer, Entity> outerIdToInnerEntity = new HashMap<>(); + private final AbstractionEngineGridWorld outerWorld; + private final Vec2<Integer> gridSize; + + @Getter + private final Vec2<Float> gridToWorld; + @Getter + private final Vec2<Float> worldToGrid; + + public GridWorldBridgeSystem(final Vec2<Integer> gridSize) { + this.gridSize = gridSize; + this.outerWorld = new AbstractionEngineGridWorld(gridSize); + + this.gridToWorld = AbstractionEngineGame.WORLD_SIZE.scale(gridSize.floatValue().transform(x -> 1f / x, y -> 1f / y)); + this.worldToGrid = gridSize.floatValue().scale(AbstractionEngineGame.WORLD_SIZE.floatValue().transform(x -> 1f/x, y -> 1f/y)); + } + + public void initLevel() { + EntityFactory.populateRect(gridSize, outerWorld, EntityFactory.Wall::addToWorld); + EntityFactory.Player.addToWorld(outerWorld, Vec2i.builder().y(10).x(10).build()); + EntityFactory.Abstraction.addToWorld(outerWorld, Vec2i.builder().y(10).x(12).build()); + EntityFactory.Abstraction.addToWorld(outerWorld, Vec2i.builder().y(10).x(13).build()); + outerWorld.update(GridInputState.builder().movement(Vec2i.ZERO).build()); + } + + @Override + public Collection<Class<? extends System<FrameState>>> getDependencies() { + return Set.of(InputSystem.class); + } + + @Override + public void update(final World<FrameState> world, final FrameState state, final Duration dt) { + final InputSystem inputSystem = world.getSystem(InputSystem.class); + final GridInputState gridInputState = inputSystem.getGridInputState(); + outerWorld.update(gridInputState); + syncEntities(world); + } + + private void syncEntities(final World<FrameState> world) { + final Set<Entity> gridEntities = outerWorld.query(Set.of(GridPosition.class, EntityType.class)); + final Set<Integer> seenGridIds = new HashSet<>(); + + for (final Entity gridEntity : gridEntities) { + seenGridIds.add(gridEntity.getId()); + + final boolean isNewEntity = !outerIdToInnerEntity.containsKey(gridEntity.getId()); + if (!isNewEntity) { + continue; + } + + final Entity outer = world.createEntity(); + outer.add(new ActorHolder( + createActor(gridEntity.get(EntityType.class), world.getSystem(RenderSystem.class).getStage()))); + outer.add(new ExternalWorldEntityRef(gridEntity)); + outer.add(gridEntity.get(EntityType.class)); + outerIdToInnerEntity.put(gridEntity.getId(), outer); + } + + final Set<Integer> outerRemoved = gridEntities.stream().map(Entity::getId) + .filter(id -> !seenGridIds.contains(id)).collect(Collectors.toSet()); + + for (final int removedId : outerRemoved) { + world.removeEntity(outerIdToInnerEntity.get(removedId)); + outerIdToInnerEntity.remove(removedId); + } + } + + @Override + public void close() { + FunctionUtils.wrapCheckedException(outerWorld::close); + } + + private Actor createActor(final EntityType type, final Stage stage) { + final Actor actor = switch (type) { + case PLAYER -> new Penguin(); + case ABSTRACTION -> new Abstraction(); + case WALL -> new Wall(); + default -> throw new UnsupportedOperationException(); + }; + actor.setBounds(0, 0, getGridToWorld().getX(), getGridToWorld().getX()); + stage.addActor(actor); + return actor; + } +} diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/InputSystem.java b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/InputSystem.java index b311221..b8e3782 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/InputSystem.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/InputSystem.java @@ -2,73 +2,75 @@ package coffee.liz.abstractionengine.app.screen.game.system; import coffee.liz.abstractionengine.app.config.KeyBinds; import coffee.liz.abstractionengine.app.screen.game.FrameState; +import coffee.liz.abstractionengine.grid.component.GridInputState; import coffee.liz.ecs.math.Vec2; import coffee.liz.ecs.math.Vec2i; import coffee.liz.ecs.model.System; import coffee.liz.ecs.model.World; -import lombok.Getter; import java.time.Duration; import java.util.Collection; import java.util.Set; public class InputSystem implements System<FrameState> { - private static final Duration INITIAL_DELAY = Duration.ofMillis(200); - private static final Duration REPEAT_INTERVAL = Duration.ofMillis(80); + private static final Duration INITIAL_DELAY = Duration.ofMillis(200); + private static final Duration REPEAT_INTERVAL = Duration.ofMillis(80); - private KeyBinds.Action heldAction = null; - private Duration heldTime = Duration.ZERO; - private boolean pastInitialDelay = false; + private KeyBinds.Action heldAction = null; + private Duration heldTime = Duration.ZERO; + private boolean pastInitialDelay = false; + private Vec2<Integer> stepMovement = Vec2i.ZERO; - @Getter - private Vec2<Integer> stepMovement = Vec2i.ZERO; + public GridInputState getGridInputState() { + return GridInputState.builder().movement(stepMovement).build(); + } - @Override - public Collection<Class<? extends System<FrameState>>> getDependencies() { - return Set.of(); - } + @Override + public Collection<Class<? extends System<FrameState>>> getDependencies() { + return Set.of(); + } - @Override - public void update(final World<FrameState> world, final FrameState state, final Duration dt) { - final Set<KeyBinds.Action> currentlyActive = state.getSettings().getKeyBinds() - .filterActiveActions(state.getIsKeyPressed()); + @Override + public void update(final World<FrameState> world, final FrameState state, final Duration dt) { + final Set<KeyBinds.Action> currentlyActive = state.getSettings().getKeyBinds() + .filterActiveActions(state.getIsKeyPressed()); - if (currentlyActive.isEmpty()) { - heldAction = null; - stepMovement = Vec2i.ZERO; - return; - } + if (currentlyActive.isEmpty()) { + heldAction = null; + stepMovement = Vec2i.ZERO; + return; + } - final KeyBinds.Action active = currentlyActive.contains(heldAction) - ? heldAction - : currentlyActive.iterator().next(); + final KeyBinds.Action active = currentlyActive.contains(heldAction) + ? heldAction + : currentlyActive.iterator().next(); - if (!active.equals(heldAction)) { - heldAction = active; - heldTime = Duration.ZERO; - pastInitialDelay = false; - stepMovement = actionToMovement(active); - } else { - heldTime = heldTime.plus(dt); - if (!pastInitialDelay && heldTime.compareTo(INITIAL_DELAY) >= 0) { - pastInitialDelay = true; - heldTime = heldTime.minus(INITIAL_DELAY); - stepMovement = actionToMovement(active); - } else if (pastInitialDelay && heldTime.compareTo(REPEAT_INTERVAL) >= 0) { - heldTime = heldTime.minus(REPEAT_INTERVAL); - stepMovement = actionToMovement(active); - } else { - stepMovement = Vec2i.ZERO; - } - } - } + if (!active.equals(heldAction)) { + heldAction = active; + heldTime = Duration.ZERO; + pastInitialDelay = false; + stepMovement = actionToMovement(active); + } else { + heldTime = heldTime.plus(dt); + if (!pastInitialDelay && heldTime.compareTo(INITIAL_DELAY) >= 0) { + pastInitialDelay = true; + heldTime = heldTime.minus(INITIAL_DELAY); + stepMovement = actionToMovement(active); + } else if (pastInitialDelay && heldTime.compareTo(REPEAT_INTERVAL) >= 0) { + heldTime = heldTime.minus(REPEAT_INTERVAL); + stepMovement = actionToMovement(active); + } else { + stepMovement = Vec2i.ZERO; + } + } + } - private static Vec2<Integer> actionToMovement(final KeyBinds.Action action) { - return switch (action) { - case MOVE_UP -> Vec2i.NORTH; - case MOVE_DOWN -> Vec2i.SOUTH; - case MOVE_LEFT -> Vec2i.WEST; - case MOVE_RIGHT -> Vec2i.EAST; - }; - } + private static Vec2<Integer> actionToMovement(final KeyBinds.Action action) { + return switch (action) { + case MOVE_UP -> Vec2i.NORTH; + case MOVE_DOWN -> Vec2i.SOUTH; + case MOVE_LEFT -> Vec2i.WEST; + case MOVE_RIGHT -> Vec2i.EAST; + }; + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/RenderSystem.java b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/RenderSystem.java index 9a839d8..0bb4f54 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/RenderSystem.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/RenderSystem.java @@ -1,16 +1,15 @@ package coffee.liz.abstractionengine.app.screen.game.system; import coffee.liz.abstractionengine.app.Theme; -import coffee.liz.abstractionengine.app.actor.GridBoardActor; import coffee.liz.abstractionengine.app.config.Settings; import coffee.liz.abstractionengine.app.screen.game.FrameState; -import coffee.liz.abstractionengine.app.screen.game.component.InterpolatedPosition; import coffee.liz.abstractionengine.app.ui.editor.LambdaEditor; -import coffee.liz.abstractionengine.entity.EntityType; import coffee.liz.ecs.math.Vec2; import coffee.liz.ecs.math.Vec2f; import coffee.liz.ecs.model.System; import coffee.liz.ecs.model.World; +import lombok.Getter; + import com.badlogic.gdx.InputMultiplexer; import com.badlogic.gdx.InputProcessor; import com.badlogic.gdx.graphics.glutils.ShapeRenderer; @@ -22,83 +21,82 @@ import com.kotcrab.vis.ui.VisUI; import java.time.Duration; import java.util.Collection; -import java.util.List; import java.util.Set; public class RenderSystem implements System<FrameState> { - private final FitViewport viewport; - private final Vec2<Integer> gridSize; - private final ShapeRenderer shapeRenderer; - private final Stage stage; - private final Stage uiStage; - private final GridBoardActor boardActor; - private final InputMultiplexer inputMultiplexer; + private final FitViewport viewport; + private final Vec2<Integer> gridSize; + private final ShapeRenderer shapeRenderer; + @Getter + private final Stage stage; + private final Stage uiStage; + private final InputMultiplexer inputMultiplexer; - public RenderSystem(final FitViewport viewport, final Vec2<Integer> gridSize, final Settings settings) { - this.viewport = viewport; - this.gridSize = gridSize; - this.shapeRenderer = new ShapeRenderer(); - this.stage = new Stage(viewport); - this.boardActor = new GridBoardActor(shapeRenderer, gridSize); + public RenderSystem(final FitViewport viewport, final Vec2<Integer> gridSize, final Settings settings) { + this.viewport = viewport; + this.gridSize = gridSize; + this.shapeRenderer = new ShapeRenderer(); + this.stage = new Stage(viewport); - stage.addActor(boardActor); + // stage.addActor(boardActor); - VisUI.load(); - this.uiStage = new Stage(new ScreenViewport()); - uiStage.addActor(new LambdaEditor(settings.isVimMode())); + VisUI.load(); + this.uiStage = new Stage(new ScreenViewport()); + uiStage.addActor(new LambdaEditor(settings.isVimMode())); - // TODO: Move this to an input system - this.inputMultiplexer = new InputMultiplexer(uiStage, stage); - } + // TODO: Move this to an input system + this.inputMultiplexer = new InputMultiplexer(uiStage, stage); + } - public InputProcessor getInputProcessor() { - return inputMultiplexer; - } + public InputProcessor getInputProcessor() { + return inputMultiplexer; + } - public void resize(final int width, final int height) { - uiStage.getViewport().update(width, height, true); - } + public void resize(final int width, final int height) { + uiStage.getViewport().update(width, height, true); + } - @Override - public Collection<Class<? extends System<FrameState>>> getDependencies() { - return Set.of(GridInterpolationSystem.class); - } + @Override + public Collection<Class<? extends System<FrameState>>> getDependencies() { + return Set.of(GridInterpolationSystem.class); + } - @Override - public void update(final World<FrameState> world, final FrameState state, final Duration dt) { - ScreenUtils.clear(Theme.BG); + @Override + public void update(final World<FrameState> world, final FrameState state, final Duration dt) { + ScreenUtils.clear(Theme.BG); - final Vec2<Float> cellSize = computeCellSize(); - final List<GridBoardActor.RenderableEntity> renderables = world - .query(Set.of(InterpolatedPosition.class, EntityType.class)).stream().map(entity -> { - final InterpolatedPosition pos = entity.get(InterpolatedPosition.class); - final EntityType type = entity.get(EntityType.class); - final Vec2<Float> drawPos = pos.getCurrent().scale(cellSize); - return new GridBoardActor.RenderableEntity(type, drawPos, cellSize); - }).toList(); + final Vec2<Float> cellSize = computeCellSize(); + // final List<GridBoardActor.RenderableEntity> renderables = world + // .query(Set.of(InterpolatedPosition.class, + // EntityType.class)).stream().map(entity -> { + // final InterpolatedPosition pos = entity.get(InterpolatedPosition.class); + // final EntityType type = entity.get(EntityType.class); + // final Vec2<Float> drawPos = pos.getCurrent().scale(cellSize); + // return new GridBoardActor.RenderableEntity(type, drawPos, cellSize); + // }).toList(); - boardActor.setCellSize(cellSize); - boardActor.setEntities(renderables); + // boardActor.setCellSize(cellSize); + // boardActor.setEntities(renderables); - final float deltaSeconds = dt.toMillis() / 1000f; - stage.act(deltaSeconds); - stage.draw(); + final float deltaSeconds = dt.toMillis() / 1000f; + stage.act(deltaSeconds); + stage.draw(); - uiStage.getViewport().apply(); - uiStage.act(deltaSeconds); - uiStage.draw(); - } + uiStage.getViewport().apply(); + uiStage.act(deltaSeconds); + uiStage.draw(); + } - @Override - public void close() { - uiStage.dispose(); - stage.dispose(); - shapeRenderer.dispose(); - VisUI.dispose(); - } + @Override + public void close() { + uiStage.dispose(); + stage.dispose(); + shapeRenderer.dispose(); + VisUI.dispose(); + } - private Vec2<Float> computeCellSize() { - return Vec2f.builder().x(viewport.getWorldWidth() / gridSize.getX()) - .y(viewport.getWorldHeight() / gridSize.getY()).build(); - } + private Vec2<Float> computeCellSize() { + return Vec2f.builder().x(viewport.getWorldWidth() / gridSize.getX()) + .y(viewport.getWorldHeight() / gridSize.getY()).build(); + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/CursorStyle.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/CursorStyle.java index c8b82af..f4c75b3 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/CursorStyle.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/CursorStyle.java @@ -1,5 +1,5 @@ package coffee.liz.abstractionengine.app.ui.editor; public enum CursorStyle { - CARET, BLOCK, CARET_AND_BLOCK, HIDDEN + CARET, BLOCK, CARET_AND_BLOCK, HIDDEN } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/EditorModifier.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/EditorModifier.java index fa9dd00..b8f6802 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/EditorModifier.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/EditorModifier.java @@ -1,5 +1,5 @@ package coffee.liz.abstractionengine.app.ui.editor; public enum EditorModifier { - CTRL, ALT, SHIFT, META + CTRL, ALT, SHIFT, META } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/EditorTextArea.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/EditorTextArea.java index e2570b4..144141f 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/EditorTextArea.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/EditorTextArea.java @@ -21,254 +21,254 @@ import java.util.EnumSet; import java.util.Set; public class EditorTextArea extends EditorHighlightTextAreaBridge { - private static final Color CURRENT_LINE_COLOR = new Color(Theme.SURFACE_ALT.r, Theme.SURFACE_ALT.g, - Theme.SURFACE_ALT.b, 0.5f); - private static final Color BLOCK_CURSOR_COLOR = new Color(Theme.FG.r, Theme.FG.g, Theme.FG.b, 0.7f); - private static final Set<EditorModifier> EMPTY_MODIFIERS = Set.of(); - private static final Drawable INVISIBLE_CURSOR = new BaseDrawable() { - }; + private static final Color CURRENT_LINE_COLOR = new Color(Theme.SURFACE_ALT.r, Theme.SURFACE_ALT.g, + Theme.SURFACE_ALT.b, 0.5f); + private static final Color BLOCK_CURSOR_COLOR = new Color(Theme.FG.r, Theme.FG.g, Theme.FG.b, 0.7f); + private static final Set<EditorModifier> EMPTY_MODIFIERS = Set.of(); + private static final Drawable INVISIBLE_CURSOR = new BaseDrawable() { + }; - private final float gutterWidth; - private final float charWidth; - private final ShapeRenderer shapeRenderer; - @Setter - private InputStrategy inputStrategy; + private final float gutterWidth; + private final float charWidth; + private final ShapeRenderer shapeRenderer; + @Setter + private InputStrategy inputStrategy; - public EditorTextArea(final String text, final VisTextFieldStyle style, final float gutterWidth) { - super(text, padStyle(style, gutterWidth)); - this.gutterWidth = gutterWidth; - this.charWidth = style.font.getData().spaceXadvance; - this.shapeRenderer = new ShapeRenderer(); - } + public EditorTextArea(final String text, final VisTextFieldStyle style, final float gutterWidth) { + super(text, padStyle(style, gutterWidth)); + this.gutterWidth = gutterWidth; + this.charWidth = style.font.getData().spaceXadvance; + this.shapeRenderer = new ShapeRenderer(); + } - public void syncCursorState() { - editorSyncCursorState(); - scrollCursorIntoView(); - } + public void syncCursorState() { + editorSyncCursorState(); + scrollCursorIntoView(); + } - public void setSelectionEndpoints(final int selectionStart, final int cursorPosition) { - editorSetSelectionEndpoints(selectionStart, cursorPosition); - scrollCursorIntoView(); - } + public void setSelectionEndpoints(final int selectionStart, final int cursorPosition) { + editorSetSelectionEndpoints(selectionStart, cursorPosition); + scrollCursorIntoView(); + } - public void setCursorKeepingSelection(final int cursorPosition) { - editorSetCursorKeepingSelection(cursorPosition); - scrollCursorIntoView(); - } + public void setCursorKeepingSelection(final int cursorPosition) { + editorSetCursorKeepingSelection(cursorPosition); + scrollCursorIntoView(); + } - @Override - protected InputListener createInputListener() { - return new TextAreaListener() { - @Override - public boolean keyDown(final InputEvent event, final int keycode) { - if (inputStrategy != null) { - final KeyEvent keyEvent = new KeyEvent(keycode, currentModifiers()); - if (inputStrategy.onKeyDown(keyEvent)) { - return true; - } - } - return super.keyDown(event, keycode); - } + @Override + protected InputListener createInputListener() { + return new TextAreaListener() { + @Override + public boolean keyDown(final InputEvent event, final int keycode) { + if (inputStrategy != null) { + final KeyEvent keyEvent = new KeyEvent(keycode, currentModifiers()); + if (inputStrategy.onKeyDown(keyEvent)) { + return true; + } + } + return super.keyDown(event, keycode); + } - @Override - public boolean keyTyped(final InputEvent event, final char character) { - if (inputStrategy != null && inputStrategy.onKeyTyped(character)) { - return true; - } - return super.keyTyped(event, character); - } + @Override + public boolean keyTyped(final InputEvent event, final char character) { + if (inputStrategy != null && inputStrategy.onKeyTyped(character)) { + return true; + } + return super.keyTyped(event, character); + } - @Override - public void clicked(final InputEvent event, final float x, final float y) { - final int count = getTapCount() % 4; - if (count == 0) { - clearSelection(); - } - if (count == 2) { - final Drawable bg = getStyle().background; - final float adjustedX = bg != null ? Math.max(0, x - bg.getLeftWidth()) : x; - final int[] word = wordUnderCursor(letterUnderCursor(adjustedX)); - setSelection(word[0], word[1]); - } - } - }; - } + @Override + public void clicked(final InputEvent event, final float x, final float y) { + final int count = getTapCount() % 4; + if (count == 0) { + clearSelection(); + } + if (count == 2) { + final Drawable bg = getStyle().background; + final float adjustedX = bg != null ? Math.max(0, x - bg.getLeftWidth()) : x; + final int[] word = wordUnderCursor(letterUnderCursor(adjustedX)); + setSelection(word[0], word[1]); + } + } + }; + } - @Override - protected void drawCursor(final Drawable cursorPatch, final Batch batch, final BitmapFont font, final float x, - final float y) { - if (shouldRenderBlockCursor()) { - if (shouldRenderCaret()) { - super.drawCursor(cursorPatch, batch, font, x, y); - } else { - super.drawCursor(INVISIBLE_CURSOR, batch, font, x, y); - } - return; - } + @Override + protected void drawCursor(final Drawable cursorPatch, final Batch batch, final BitmapFont font, final float x, + final float y) { + if (shouldRenderBlockCursor()) { + if (shouldRenderCaret()) { + super.drawCursor(cursorPatch, batch, font, x, y); + } else { + super.drawCursor(INVISIBLE_CURSOR, batch, font, x, y); + } + return; + } - if (shouldRenderCaret()) { - super.drawCursor(cursorPatch, batch, font, x, y); - } - } + if (shouldRenderCaret()) { + super.drawCursor(cursorPatch, batch, font, x, y); + } + } - @Override - protected void drawText(final Batch batch, final BitmapFont font, final float x, final float y) { - final float parentAlpha = font.getColor().a; - final int first = getFirstLineShowing(); - final int count = getLinesShowing(); - final int total = getLineNumberCount(); - final int cursorLine = getCursorLine(); - final float lineHeight = font.getLineHeight(); + @Override + protected void drawText(final Batch batch, final BitmapFont font, final float x, final float y) { + final float parentAlpha = font.getColor().a; + final int first = getFirstLineShowing(); + final int count = getLinesShowing(); + final int total = getLineNumberCount(); + final int cursorLine = getCursorLine(); + final float lineHeight = font.getLineHeight(); - batch.end(); - Gdx.gl.glEnable(GL20.GL_BLEND); - shapeRenderer.setProjectionMatrix(batch.getProjectionMatrix()); - shapeRenderer.setTransformMatrix(batch.getTransformMatrix()); - shapeRenderer.begin(ShapeRenderer.ShapeType.Filled); - if (cursorLine >= first && cursorLine < first + count) { - final float highlightY = y - font.getCapHeight() + font.getDescent() - (cursorLine - first) * lineHeight; - shapeRenderer.setColor(CURRENT_LINE_COLOR.r, CURRENT_LINE_COLOR.g, CURRENT_LINE_COLOR.b, - CURRENT_LINE_COLOR.a * parentAlpha); - shapeRenderer.rect(x - gutterWidth, highlightY, getWidth(), lineHeight); - } + batch.end(); + Gdx.gl.glEnable(GL20.GL_BLEND); + shapeRenderer.setProjectionMatrix(batch.getProjectionMatrix()); + shapeRenderer.setTransformMatrix(batch.getTransformMatrix()); + shapeRenderer.begin(ShapeRenderer.ShapeType.Filled); + if (cursorLine >= first && cursorLine < first + count) { + final float highlightY = y - font.getCapHeight() + font.getDescent() - (cursorLine - first) * lineHeight; + shapeRenderer.setColor(CURRENT_LINE_COLOR.r, CURRENT_LINE_COLOR.g, CURRENT_LINE_COLOR.b, + CURRENT_LINE_COLOR.a * parentAlpha); + shapeRenderer.rect(x - gutterWidth, highlightY, getWidth(), lineHeight); + } - if (shouldRenderBlockCursor() && isFocused()) { - // Keep VisTextArea cursor metrics current even when caret is hidden. - super.drawCursor(INVISIBLE_CURSOR, batch, font, x, y); - drawBlockCursor(font, x, y, first, cursorLine, lineHeight, parentAlpha); - } - shapeRenderer.end(); - Gdx.gl.glDisable(GL20.GL_BLEND); - batch.begin(); + if (shouldRenderBlockCursor() && isFocused()) { + // Keep VisTextArea cursor metrics current even when caret is hidden. + super.drawCursor(INVISIBLE_CURSOR, batch, font, x, y); + drawBlockCursor(font, x, y, first, cursorLine, lineHeight, parentAlpha); + } + shapeRenderer.end(); + Gdx.gl.glDisable(GL20.GL_BLEND); + batch.begin(); - drawLineNumbers(batch, font, x, y, first, count, total, cursorLine, lineHeight, parentAlpha); + drawLineNumbers(batch, font, x, y, first, count, total, cursorLine, lineHeight, parentAlpha); - font.setColor(1, 1, 1, parentAlpha); - super.drawText(batch, font, x, y); - } + font.setColor(1, 1, 1, parentAlpha); + super.drawText(batch, font, x, y); + } - private static VisTextFieldStyle padStyle(final VisTextFieldStyle original, final float gutterWidth) { - final VisTextFieldStyle style = new VisTextFieldStyle(original); - style.background = wrapIfPresent(style.background, gutterWidth); - style.backgroundOver = wrapIfPresent(style.backgroundOver, gutterWidth); - style.focusedBackground = wrapIfPresent(style.focusedBackground, gutterWidth); - style.disabledBackground = wrapIfPresent(style.disabledBackground, gutterWidth); - return style; - } + private static VisTextFieldStyle padStyle(final VisTextFieldStyle original, final float gutterWidth) { + final VisTextFieldStyle style = new VisTextFieldStyle(original); + style.background = wrapIfPresent(style.background, gutterWidth); + style.backgroundOver = wrapIfPresent(style.backgroundOver, gutterWidth); + style.focusedBackground = wrapIfPresent(style.focusedBackground, gutterWidth); + style.disabledBackground = wrapIfPresent(style.disabledBackground, gutterWidth); + return style; + } - private static Drawable wrapIfPresent(final Drawable drawable, final float gutterWidth) { - return drawable != null ? new GutterPaddedDrawable(drawable, gutterWidth) : null; - } + private static Drawable wrapIfPresent(final Drawable drawable, final float gutterWidth) { + return drawable != null ? new GutterPaddedDrawable(drawable, gutterWidth) : null; + } - private void drawLineNumbers(final Batch batch, final BitmapFont font, final float x, final float y, - final int firstLine, final int visibleLineCount, final int totalLines, final int cursorLine, - final float lineHeight, final float parentAlpha) { - final LineNumberMode mode = resolveRenderState().lineNumberMode(); - if (mode == LineNumberMode.OFF) { - return; - } + private void drawLineNumbers(final Batch batch, final BitmapFont font, final float x, final float y, + final int firstLine, final int visibleLineCount, final int totalLines, final int cursorLine, + final float lineHeight, final float parentAlpha) { + final LineNumberMode mode = resolveRenderState().lineNumberMode(); + if (mode == LineNumberMode.OFF) { + return; + } - float offsetY = 0f; - for (int line = firstLine; line < firstLine + visibleLineCount && line < totalLines; line++) { - final Color color = line == cursorLine ? Theme.FG : Theme.BORDER_LIGHT; - font.setColor(color.r, color.g, color.b, color.a * parentAlpha); - final String lineNum = formatLineNumber(mode, line, cursorLine); - font.draw(batch, lineNum, x - gutterWidth, y + offsetY); - offsetY -= lineHeight; - } - } + float offsetY = 0f; + for (int line = firstLine; line < firstLine + visibleLineCount && line < totalLines; line++) { + final Color color = line == cursorLine ? Theme.FG : Theme.BORDER_LIGHT; + font.setColor(color.r, color.g, color.b, color.a * parentAlpha); + final String lineNum = formatLineNumber(mode, line, cursorLine); + font.draw(batch, lineNum, x - gutterWidth, y + offsetY); + offsetY -= lineHeight; + } + } - private int getLineNumberCount() { - final int totalLines = getLines(); - if (totalLines > 1 && getText().endsWith("\n")) { - return totalLines - 1; - } - return totalLines; - } + private int getLineNumberCount() { + final int totalLines = getLines(); + if (totalLines > 1 && getText().endsWith("\n")) { + return totalLines - 1; + } + return totalLines; + } - private String formatLineNumber(final LineNumberMode mode, final int line, final int cursorLine) { - if (mode == LineNumberMode.RELATIVE && line != cursorLine) { - return String.valueOf(Math.abs(line - cursorLine)); - } - return String.valueOf(line + 1); - } + private String formatLineNumber(final LineNumberMode mode, final int line, final int cursorLine) { + if (mode == LineNumberMode.RELATIVE && line != cursorLine) { + return String.valueOf(Math.abs(line - cursorLine)); + } + return String.valueOf(line + 1); + } - private void drawBlockCursor(final BitmapFont font, final float x, final float y, final int firstLine, - final int cursorLine, final float lineHeight, final float parentAlpha) { - final float cursorX = x + getCursorX(); - final float cursorY = y - font.getCapHeight() + font.getDescent() - (cursorLine - firstLine) * lineHeight; - shapeRenderer.setColor(BLOCK_CURSOR_COLOR.r, BLOCK_CURSOR_COLOR.g, BLOCK_CURSOR_COLOR.b, - BLOCK_CURSOR_COLOR.a * parentAlpha); - shapeRenderer.rect(cursorX, cursorY, Math.max(1f, charWidth), lineHeight); - } + private void drawBlockCursor(final BitmapFont font, final float x, final float y, final int firstLine, + final int cursorLine, final float lineHeight, final float parentAlpha) { + final float cursorX = x + getCursorX(); + final float cursorY = y - font.getCapHeight() + font.getDescent() - (cursorLine - firstLine) * lineHeight; + shapeRenderer.setColor(BLOCK_CURSOR_COLOR.r, BLOCK_CURSOR_COLOR.g, BLOCK_CURSOR_COLOR.b, + BLOCK_CURSOR_COLOR.a * parentAlpha); + shapeRenderer.rect(cursorX, cursorY, Math.max(1f, charWidth), lineHeight); + } - private boolean shouldRenderCaret() { - final CursorStyle cursorStyle = resolveRenderState().cursorStyle(); - return cursorStyle == CursorStyle.CARET || cursorStyle == CursorStyle.CARET_AND_BLOCK; - } + private boolean shouldRenderCaret() { + final CursorStyle cursorStyle = resolveRenderState().cursorStyle(); + return cursorStyle == CursorStyle.CARET || cursorStyle == CursorStyle.CARET_AND_BLOCK; + } - private boolean shouldRenderBlockCursor() { - final CursorStyle cursorStyle = resolveRenderState().cursorStyle(); - return cursorStyle == CursorStyle.BLOCK || cursorStyle == CursorStyle.CARET_AND_BLOCK; - } + private boolean shouldRenderBlockCursor() { + final CursorStyle cursorStyle = resolveRenderState().cursorStyle(); + return cursorStyle == CursorStyle.BLOCK || cursorStyle == CursorStyle.CARET_AND_BLOCK; + } - private RenderState resolveRenderState() { - if (inputStrategy == null) { - return RenderState.DEFAULT; - } - final RenderState renderState = inputStrategy.getRenderState(); - return renderState != null ? renderState : RenderState.DEFAULT; - } + private RenderState resolveRenderState() { + if (inputStrategy == null) { + return RenderState.DEFAULT; + } + final RenderState renderState = inputStrategy.getRenderState(); + return renderState != null ? renderState : RenderState.DEFAULT; + } - private boolean isFocused() { - final Stage stage = getStage(); - return stage != null && stage.getKeyboardFocus() == this && !isDisabled(); - } + private boolean isFocused() { + final Stage stage = getStage(); + return stage != null && stage.getKeyboardFocus() == this && !isDisabled(); + } - private void scrollCursorIntoView() { - if (!(getParent() instanceof ScrollPane scrollPane)) { - return; - } + private void scrollCursorIntoView() { + if (!(getParent() instanceof ScrollPane scrollPane)) { + return; + } - final float lineHeight = getStyle().font.getLineHeight(); - final float cursorX = getCursorX(); - final float cursorY = getHeight() - getCursorY() + lineHeight; - scrollPane.scrollTo(cursorX, cursorY, Math.max(1f, charWidth), lineHeight, false, false); - } + final float lineHeight = getStyle().font.getLineHeight(); + final float cursorX = getCursorX(); + final float cursorY = getHeight() - getCursorY() + lineHeight; + scrollPane.scrollTo(cursorX, cursorY, Math.max(1f, charWidth), lineHeight, false, false); + } - private Set<EditorModifier> currentModifiers() { - if (Gdx.input == null) { - return EMPTY_MODIFIERS; - } + private Set<EditorModifier> currentModifiers() { + if (Gdx.input == null) { + return EMPTY_MODIFIERS; + } - final EnumSet<EditorModifier> modifiers = EnumSet.noneOf(EditorModifier.class); - if (Gdx.input.isKeyPressed(Input.Keys.CONTROL_LEFT) || Gdx.input.isKeyPressed(Input.Keys.CONTROL_RIGHT)) { - modifiers.add(EditorModifier.CTRL); - } - if (Gdx.input.isKeyPressed(Input.Keys.ALT_LEFT) || Gdx.input.isKeyPressed(Input.Keys.ALT_RIGHT)) { - modifiers.add(EditorModifier.ALT); - } - if (Gdx.input.isKeyPressed(Input.Keys.SHIFT_LEFT) || Gdx.input.isKeyPressed(Input.Keys.SHIFT_RIGHT)) { - modifiers.add(EditorModifier.SHIFT); - } - if (Gdx.input.isKeyPressed(Input.Keys.SYM)) { - modifiers.add(EditorModifier.META); - } - return modifiers.isEmpty() ? EMPTY_MODIFIERS : modifiers; - } + final EnumSet<EditorModifier> modifiers = EnumSet.noneOf(EditorModifier.class); + if (Gdx.input.isKeyPressed(Input.Keys.CONTROL_LEFT) || Gdx.input.isKeyPressed(Input.Keys.CONTROL_RIGHT)) { + modifiers.add(EditorModifier.CTRL); + } + if (Gdx.input.isKeyPressed(Input.Keys.ALT_LEFT) || Gdx.input.isKeyPressed(Input.Keys.ALT_RIGHT)) { + modifiers.add(EditorModifier.ALT); + } + if (Gdx.input.isKeyPressed(Input.Keys.SHIFT_LEFT) || Gdx.input.isKeyPressed(Input.Keys.SHIFT_RIGHT)) { + modifiers.add(EditorModifier.SHIFT); + } + if (Gdx.input.isKeyPressed(Input.Keys.SYM)) { + modifiers.add(EditorModifier.META); + } + return modifiers.isEmpty() ? EMPTY_MODIFIERS : modifiers; + } - private static final class GutterPaddedDrawable extends BaseDrawable { - private final Drawable wrapped; + private static final class GutterPaddedDrawable extends BaseDrawable { + private final Drawable wrapped; - GutterPaddedDrawable(final Drawable wrapped, final float gutterWidth) { - super(wrapped); - this.wrapped = wrapped; - setLeftWidth(wrapped.getLeftWidth() + gutterWidth); - } + GutterPaddedDrawable(final Drawable wrapped, final float gutterWidth) { + super(wrapped); + this.wrapped = wrapped; + setLeftWidth(wrapped.getLeftWidth() + gutterWidth); + } - @Override - public void draw(final Batch batch, final float x, final float y, final float width, final float height) { - wrapped.draw(batch, x, y, width, height); - } - } + @Override + public void draw(final Batch batch, final float x, final float y, final float width, final float height) { + wrapped.draw(batch, x, y, width, height); + } + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/InputStrategy.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/InputStrategy.java index 49816d3..ba55466 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/InputStrategy.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/InputStrategy.java @@ -1,11 +1,11 @@ package coffee.liz.abstractionengine.app.ui.editor; public interface InputStrategy { - boolean onKeyDown(KeyEvent event); + boolean onKeyDown(KeyEvent event); - boolean onKeyTyped(char character); + boolean onKeyTyped(char character); - default RenderState getRenderState() { - return RenderState.DEFAULT; - } + default RenderState getRenderState() { + return RenderState.DEFAULT; + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/KeyEvent.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/KeyEvent.java index 4da2f60..c4bade5 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/KeyEvent.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/KeyEvent.java @@ -3,7 +3,7 @@ package coffee.liz.abstractionengine.app.ui.editor; import java.util.Set; public record KeyEvent(int keycode, Set<EditorModifier> modifiers) { - public boolean hasModifier(final EditorModifier modifier) { - return modifiers.contains(modifier); - } + public boolean hasModifier(final EditorModifier modifier) { + return modifiers.contains(modifier); + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/LambdaEditor.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/LambdaEditor.java index 46fcb22..11a4b0e 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/LambdaEditor.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/LambdaEditor.java @@ -15,66 +15,66 @@ import com.kotcrab.vis.ui.widget.VisTextField.VisTextFieldStyle; import com.kotcrab.vis.ui.widget.VisWindow; public class LambdaEditor extends VisWindow { - private static final int FONT_SIZE = 15; - private static final float WINDOW_ALPHA = 0.92f; + private static final int FONT_SIZE = 15; + private static final float WINDOW_ALPHA = 0.92f; - private static final String DEFAULT_TEXT = "-- Church numerals\n" + "let zero = λf.λx.x;\n" - + "let one = λf.λx.f x;\n" + "let succ = λn.λf.λx.f (n f x);\n" + "let add = λm.λn.λf.λx.m f (n f x);\n" - + "\n" + "succ (add one zero)\n"; + private static final String DEFAULT_TEXT = "-- Church numerals\n" + "let zero = λf.λx.x;\n" + + "let one = λf.λx.f x;\n" + "let succ = λn.λf.λx.f (n f x);\n" + "let add = λm.λn.λf.λx.m f (n f x);\n" + + "\n" + "succ (add one zero)\n"; - private final BitmapFont font; + private final BitmapFont font; - public LambdaEditor(final boolean vimMode) { - super("λ-editor"); + public LambdaEditor(final boolean vimMode) { + super("λ-editor"); - TableUtils.setSpacingDefaults(this); - columnDefaults(0).left(); + TableUtils.setSpacingDefaults(this); + columnDefaults(0).left(); - this.font = FontHelper.initFont(FONT_SIZE, FontHelper.MONO, 1f); + this.font = FontHelper.initFont(FONT_SIZE, FontHelper.MONO, 1f); - setResizable(true); - addCloseButton(); - addEditor(vimMode); + setResizable(true); + addCloseButton(); + addEditor(vimMode); - setSize(400, 300); - setColor(1, 1, 1, WINDOW_ALPHA); - centerWindow(); - } + setSize(400, 300); + setColor(1, 1, 1, WINDOW_ALPHA); + centerWindow(); + } - private void addEditor(final boolean vimMode) { - final VisTextFieldStyle style = new VisTextFieldStyle(VisUI.getSkin().get("textArea", VisTextFieldStyle.class)); - style.font = font; + private void addEditor(final boolean vimMode) { + final VisTextFieldStyle style = new VisTextFieldStyle(VisUI.getSkin().get("textArea", VisTextFieldStyle.class)); + style.font = font; - final GlyphLayout layout = new GlyphLayout(font, "000 "); - final float gutterWidth = layout.width; + final GlyphLayout layout = new GlyphLayout(font, "000 "); + final float gutterWidth = layout.width; - final EditorTextArea textArea = new EditorTextArea(DEFAULT_TEXT, style, gutterWidth); - textArea.setHighlighter(createHighlighter()); + final EditorTextArea textArea = new EditorTextArea(DEFAULT_TEXT, style, gutterWidth); + textArea.setHighlighter(createHighlighter()); - final ScrollPane scrollPane = textArea.createCompatibleScrollPane(); - scrollPane.setFadeScrollBars(false); + final ScrollPane scrollPane = textArea.createCompatibleScrollPane(); + scrollPane.setFadeScrollBars(false); - final VisLabel statusLabel = new VisLabel(vimMode ? "-- NORMAL --" : "-- INSERT --"); - statusLabel.setAlignment(Align.left); + final VisLabel statusLabel = new VisLabel(vimMode ? "-- NORMAL --" : "-- INSERT --"); + statusLabel.setAlignment(Align.left); - if (vimMode) { - final VimEngine controller = new VimEngine(textArea, statusLabel::setText); - textArea.setInputStrategy(controller); - statusLabel.setText(controller.getStatusText()); - } + if (vimMode) { + final VimEngine controller = new VimEngine(textArea, statusLabel::setText); + textArea.setInputStrategy(controller); + statusLabel.setText(controller.getStatusText()); + } - add(scrollPane).grow().row(); - add(statusLabel).growX().padTop(6f); - } + add(scrollPane).grow().row(); + add(statusLabel).growX().padTop(6f); + } - private static Highlighter createHighlighter() { - final Highlighter highlighter = new Highlighter(); - highlighter.regex(Theme.MUTED, "--[^\\n]*"); - highlighter.regex(Theme.PRIMARY, "\\blet\\b"); - highlighter.regex(Theme.SECONDARY, "[λ\\\\]"); - highlighter.regex(Theme.SECONDARY, "\\."); - highlighter.regex(Theme.BORDER_LIGHT, "[=;]"); - highlighter.regex(Theme.BORDER_LIGHT, "[()]"); - return highlighter; - } + private static Highlighter createHighlighter() { + final Highlighter highlighter = new Highlighter(); + highlighter.regex(Theme.MUTED, "--[^\\n]*"); + highlighter.regex(Theme.PRIMARY, "\\blet\\b"); + highlighter.regex(Theme.SECONDARY, "[λ\\\\]"); + highlighter.regex(Theme.SECONDARY, "\\."); + highlighter.regex(Theme.BORDER_LIGHT, "[=;]"); + highlighter.regex(Theme.BORDER_LIGHT, "[()]"); + return highlighter; + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/LineNumberMode.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/LineNumberMode.java index 3ec768d..0ab0f20 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/LineNumberMode.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/LineNumberMode.java @@ -1,5 +1,5 @@ package coffee.liz.abstractionengine.app.ui.editor; public enum LineNumberMode { - OFF, ABSOLUTE, RELATIVE + OFF, ABSOLUTE, RELATIVE } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/RenderState.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/RenderState.java index a04c988..8961cfa 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/RenderState.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/RenderState.java @@ -1,5 +1,5 @@ package coffee.liz.abstractionengine.app.ui.editor; public record RenderState(CursorStyle cursorStyle, LineNumberMode lineNumberMode) { - public static final RenderState DEFAULT = new RenderState(CursorStyle.CARET, LineNumberMode.ABSOLUTE); + public static final RenderState DEFAULT = new RenderState(CursorStyle.CARET, LineNumberMode.ABSOLUTE); } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/EditorBuffer.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/EditorBuffer.java index eadc8e8..0ccde34 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/EditorBuffer.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/EditorBuffer.java @@ -1,21 +1,21 @@ package coffee.liz.abstractionengine.app.ui.editor.vim; public interface EditorBuffer { - String getText(); + String getText(); - void setText(String text); + void setText(String text); - int getCursorPosition(); + int getCursorPosition(); - void setCursorPosition(int cursorPosition); + void setCursorPosition(int cursorPosition); - int getCursorLine(); + int getCursorLine(); - int getLines(); + int getLines(); - void moveCursorLine(int line); + void moveCursorLine(int line); - void setSelectionEndpoints(int selectionStart, int cursorPosition); + void setSelectionEndpoints(int selectionStart, int cursorPosition); - void clearSelection(); + void clearSelection(); } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/Mode.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/Mode.java index e470f91..97e1d62 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/Mode.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/Mode.java @@ -1,5 +1,5 @@ package coffee.liz.abstractionengine.app.ui.editor.vim; public enum Mode { - NORMAL, INSERT, VISUAL, VISUAL_LINE, SEARCH + NORMAL, INSERT, VISUAL, VISUAL_LINE, SEARCH } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/VimConfig.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/VimConfig.java index 050790c..5b62f61 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/VimConfig.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/VimConfig.java @@ -1,34 +1,34 @@ package coffee.liz.abstractionengine.app.ui.editor.vim; public record VimConfig(int maxUndoStates, char ctrlRedoCharacter, String normalStatusLabel, String insertStatusLabel, - String visualStatusLabel, String visualLineStatusLabel, boolean wrapSearch) { - private static final int DEFAULT_MAX_UNDO_STATES = 200; - private static final char DEFAULT_CTRL_REDO_CHARACTER = 18; - private static final String DEFAULT_NORMAL_STATUS_LABEL = "-- NORMAL --"; - private static final String DEFAULT_INSERT_STATUS_LABEL = "-- INSERT --"; - private static final String DEFAULT_VISUAL_STATUS_LABEL = "-- VISUAL --"; - private static final String DEFAULT_VISUAL_LINE_STATUS_LABEL = "-- VISUAL LINE --"; + String visualStatusLabel, String visualLineStatusLabel, boolean wrapSearch) { + private static final int DEFAULT_MAX_UNDO_STATES = 200; + private static final char DEFAULT_CTRL_REDO_CHARACTER = 18; + private static final String DEFAULT_NORMAL_STATUS_LABEL = "-- NORMAL --"; + private static final String DEFAULT_INSERT_STATUS_LABEL = "-- INSERT --"; + private static final String DEFAULT_VISUAL_STATUS_LABEL = "-- VISUAL --"; + private static final String DEFAULT_VISUAL_LINE_STATUS_LABEL = "-- VISUAL LINE --"; - public VimConfig { - if (maxUndoStates <= 0) { - throw new IllegalArgumentException("maxUndoStates must be positive"); - } - if (normalStatusLabel == null || normalStatusLabel.isEmpty()) { - throw new IllegalArgumentException("normalStatusLabel must not be empty"); - } - if (insertStatusLabel == null || insertStatusLabel.isEmpty()) { - throw new IllegalArgumentException("insertStatusLabel must not be empty"); - } - if (visualStatusLabel == null || visualStatusLabel.isEmpty()) { - throw new IllegalArgumentException("visualStatusLabel must not be empty"); - } - if (visualLineStatusLabel == null || visualLineStatusLabel.isEmpty()) { - throw new IllegalArgumentException("visualLineStatusLabel must not be empty"); - } - } + public VimConfig { + if (maxUndoStates <= 0) { + throw new IllegalArgumentException("maxUndoStates must be positive"); + } + if (normalStatusLabel == null || normalStatusLabel.isEmpty()) { + throw new IllegalArgumentException("normalStatusLabel must not be empty"); + } + if (insertStatusLabel == null || insertStatusLabel.isEmpty()) { + throw new IllegalArgumentException("insertStatusLabel must not be empty"); + } + if (visualStatusLabel == null || visualStatusLabel.isEmpty()) { + throw new IllegalArgumentException("visualStatusLabel must not be empty"); + } + if (visualLineStatusLabel == null || visualLineStatusLabel.isEmpty()) { + throw new IllegalArgumentException("visualLineStatusLabel must not be empty"); + } + } - public static VimConfig defaults() { - return new VimConfig(DEFAULT_MAX_UNDO_STATES, DEFAULT_CTRL_REDO_CHARACTER, DEFAULT_NORMAL_STATUS_LABEL, - DEFAULT_INSERT_STATUS_LABEL, DEFAULT_VISUAL_STATUS_LABEL, DEFAULT_VISUAL_LINE_STATUS_LABEL, true); - } + public static VimConfig defaults() { + return new VimConfig(DEFAULT_MAX_UNDO_STATES, DEFAULT_CTRL_REDO_CHARACTER, DEFAULT_NORMAL_STATUS_LABEL, + DEFAULT_INSERT_STATUS_LABEL, DEFAULT_VISUAL_STATUS_LABEL, DEFAULT_VISUAL_LINE_STATUS_LABEL, true); + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/VimEngine.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/VimEngine.java index dd577fb..ab5f203 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/VimEngine.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/VimEngine.java @@ -31,175 +31,175 @@ import java.util.Set; import java.util.function.Consumer; public class VimEngine implements InputStrategy { - private final VimConfig config; - private final KeyContext context; - private final StatusFormatter statusFormatter; - private final ModeHandler<NormalState> normalModeHandler; - private final ModeHandler<InsertState> insertModeHandler; - private final ModeHandler<VisualState> visualModeHandler; - private final ModeHandler<VisualLineState> visualLineModeHandler; - private final ModeHandler<SearchState> searchModeHandler; + private final VimConfig config; + private final KeyContext context; + private final StatusFormatter statusFormatter; + private final ModeHandler<NormalState> normalModeHandler; + private final ModeHandler<InsertState> insertModeHandler; + private final ModeHandler<VisualState> visualModeHandler; + private final ModeHandler<VisualLineState> visualLineModeHandler; + private final ModeHandler<SearchState> searchModeHandler; - private ModeState state; + private ModeState state; - public VimEngine(final EditorTextArea textArea, final Consumer<String> statusSink, final VimConfig config) { - this(new TextAreaBuffer(textArea), statusSink, config); - } + public VimEngine(final EditorTextArea textArea, final Consumer<String> statusSink, final VimConfig config) { + this(new TextAreaBuffer(textArea), statusSink, config); + } - public VimEngine(final EditorTextArea textArea, final Consumer<String> statusSink) { - this(textArea, statusSink, VimConfig.defaults()); - } + public VimEngine(final EditorTextArea textArea, final Consumer<String> statusSink) { + this(textArea, statusSink, VimConfig.defaults()); + } - public VimEngine(final EditorTextArea textArea) { - this(textArea, null, VimConfig.defaults()); - } + public VimEngine(final EditorTextArea textArea) { + this(textArea, null, VimConfig.defaults()); + } - public VimEngine(final EditorBuffer buffer, final Consumer<String> statusSink, final VimConfig config) { - this.config = config != null ? config : VimConfig.defaults(); - this.context = new KeyContext(buffer, statusSink, new TextNavigation(), - new UndoManager(this.config.maxUndoStates()), new Register(), this.config); - this.statusFormatter = new StatusFormatter(); - this.normalModeHandler = new NormalModeHandler(); - this.insertModeHandler = new InsertModeHandler(); - this.visualModeHandler = new VisualModeHandler(); - this.visualLineModeHandler = new VisualLineModeHandler(); - this.searchModeHandler = new SearchModeHandler(); - this.state = NormalState.DEFAULT; - context.recordUndoState(); - publishStatus(); - } + public VimEngine(final EditorBuffer buffer, final Consumer<String> statusSink, final VimConfig config) { + this.config = config != null ? config : VimConfig.defaults(); + this.context = new KeyContext(buffer, statusSink, new TextNavigation(), + new UndoManager(this.config.maxUndoStates()), new Register(), this.config); + this.statusFormatter = new StatusFormatter(); + this.normalModeHandler = new NormalModeHandler(); + this.insertModeHandler = new InsertModeHandler(); + this.visualModeHandler = new VisualModeHandler(); + this.visualLineModeHandler = new VisualLineModeHandler(); + this.searchModeHandler = new SearchModeHandler(); + this.state = NormalState.DEFAULT; + context.recordUndoState(); + publishStatus(); + } - public VimEngine(final EditorBuffer buffer, final Consumer<String> statusSink) { - this(buffer, statusSink, VimConfig.defaults()); - } + public VimEngine(final EditorBuffer buffer, final Consumer<String> statusSink) { + this(buffer, statusSink, VimConfig.defaults()); + } - @Override - public boolean onKeyDown(final KeyEvent event) { - return handleKeyDown(event); - } + @Override + public boolean onKeyDown(final KeyEvent event) { + return handleKeyDown(event); + } - @Override - public boolean onKeyTyped(final char character) { - return handleKeyTyped(character); - } + @Override + public boolean onKeyTyped(final char character) { + return handleKeyTyped(character); + } - @Override - public RenderState getRenderState() { - if (isBlockCursorMode(state)) { - return new RenderState(CursorStyle.BLOCK, LineNumberMode.RELATIVE); - } - return new RenderState(CursorStyle.CARET, LineNumberMode.ABSOLUTE); - } + @Override + public RenderState getRenderState() { + if (isBlockCursorMode(state)) { + return new RenderState(CursorStyle.BLOCK, LineNumberMode.RELATIVE); + } + return new RenderState(CursorStyle.CARET, LineNumberMode.ABSOLUTE); + } - public String getStatusText() { - return context.getStatusText(); - } + public String getStatusText() { + return context.getStatusText(); + } - public boolean handleKeyDown(final int keycode) { - return handleKeyDown(new KeyEvent(keycode, Set.of())); - } + public boolean handleKeyDown(final int keycode) { + return handleKeyDown(new KeyEvent(keycode, Set.of())); + } - public boolean handleKeyDown(final int keycode, final boolean ctrlDown) { - final Set<EditorModifier> modifiers = ctrlDown ? Set.of(EditorModifier.CTRL) : Set.of(); - return handleKeyDown(new KeyEvent(keycode, modifiers)); - } + public boolean handleKeyDown(final int keycode, final boolean ctrlDown) { + final Set<EditorModifier> modifiers = ctrlDown ? Set.of(EditorModifier.CTRL) : Set.of(); + return handleKeyDown(new KeyEvent(keycode, modifiers)); + } - public boolean handleKeyDown(final KeyEvent event) { - context.setCurrentKeyEvent(event); - if (isInsertState(state)) { - final boolean consumed = applyHandleResult(insertModeHandler.onKeyDown((InsertState) state, context)); - return consumed; - } + public boolean handleKeyDown(final KeyEvent event) { + context.setCurrentKeyEvent(event); + if (isInsertState(state)) { + final boolean consumed = applyHandleResult(insertModeHandler.onKeyDown((InsertState) state, context)); + return consumed; + } - if (isRedoKeyDown(event)) { - state = context.redoToNormalState(); - context.suppressNextRedoTyped(); - publishStatus(); - return true; - } + if (isRedoKeyDown(event)) { + state = context.redoToNormalState(); + context.suppressNextRedoTyped(); + publishStatus(); + return true; + } - final HandleResult result = dispatchKeyDown(state); - if (applyHandleResult(result)) { - return true; - } + final HandleResult result = dispatchKeyDown(state); + if (applyHandleResult(result)) { + return true; + } - return !isInsertState(state); - } + return !isInsertState(state); + } - public boolean handleKeyTyped(final char character) { - if (character == config.ctrlRedoCharacter()) { - if (context.consumeRedoTypedSuppression()) { - return true; - } - state = context.redoToNormalState(); - publishStatus(); - return true; - } + public boolean handleKeyTyped(final char character) { + if (character == config.ctrlRedoCharacter()) { + if (context.consumeRedoTypedSuppression()) { + return true; + } + state = context.redoToNormalState(); + publishStatus(); + return true; + } - if (context.consumeSuppressedTypedCommand()) { - return true; - } - context.clearRedoTypedSuppression(); + if (context.consumeSuppressedTypedCommand()) { + return true; + } + context.clearRedoTypedSuppression(); - if (isInsertState(state)) { - return false; - } + if (isInsertState(state)) { + return false; + } - final HandleResult result = dispatchKeyTyped(state, character); - if (applyHandleResult(result)) { - return true; - } + final HandleResult result = dispatchKeyTyped(state, character); + if (applyHandleResult(result)) { + return true; + } - return !isInsertState(state); - } + return !isInsertState(state); + } - private HandleResult dispatchKeyDown(final ModeState modeState) { - return switch (modeState) { - case NormalState normalState -> normalModeHandler.onKeyDown(normalState, context); - case InsertState insertState -> insertModeHandler.onKeyDown(insertState, context); - case VisualState visualState -> visualModeHandler.onKeyDown(visualState, context); - case VisualLineState visualLineState -> visualLineModeHandler.onKeyDown(visualLineState, context); - case SearchState searchState -> searchModeHandler.onKeyDown(searchState, context); - }; - } + private HandleResult dispatchKeyDown(final ModeState modeState) { + return switch (modeState) { + case NormalState normalState -> normalModeHandler.onKeyDown(normalState, context); + case InsertState insertState -> insertModeHandler.onKeyDown(insertState, context); + case VisualState visualState -> visualModeHandler.onKeyDown(visualState, context); + case VisualLineState visualLineState -> visualLineModeHandler.onKeyDown(visualLineState, context); + case SearchState searchState -> searchModeHandler.onKeyDown(searchState, context); + }; + } - private HandleResult dispatchKeyTyped(final ModeState modeState, final char character) { - return switch (modeState) { - case NormalState normalState -> normalModeHandler.onKeyTyped(normalState, character, context); - case InsertState insertState -> insertModeHandler.onKeyTyped(insertState, character, context); - case VisualState visualState -> visualModeHandler.onKeyTyped(visualState, character, context); - case VisualLineState visualLineState -> - visualLineModeHandler.onKeyTyped(visualLineState, character, context); - case SearchState searchState -> searchModeHandler.onKeyTyped(searchState, character, context); - }; - } + private HandleResult dispatchKeyTyped(final ModeState modeState, final char character) { + return switch (modeState) { + case NormalState normalState -> normalModeHandler.onKeyTyped(normalState, character, context); + case InsertState insertState -> insertModeHandler.onKeyTyped(insertState, character, context); + case VisualState visualState -> visualModeHandler.onKeyTyped(visualState, character, context); + case VisualLineState visualLineState -> + visualLineModeHandler.onKeyTyped(visualLineState, character, context); + case SearchState searchState -> searchModeHandler.onKeyTyped(searchState, character, context); + }; + } - private boolean applyHandleResult(final HandleResult result) { - if (result.suppressTyped()) { - context.incrementSuppressedTypedCommands(); - } - if (result.nextState() != null) { - state = result.nextState(); - } - publishStatus(); - return result.consumed(); - } + private boolean applyHandleResult(final HandleResult result) { + if (result.suppressTyped()) { + context.incrementSuppressedTypedCommands(); + } + if (result.nextState() != null) { + state = result.nextState(); + } + publishStatus(); + return result.consumed(); + } - private boolean isRedoKeyDown(final KeyEvent event) { - final boolean ctrlDown = event.hasModifier(EditorModifier.CTRL) || event.hasModifier(EditorModifier.META); - return ctrlDown && event.keycode() == Input.Keys.R; - } + private boolean isRedoKeyDown(final KeyEvent event) { + final boolean ctrlDown = event.hasModifier(EditorModifier.CTRL) || event.hasModifier(EditorModifier.META); + return ctrlDown && event.keycode() == Input.Keys.R; + } - private boolean isInsertState(final ModeState modeState) { - return modeState instanceof InsertState; - } + private boolean isInsertState(final ModeState modeState) { + return modeState instanceof InsertState; + } - private boolean isBlockCursorMode(final ModeState modeState) { - return modeState instanceof NormalState || modeState instanceof VisualState - || modeState instanceof VisualLineState; - } + private boolean isBlockCursorMode(final ModeState modeState) { + return modeState instanceof NormalState || modeState instanceof VisualState + || modeState instanceof VisualLineState; + } - private void publishStatus() { - context.publishStatus(statusFormatter.format(state, config)); - } + private void publishStatus() { + context.publishStatus(statusFormatter.format(state, config)); + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/buffer/TextAreaBuffer.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/buffer/TextAreaBuffer.java index 36fe9d3..428976e 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/buffer/TextAreaBuffer.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/buffer/TextAreaBuffer.java @@ -6,59 +6,59 @@ import lombok.RequiredArgsConstructor; @RequiredArgsConstructor public class TextAreaBuffer implements EditorBuffer { - private final EditorTextArea textArea; + private final EditorTextArea textArea; - @Override - public String getText() { - return textArea.getText(); - } + @Override + public String getText() { + return textArea.getText(); + } - @Override - public void setText(final String text) { - if (text.equals(textArea.getText())) { - return; - } - textArea.setText(text); - textArea.syncCursorState(); - } + @Override + public void setText(final String text) { + if (text.equals(textArea.getText())) { + return; + } + textArea.setText(text); + textArea.syncCursorState(); + } - @Override - public int getCursorPosition() { - return textArea.getCursorPosition(); - } + @Override + public int getCursorPosition() { + return textArea.getCursorPosition(); + } - @Override - public void setCursorPosition(final int cursorPosition) { - final int clampedCursor = Math.max(0, Math.min(cursorPosition, textArea.getText().length())); - if (clampedCursor == textArea.getCursorPosition()) { - return; - } - textArea.setCursorPosition(clampedCursor); - textArea.syncCursorState(); - } + @Override + public void setCursorPosition(final int cursorPosition) { + final int clampedCursor = Math.max(0, Math.min(cursorPosition, textArea.getText().length())); + if (clampedCursor == textArea.getCursorPosition()) { + return; + } + textArea.setCursorPosition(clampedCursor); + textArea.syncCursorState(); + } - @Override - public int getCursorLine() { - return textArea.getCursorLine(); - } + @Override + public int getCursorLine() { + return textArea.getCursorLine(); + } - @Override - public int getLines() { - return textArea.getLines(); - } + @Override + public int getLines() { + return textArea.getLines(); + } - @Override - public void moveCursorLine(final int line) { - textArea.moveCursorLine(line); - } + @Override + public void moveCursorLine(final int line) { + textArea.moveCursorLine(line); + } - @Override - public void setSelectionEndpoints(final int selectionStart, final int cursorPosition) { - textArea.setSelectionEndpoints(selectionStart, cursorPosition); - } + @Override + public void setSelectionEndpoints(final int selectionStart, final int cursorPosition) { + textArea.setSelectionEndpoints(selectionStart, cursorPosition); + } - @Override - public void clearSelection() { - textArea.clearSelection(); - } + @Override + public void clearSelection() { + textArea.clearSelection(); + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/context/KeyContext.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/context/KeyContext.java index a15232d..63ebff9 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/context/KeyContext.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/context/KeyContext.java @@ -24,683 +24,683 @@ import java.util.Set; import java.util.function.Consumer; public class KeyContext { - private final EditorBuffer editorBuffer; - private final Consumer<String> statusUpdater; - private final TextNavigation navigation; - private final UndoManager undoManager; - private final Register register; - private final VimConfig config; + private final EditorBuffer editorBuffer; + private final Consumer<String> statusUpdater; + private final TextNavigation navigation; + private final UndoManager undoManager; + private final Register register; + private final VimConfig config; - private String statusText; - private String lastSearchQuery; - private SearchDirection lastSearchDirection; - private SearchIterator searchIterator; - private String searchIteratorQuery; - private String searchIteratorText; - private int suppressedTypedCommands; - private boolean suppressNextRedoTyped; - private KeyEvent currentKeyEvent; + private String statusText; + private String lastSearchQuery; + private SearchDirection lastSearchDirection; + private SearchIterator searchIterator; + private String searchIteratorQuery; + private String searchIteratorText; + private int suppressedTypedCommands; + private boolean suppressNextRedoTyped; + private KeyEvent currentKeyEvent; - public KeyContext(final EditorBuffer editorBuffer, final Consumer<String> statusUpdater, - final TextNavigation navigation, final UndoManager undoManager, final Register register, - final VimConfig config) { - this.editorBuffer = editorBuffer; - this.statusUpdater = statusUpdater; - this.navigation = navigation; - this.undoManager = undoManager; - this.register = register; - this.config = config; - this.statusText = ""; - this.lastSearchQuery = ""; - this.lastSearchDirection = SearchDirection.FORWARD; - this.searchIterator = null; - this.searchIteratorQuery = ""; - this.searchIteratorText = ""; - this.suppressedTypedCommands = 0; - this.suppressNextRedoTyped = false; - this.currentKeyEvent = new KeyEvent(Input.Keys.UNKNOWN, Set.of()); - } + public KeyContext(final EditorBuffer editorBuffer, final Consumer<String> statusUpdater, + final TextNavigation navigation, final UndoManager undoManager, final Register register, + final VimConfig config) { + this.editorBuffer = editorBuffer; + this.statusUpdater = statusUpdater; + this.navigation = navigation; + this.undoManager = undoManager; + this.register = register; + this.config = config; + this.statusText = ""; + this.lastSearchQuery = ""; + this.lastSearchDirection = SearchDirection.FORWARD; + this.searchIterator = null; + this.searchIteratorQuery = ""; + this.searchIteratorText = ""; + this.suppressedTypedCommands = 0; + this.suppressNextRedoTyped = false; + this.currentKeyEvent = new KeyEvent(Input.Keys.UNKNOWN, Set.of()); + } - public EditorBuffer editorBuffer() { - return editorBuffer; - } + public EditorBuffer editorBuffer() { + return editorBuffer; + } - public TextNavigation navigation() { - return navigation; - } + public TextNavigation navigation() { + return navigation; + } - public VimConfig config() { - return config; - } + public VimConfig config() { + return config; + } - public void setCurrentKeyEvent(final KeyEvent currentKeyEvent) { - this.currentKeyEvent = currentKeyEvent; - } + public void setCurrentKeyEvent(final KeyEvent currentKeyEvent) { + this.currentKeyEvent = currentKeyEvent; + } - public KeyEvent currentKeyEvent() { - return currentKeyEvent; - } + public KeyEvent currentKeyEvent() { + return currentKeyEvent; + } - public String getStatusText() { - return statusText; - } + public String getStatusText() { + return statusText; + } - public void publishStatus(final String status) { - if (!status.equals(statusText)) { - statusText = status; - if (statusUpdater != null) { - statusUpdater.accept(statusText); - } - } - } + public void publishStatus(final String status) { + if (!status.equals(statusText)) { + statusText = status; + if (statusUpdater != null) { + statusUpdater.accept(statusText); + } + } + } - public String getLastSearchQuery() { - return lastSearchQuery; - } + public String getLastSearchQuery() { + return lastSearchQuery; + } - public void setLastSearchQuery(final String lastSearchQuery) { - this.lastSearchQuery = lastSearchQuery == null ? "" : lastSearchQuery; - } + public void setLastSearchQuery(final String lastSearchQuery) { + this.lastSearchQuery = lastSearchQuery == null ? "" : lastSearchQuery; + } - public void incrementSuppressedTypedCommands() { - suppressedTypedCommands++; - } + public void incrementSuppressedTypedCommands() { + suppressedTypedCommands++; + } - public boolean consumeSuppressedTypedCommand() { - if (suppressedTypedCommands <= 0) { - return false; - } - suppressedTypedCommands--; - return true; - } + public boolean consumeSuppressedTypedCommand() { + if (suppressedTypedCommands <= 0) { + return false; + } + suppressedTypedCommands--; + return true; + } - public void suppressNextRedoTyped() { - suppressNextRedoTyped = true; - } + public void suppressNextRedoTyped() { + suppressNextRedoTyped = true; + } - public boolean consumeRedoTypedSuppression() { - if (!suppressNextRedoTyped) { - return false; - } - suppressNextRedoTyped = false; - return true; - } + public boolean consumeRedoTypedSuppression() { + if (!suppressNextRedoTyped) { + return false; + } + suppressNextRedoTyped = false; + return true; + } - public void clearRedoTypedSuppression() { - suppressNextRedoTyped = false; - } + public void clearRedoTypedSuppression() { + suppressNextRedoTyped = false; + } - public Character toCommandCharacter() { - final int keycode = currentKeyEvent.keycode(); - final boolean shiftDown = currentKeyEvent.hasModifier(EditorModifier.SHIFT); + public Character toCommandCharacter() { + final int keycode = currentKeyEvent.keycode(); + final boolean shiftDown = currentKeyEvent.hasModifier(EditorModifier.SHIFT); - if (keycode == Input.Keys.SLASH) { - return shiftDown ? '?' : '/'; - } + if (keycode == Input.Keys.SLASH) { + return shiftDown ? '?' : '/'; + } - if (keycode == Input.Keys.NUM_4 && shiftDown) { - return '$'; - } + if (keycode == Input.Keys.NUM_4 && shiftDown) { + return '$'; + } - final String keyName = Input.Keys.toString(keycode); - if (keyName == null || keyName.length() != 1) { - return null; - } + final String keyName = Input.Keys.toString(keycode); + if (keyName == null || keyName.length() != 1) { + return null; + } - final char key = keyName.charAt(0); - if (Character.isLetter(key)) { - return shiftDown ? Character.toUpperCase(key) : Character.toLowerCase(key); - } + final char key = keyName.charAt(0); + if (Character.isLetter(key)) { + return shiftDown ? Character.toUpperCase(key) : Character.toLowerCase(key); + } - if (Character.isDigit(key) && !shiftDown) { - return key; - } + if (Character.isDigit(key) && !shiftDown) { + return key; + } - return null; - } + return null; + } - public int readCount(final int count) { - return count > 0 ? count : 1; - } + public int readCount(final int count) { + return count > 0 ? count : 1; + } - public int resetPreferredColumn(final char character, final int preferredColumn) { - if (character == 'j' || character == 'k') { - return preferredColumn; - } - return -1; - } + public int resetPreferredColumn(final char character, final int preferredColumn) { + if (character == 'j' || character == 'k') { + return preferredColumn; + } + return -1; + } - public void moveLeft() { - final int cursor = editorBuffer.getCursorPosition(); - final int lineStart = navigation.findLineStart(editorBuffer.getText(), cursor); - if (cursor > lineStart) { - editorBuffer.setCursorPosition(cursor - 1); - } - } + public void moveLeft() { + final int cursor = editorBuffer.getCursorPosition(); + final int lineStart = navigation.findLineStart(editorBuffer.getText(), cursor); + if (cursor > lineStart) { + editorBuffer.setCursorPosition(cursor - 1); + } + } - public void moveRightNormal() { - final String text = editorBuffer.getText(); - final int cursor = editorBuffer.getCursorPosition(); - final int lineEnd = navigation.findLineEnd(text, cursor); - if (cursor < lineEnd) { - editorBuffer.setCursorPosition(cursor + 1); - } - } + public void moveRightNormal() { + final String text = editorBuffer.getText(); + final int cursor = editorBuffer.getCursorPosition(); + final int lineEnd = navigation.findLineEnd(text, cursor); + if (cursor < lineEnd) { + editorBuffer.setCursorPosition(cursor + 1); + } + } - public void moveRightForAppend() { - final String text = editorBuffer.getText(); - final int cursor = editorBuffer.getCursorPosition(); - if (cursor < text.length() && text.charAt(cursor) != '\n') { - editorBuffer.setCursorPosition(cursor + 1); - } - } + public void moveRightForAppend() { + final String text = editorBuffer.getText(); + final int cursor = editorBuffer.getCursorPosition(); + if (cursor < text.length() && text.charAt(cursor) != '\n') { + editorBuffer.setCursorPosition(cursor + 1); + } + } - public int moveVertical(final int delta, final int repeats, final int preferredColumn, - final boolean clampInNormalMode) { - int preferred = preferredColumn; - for (int i = 0; i < repeats; i++) { - preferred = moveVerticalOnce(delta, preferred, clampInNormalMode); - } - return preferred; - } + public int moveVertical(final int delta, final int repeats, final int preferredColumn, + final boolean clampInNormalMode) { + int preferred = preferredColumn; + for (int i = 0; i < repeats; i++) { + preferred = moveVerticalOnce(delta, preferred, clampInNormalMode); + } + return preferred; + } - private int moveVerticalOnce(final int delta, final int preferredColumn, final boolean clampInNormalMode) { - if (delta == 0) { - return preferredColumn; - } + private int moveVerticalOnce(final int delta, final int preferredColumn, final boolean clampInNormalMode) { + if (delta == 0) { + return preferredColumn; + } - final String text = editorBuffer.getText(); - if (text.isEmpty()) { - return preferredColumn; - } + final String text = editorBuffer.getText(); + if (text.isEmpty()) { + return preferredColumn; + } - final int cursor = editorBuffer.getCursorPosition(); - final int currentLineStart = navigation.findLineStart(text, cursor); - final int resolvedPreferred = preferredColumn < 0 ? Math.max(0, cursor - currentLineStart) : preferredColumn; + final int cursor = editorBuffer.getCursorPosition(); + final int currentLineStart = navigation.findLineStart(text, cursor); + final int resolvedPreferred = preferredColumn < 0 ? Math.max(0, cursor - currentLineStart) : preferredColumn; - int targetStart = currentLineStart; - if (delta > 0) { - targetStart = navigation.findNextLineStart(text, targetStart); - if (targetStart < 0 || targetStart > text.length()) { - return resolvedPreferred; - } - } else { - targetStart = navigation.findPreviousLineStart(text, targetStart); - if (targetStart < 0) { - return resolvedPreferred; - } - } + int targetStart = currentLineStart; + if (delta > 0) { + targetStart = navigation.findNextLineStart(text, targetStart); + if (targetStart < 0 || targetStart > text.length()) { + return resolvedPreferred; + } + } else { + targetStart = navigation.findPreviousLineStart(text, targetStart); + if (targetStart < 0) { + return resolvedPreferred; + } + } - final int targetEndExclusive = navigation.findLineEndExclusiveByStart(text, targetStart); - final int targetLineLength = Math.max(0, targetEndExclusive - targetStart); - final int maxColumn = targetLineLength > 0 ? targetLineLength - 1 : 0; - final int targetColumn = Math.min(resolvedPreferred, maxColumn); - editorBuffer.setCursorPosition(targetStart + targetColumn); - if (clampInNormalMode) { - clampCursorInNormalMode(); - } - return resolvedPreferred; - } + final int targetEndExclusive = navigation.findLineEndExclusiveByStart(text, targetStart); + final int targetLineLength = Math.max(0, targetEndExclusive - targetStart); + final int maxColumn = targetLineLength > 0 ? targetLineLength - 1 : 0; + final int targetColumn = Math.min(resolvedPreferred, maxColumn); + editorBuffer.setCursorPosition(targetStart + targetColumn); + if (clampInNormalMode) { + clampCursorInNormalMode(); + } + return resolvedPreferred; + } - public void moveWordForward() { - final String text = editorBuffer.getText(); - int cursor = editorBuffer.getCursorPosition(); - final int length = text.length(); + public void moveWordForward() { + final String text = editorBuffer.getText(); + int cursor = editorBuffer.getCursorPosition(); + final int length = text.length(); - if (cursor >= length) { - return; - } + if (cursor >= length) { + return; + } - final int originalCursor = cursor; - final char current = text.charAt(cursor); + final int originalCursor = cursor; + final char current = text.charAt(cursor); - if (navigation.isWordCharacter(current)) { - while (cursor < length && navigation.isWordCharacter(text.charAt(cursor))) { - cursor++; - } - while (cursor < length && navigation.isInlineWhitespace(text.charAt(cursor))) { - cursor++; - } - } else if (navigation.isPunctuationWord(current)) { - while (cursor < length && navigation.isPunctuationWord(text.charAt(cursor))) { - cursor++; - } - while (cursor < length && navigation.isInlineWhitespace(text.charAt(cursor))) { - cursor++; - } - if (cursor >= length) { - cursor = originalCursor; - } - } else if (navigation.isInlineWhitespace(current)) { - while (cursor < length && navigation.isInlineWhitespace(text.charAt(cursor))) { - cursor++; - } - } else if (current == '\n' || current == '\r') { - if (cursor < length) { - cursor++; - } - while (cursor < length && navigation.isInlineWhitespace(text.charAt(cursor))) { - cursor++; - } - } + if (navigation.isWordCharacter(current)) { + while (cursor < length && navigation.isWordCharacter(text.charAt(cursor))) { + cursor++; + } + while (cursor < length && navigation.isInlineWhitespace(text.charAt(cursor))) { + cursor++; + } + } else if (navigation.isPunctuationWord(current)) { + while (cursor < length && navigation.isPunctuationWord(text.charAt(cursor))) { + cursor++; + } + while (cursor < length && navigation.isInlineWhitespace(text.charAt(cursor))) { + cursor++; + } + if (cursor >= length) { + cursor = originalCursor; + } + } else if (navigation.isInlineWhitespace(current)) { + while (cursor < length && navigation.isInlineWhitespace(text.charAt(cursor))) { + cursor++; + } + } else if (current == '\n' || current == '\r') { + if (cursor < length) { + cursor++; + } + while (cursor < length && navigation.isInlineWhitespace(text.charAt(cursor))) { + cursor++; + } + } - editorBuffer.setCursorPosition(Math.min(cursor, length)); - } + editorBuffer.setCursorPosition(Math.min(cursor, length)); + } - public void moveWordBackward() { - final String text = editorBuffer.getText(); - int cursor = editorBuffer.getCursorPosition(); + public void moveWordBackward() { + final String text = editorBuffer.getText(); + int cursor = editorBuffer.getCursorPosition(); - if (cursor <= 0) { - return; - } + if (cursor <= 0) { + return; + } - cursor--; - while (cursor > 0 && Character.isWhitespace(text.charAt(cursor))) { - cursor--; - } + cursor--; + while (cursor > 0 && Character.isWhitespace(text.charAt(cursor))) { + cursor--; + } - final char current = text.charAt(cursor); - if (navigation.isWordCharacter(current)) { - while (cursor > 0 && navigation.isWordCharacter(text.charAt(cursor - 1))) { - cursor--; - } - } else { - while (cursor > 0) { - final char previous = text.charAt(cursor - 1); - if (Character.isWhitespace(previous) || navigation.isWordCharacter(previous)) { - break; - } - cursor--; - } - } + final char current = text.charAt(cursor); + if (navigation.isWordCharacter(current)) { + while (cursor > 0 && navigation.isWordCharacter(text.charAt(cursor - 1))) { + cursor--; + } + } else { + while (cursor > 0) { + final char previous = text.charAt(cursor - 1); + if (Character.isWhitespace(previous) || navigation.isWordCharacter(previous)) { + break; + } + cursor--; + } + } - editorBuffer.setCursorPosition(cursor); - } + editorBuffer.setCursorPosition(cursor); + } - public void moveLineStart() { - final int cursor = editorBuffer.getCursorPosition(); - editorBuffer.setCursorPosition(navigation.findLineStart(editorBuffer.getText(), cursor)); - } + public void moveLineStart() { + final int cursor = editorBuffer.getCursorPosition(); + editorBuffer.setCursorPosition(navigation.findLineStart(editorBuffer.getText(), cursor)); + } - public void moveLineEnd() { - final int cursor = editorBuffer.getCursorPosition(); - editorBuffer.setCursorPosition(navigation.findLineEnd(editorBuffer.getText(), cursor)); - } + public void moveLineEnd() { + final int cursor = editorBuffer.getCursorPosition(); + editorBuffer.setCursorPosition(navigation.findLineEnd(editorBuffer.getText(), cursor)); + } - public void moveDocumentEnd() { - final String text = editorBuffer.getText(); - if (!text.isEmpty()) { - editorBuffer.setCursorPosition(navigation.lastNavigablePosition(text)); - } - } + public void moveDocumentEnd() { + final String text = editorBuffer.getText(); + if (!text.isEmpty()) { + editorBuffer.setCursorPosition(navigation.lastNavigablePosition(text)); + } + } - public void moveDocumentStart() { - editorBuffer.setCursorPosition(0); - clampCursorInNormalMode(); - } + public void moveDocumentStart() { + editorBuffer.setCursorPosition(0); + clampCursorInNormalMode(); + } - public void openLineBelow() { - final String text = editorBuffer.getText(); - final int insertAt = navigation.findBelowLineInsertionIndex(text, editorBuffer.getCursorPosition()); - editorBuffer.setText(text.substring(0, insertAt) + "\n" + text.substring(insertAt)); - editorBuffer.setCursorPosition(Math.min(insertAt, editorBuffer.getText().length())); - invalidateSearchIterator(); - recordUndoState(); - } + public void openLineBelow() { + final String text = editorBuffer.getText(); + final int insertAt = navigation.findBelowLineInsertionIndex(text, editorBuffer.getCursorPosition()); + editorBuffer.setText(text.substring(0, insertAt) + "\n" + text.substring(insertAt)); + editorBuffer.setCursorPosition(Math.min(insertAt, editorBuffer.getText().length())); + invalidateSearchIterator(); + recordUndoState(); + } - public void openLineAbove() { - final String text = editorBuffer.getText(); - final int lineStart = navigation.findLineStart(text, editorBuffer.getCursorPosition()); - editorBuffer.setText(text.substring(0, lineStart) + "\n" + text.substring(lineStart)); - editorBuffer.setCursorPosition(lineStart); - invalidateSearchIterator(); - recordUndoState(); - } + public void openLineAbove() { + final String text = editorBuffer.getText(); + final int lineStart = navigation.findLineStart(text, editorBuffer.getCursorPosition()); + editorBuffer.setText(text.substring(0, lineStart) + "\n" + text.substring(lineStart)); + editorBuffer.setCursorPosition(lineStart); + invalidateSearchIterator(); + recordUndoState(); + } - public void deleteCharUnderCursor() { - final String text = editorBuffer.getText(); - final int cursor = editorBuffer.getCursorPosition(); - if (cursor >= text.length() || text.charAt(cursor) == '\n') { - return; - } - register.setText(String.valueOf(text.charAt(cursor))); - register.setType(RegisterType.CHARACTER); - editorBuffer.setText(text.substring(0, cursor) + text.substring(cursor + 1)); - editorBuffer.setCursorPosition(Math.min(cursor, editorBuffer.getText().length())); - invalidateSearchIterator(); - } + public void deleteCharUnderCursor() { + final String text = editorBuffer.getText(); + final int cursor = editorBuffer.getCursorPosition(); + if (cursor >= text.length() || text.charAt(cursor) == '\n') { + return; + } + register.setText(String.valueOf(text.charAt(cursor))); + register.setType(RegisterType.CHARACTER); + editorBuffer.setText(text.substring(0, cursor) + text.substring(cursor + 1)); + editorBuffer.setCursorPosition(Math.min(cursor, editorBuffer.getText().length())); + invalidateSearchIterator(); + } - public void deleteLines(final int repeats) { - final String text = editorBuffer.getText(); - final int start = navigation.findLineStart(text, editorBuffer.getCursorPosition()); - int end = start; - for (int i = 0; i < repeats; i++) { - end = navigation.findNextLineStart(text, end); - if (end == -1) { - end = text.length(); - break; - } - } - if (start >= end) { - return; - } - register.setText(normalizeLineRegister(text.substring(start, end))); - register.setType(RegisterType.LINE); - editorBuffer.setText(text.substring(0, start) + text.substring(end)); - editorBuffer.setCursorPosition(Math.min(start, editorBuffer.getText().length())); - clampCursorInNormalMode(); - invalidateSearchIterator(); - } + public void deleteLines(final int repeats) { + final String text = editorBuffer.getText(); + final int start = navigation.findLineStart(text, editorBuffer.getCursorPosition()); + int end = start; + for (int i = 0; i < repeats; i++) { + end = navigation.findNextLineStart(text, end); + if (end == -1) { + end = text.length(); + break; + } + } + if (start >= end) { + return; + } + register.setText(normalizeLineRegister(text.substring(start, end))); + register.setType(RegisterType.LINE); + editorBuffer.setText(text.substring(0, start) + text.substring(end)); + editorBuffer.setCursorPosition(Math.min(start, editorBuffer.getText().length())); + clampCursorInNormalMode(); + invalidateSearchIterator(); + } - public void yankLines(final int repeats) { - final String text = editorBuffer.getText(); - final int start = navigation.findLineStart(text, editorBuffer.getCursorPosition()); - int end = start; - for (int i = 0; i < repeats; i++) { - end = navigation.findNextLineStart(text, end); - if (end == -1) { - end = text.length(); - break; - } - } - if (start < end) { - register.setText(normalizeLineRegister(text.substring(start, end))); - register.setType(RegisterType.LINE); - } - } + public void yankLines(final int repeats) { + final String text = editorBuffer.getText(); + final int start = navigation.findLineStart(text, editorBuffer.getCursorPosition()); + int end = start; + for (int i = 0; i < repeats; i++) { + end = navigation.findNextLineStart(text, end); + if (end == -1) { + end = text.length(); + break; + } + } + if (start < end) { + register.setText(normalizeLineRegister(text.substring(start, end))); + register.setType(RegisterType.LINE); + } + } - public void changeLines(final int repeats) { - deleteLines(repeats); - } + public void changeLines(final int repeats) { + deleteLines(repeats); + } - public void pasteAfter() { - if (register.getText().isEmpty()) { - return; - } - final String text = editorBuffer.getText(); - final int cursor = editorBuffer.getCursorPosition(); - if (register.getType() == RegisterType.LINE) { - final int insertAt = navigation.findBelowLineInsertionIndex(text, cursor); - String inserted = register.getText(); - if (insertAt == text.length() && insertAt > 0 && text.charAt(insertAt - 1) != '\n') { - inserted = "\n" + inserted; - } - editorBuffer.setText(text.substring(0, insertAt) + inserted + text.substring(insertAt)); - editorBuffer.setCursorPosition(Math.min(insertAt, editorBuffer.getText().length())); - invalidateSearchIterator(); - return; - } + public void pasteAfter() { + if (register.getText().isEmpty()) { + return; + } + final String text = editorBuffer.getText(); + final int cursor = editorBuffer.getCursorPosition(); + if (register.getType() == RegisterType.LINE) { + final int insertAt = navigation.findBelowLineInsertionIndex(text, cursor); + String inserted = register.getText(); + if (insertAt == text.length() && insertAt > 0 && text.charAt(insertAt - 1) != '\n') { + inserted = "\n" + inserted; + } + editorBuffer.setText(text.substring(0, insertAt) + inserted + text.substring(insertAt)); + editorBuffer.setCursorPosition(Math.min(insertAt, editorBuffer.getText().length())); + invalidateSearchIterator(); + return; + } - final int insertAt = Math.min(cursor + 1, text.length()); - editorBuffer.setText(text.substring(0, insertAt) + register.getText() + text.substring(insertAt)); - editorBuffer.setCursorPosition(insertAt + register.getText().length() - 1); - invalidateSearchIterator(); - } + final int insertAt = Math.min(cursor + 1, text.length()); + editorBuffer.setText(text.substring(0, insertAt) + register.getText() + text.substring(insertAt)); + editorBuffer.setCursorPosition(insertAt + register.getText().length() - 1); + invalidateSearchIterator(); + } - public void pasteBefore() { - if (register.getText().isEmpty()) { - return; - } - final String text = editorBuffer.getText(); - final int cursor = editorBuffer.getCursorPosition(); - if (register.getType() == RegisterType.LINE) { - final int insertAt = navigation.findLineStart(text, cursor); - editorBuffer.setText(text.substring(0, insertAt) + register.getText() + text.substring(insertAt)); - editorBuffer.setCursorPosition(insertAt); - invalidateSearchIterator(); - return; - } + public void pasteBefore() { + if (register.getText().isEmpty()) { + return; + } + final String text = editorBuffer.getText(); + final int cursor = editorBuffer.getCursorPosition(); + if (register.getType() == RegisterType.LINE) { + final int insertAt = navigation.findLineStart(text, cursor); + editorBuffer.setText(text.substring(0, insertAt) + register.getText() + text.substring(insertAt)); + editorBuffer.setCursorPosition(insertAt); + invalidateSearchIterator(); + return; + } - final int insertAt = cursor; - editorBuffer.setText(text.substring(0, insertAt) + register.getText() + text.substring(insertAt)); - editorBuffer.setCursorPosition(insertAt + register.getText().length() - 1); - invalidateSearchIterator(); - } + final int insertAt = cursor; + editorBuffer.setText(text.substring(0, insertAt) + register.getText() + text.substring(insertAt)); + editorBuffer.setCursorPosition(insertAt + register.getText().length() - 1); + invalidateSearchIterator(); + } - public void updateVisualSelection(final int anchor) { - final int cursor = editorBuffer.getCursorPosition(); - if (cursor != anchor) { - int selectionStart = anchor; - if (cursor < anchor && anchor < editorBuffer.getText().length()) { - selectionStart = anchor + 1; - } - editorBuffer.setSelectionEndpoints(selectionStart, cursor); - } else { - editorBuffer.clearSelection(); - } - } + public void updateVisualSelection(final int anchor) { + final int cursor = editorBuffer.getCursorPosition(); + if (cursor != anchor) { + int selectionStart = anchor; + if (cursor < anchor && anchor < editorBuffer.getText().length()) { + selectionStart = anchor + 1; + } + editorBuffer.setSelectionEndpoints(selectionStart, cursor); + } else { + editorBuffer.clearSelection(); + } + } - public void updateVisualLineSelection(final int anchorLine, final int cursorLine) { - final String text = editorBuffer.getText(); - final int startLine = Math.min(anchorLine, cursorLine); - final int endLine = Math.max(anchorLine, cursorLine); - final int start = navigation.findLineStartByLine(text, startLine); - final int end = navigation.findLineSelectionEndByLine(text, endLine); - if (start < end) { - final int selectionStart = cursorLine > anchorLine ? start : end; - final int cursorPosition = cursorLine > anchorLine - ? resolveVisualLineCursorPosition(text, start, end) - : start; - editorBuffer.setSelectionEndpoints(selectionStart, cursorPosition); - } else { - editorBuffer.clearSelection(); - } - } + public void updateVisualLineSelection(final int anchorLine, final int cursorLine) { + final String text = editorBuffer.getText(); + final int startLine = Math.min(anchorLine, cursorLine); + final int endLine = Math.max(anchorLine, cursorLine); + final int start = navigation.findLineStartByLine(text, startLine); + final int end = navigation.findLineSelectionEndByLine(text, endLine); + if (start < end) { + final int selectionStart = cursorLine > anchorLine ? start : end; + final int cursorPosition = cursorLine > anchorLine + ? resolveVisualLineCursorPosition(text, start, end) + : start; + editorBuffer.setSelectionEndpoints(selectionStart, cursorPosition); + } else { + editorBuffer.clearSelection(); + } + } - public int adjustVisualLineCursor(final int cursorLine, final int delta) { - final int maxLine = maxSelectableLineIndex(); - return Math.max(0, Math.min(maxLine, cursorLine + delta)); - } + public int adjustVisualLineCursor(final int cursorLine, final int delta) { + final int maxLine = maxSelectableLineIndex(); + return Math.max(0, Math.min(maxLine, cursorLine + delta)); + } - public int maxSelectableLineIndex() { - int totalLines = editorBuffer.getLines(); - final String text = editorBuffer.getText(); - if (totalLines > 1 && text.endsWith("\n")) { - totalLines--; - } - return Math.max(0, totalLines - 1); - } + public int maxSelectableLineIndex() { + int totalLines = editorBuffer.getLines(); + final String text = editorBuffer.getText(); + if (totalLines > 1 && text.endsWith("\n")) { + totalLines--; + } + return Math.max(0, totalLines - 1); + } - private int resolveVisualLineCursorPosition(final String text, final int lineStart, final int selectionEnd) { - if (!text.isEmpty() && selectionEnd == text.length() && text.charAt(text.length() - 1) == '\n') { - return Math.max(lineStart, selectionEnd - 1); - } - return selectionEnd; - } + private int resolveVisualLineCursorPosition(final String text, final int lineStart, final int selectionEnd) { + if (!text.isEmpty() && selectionEnd == text.length() && text.charAt(text.length() - 1) == '\n') { + return Math.max(lineStart, selectionEnd - 1); + } + return selectionEnd; + } - public HandleResult applyVisualOperator(final VisualState state, final PendingOperator operator) { - final int start = Math.min(state.anchor(), editorBuffer.getCursorPosition()); - final int max = Math.max(state.anchor(), editorBuffer.getCursorPosition()); - final int end = Math.min(editorBuffer.getText().length(), max + 1); - if (start >= end) { - editorBuffer.clearSelection(); - return HandleResult.handled(NormalState.DEFAULT); - } + public HandleResult applyVisualOperator(final VisualState state, final PendingOperator operator) { + final int start = Math.min(state.anchor(), editorBuffer.getCursorPosition()); + final int max = Math.max(state.anchor(), editorBuffer.getCursorPosition()); + final int end = Math.min(editorBuffer.getText().length(), max + 1); + if (start >= end) { + editorBuffer.clearSelection(); + return HandleResult.handled(NormalState.DEFAULT); + } - final String text = editorBuffer.getText(); - register.setText(text.substring(start, end)); - register.setType(RegisterType.CHARACTER); + final String text = editorBuffer.getText(); + register.setText(text.substring(start, end)); + register.setType(RegisterType.CHARACTER); - if (operator == PendingOperator.YANK) { - editorBuffer.clearSelection(); - editorBuffer.setCursorPosition(start); - clampCursorInNormalMode(); - return HandleResult.handled(NormalState.DEFAULT); - } + if (operator == PendingOperator.YANK) { + editorBuffer.clearSelection(); + editorBuffer.setCursorPosition(start); + clampCursorInNormalMode(); + return HandleResult.handled(NormalState.DEFAULT); + } - editorBuffer.setText(text.substring(0, start) + text.substring(end)); - editorBuffer.setCursorPosition(Math.min(start, editorBuffer.getText().length())); - editorBuffer.clearSelection(); - recordUndoState(); - invalidateSearchIterator(); + editorBuffer.setText(text.substring(0, start) + text.substring(end)); + editorBuffer.setCursorPosition(Math.min(start, editorBuffer.getText().length())); + editorBuffer.clearSelection(); + recordUndoState(); + invalidateSearchIterator(); - if (operator == PendingOperator.CHANGE) { - return HandleResult.handled(new InsertState(editorBuffer.getText(), editorBuffer.getCursorPosition())); - } - clampCursorInNormalMode(); - return HandleResult.handled(NormalState.DEFAULT); - } + if (operator == PendingOperator.CHANGE) { + return HandleResult.handled(new InsertState(editorBuffer.getText(), editorBuffer.getCursorPosition())); + } + clampCursorInNormalMode(); + return HandleResult.handled(NormalState.DEFAULT); + } - public HandleResult applyVisualLineOperator(final VisualLineState state, final PendingOperator operator) { - final String text = editorBuffer.getText(); - final int startLine = Math.min(state.anchorLine(), state.cursorLine()); - final int endLine = Math.max(state.anchorLine(), state.cursorLine()); - final int start = navigation.findLineStartByLine(text, startLine); - final int end = navigation.findLineSelectionEndByLine(text, endLine); - if (start >= end) { - editorBuffer.clearSelection(); - return HandleResult.handled(NormalState.DEFAULT); - } + public HandleResult applyVisualLineOperator(final VisualLineState state, final PendingOperator operator) { + final String text = editorBuffer.getText(); + final int startLine = Math.min(state.anchorLine(), state.cursorLine()); + final int endLine = Math.max(state.anchorLine(), state.cursorLine()); + final int start = navigation.findLineStartByLine(text, startLine); + final int end = navigation.findLineSelectionEndByLine(text, endLine); + if (start >= end) { + editorBuffer.clearSelection(); + return HandleResult.handled(NormalState.DEFAULT); + } - register.setText(normalizeLineRegister(text.substring(start, end))); - register.setType(RegisterType.LINE); + register.setText(normalizeLineRegister(text.substring(start, end))); + register.setType(RegisterType.LINE); - if (operator == PendingOperator.YANK) { - editorBuffer.clearSelection(); - editorBuffer.setCursorPosition(start); - clampCursorInNormalMode(); - return HandleResult.handled(NormalState.DEFAULT); - } + if (operator == PendingOperator.YANK) { + editorBuffer.clearSelection(); + editorBuffer.setCursorPosition(start); + clampCursorInNormalMode(); + return HandleResult.handled(NormalState.DEFAULT); + } - editorBuffer.setText(text.substring(0, start) + text.substring(end)); - editorBuffer.setCursorPosition(Math.min(start, editorBuffer.getText().length())); - editorBuffer.clearSelection(); - recordUndoState(); - invalidateSearchIterator(); + editorBuffer.setText(text.substring(0, start) + text.substring(end)); + editorBuffer.setCursorPosition(Math.min(start, editorBuffer.getText().length())); + editorBuffer.clearSelection(); + recordUndoState(); + invalidateSearchIterator(); - if (operator == PendingOperator.CHANGE) { - return HandleResult.handled(new InsertState(editorBuffer.getText(), editorBuffer.getCursorPosition())); - } - clampCursorInNormalMode(); - return HandleResult.handled(NormalState.DEFAULT); - } + if (operator == PendingOperator.CHANGE) { + return HandleResult.handled(new InsertState(editorBuffer.getText(), editorBuffer.getCursorPosition())); + } + clampCursorInNormalMode(); + return HandleResult.handled(NormalState.DEFAULT); + } - public boolean findNext(final String query, final SearchDirection direction) { - if (query == null || query.isEmpty()) { - return false; - } - final String text = editorBuffer.getText(); - if (text.isEmpty()) { - return false; - } + public boolean findNext(final String query, final SearchDirection direction) { + if (query == null || query.isEmpty()) { + return false; + } + final String text = editorBuffer.getText(); + if (text.isEmpty()) { + return false; + } - if (searchIterator == null || !query.equals(searchIteratorQuery) || direction != lastSearchDirection - || !text.equals(searchIteratorText)) { - searchIterator = new SearchIterator(text, query, editorBuffer.getCursorPosition(), direction, - config.wrapSearch()); - searchIteratorQuery = query; - searchIteratorText = text; - lastSearchDirection = direction; - } + if (searchIterator == null || !query.equals(searchIteratorQuery) || direction != lastSearchDirection + || !text.equals(searchIteratorText)) { + searchIterator = new SearchIterator(text, query, editorBuffer.getCursorPosition(), direction, + config.wrapSearch()); + searchIteratorQuery = query; + searchIteratorText = text; + lastSearchDirection = direction; + } - final OptionalInt found = searchIterator.nextMatch(); - if (found.isEmpty()) { - return false; - } + final OptionalInt found = searchIterator.nextMatch(); + if (found.isEmpty()) { + return false; + } - editorBuffer.setCursorPosition(found.getAsInt()); - clampCursorInNormalMode(); - return true; - } + editorBuffer.setCursorPosition(found.getAsInt()); + clampCursorInNormalMode(); + return true; + } - public void invalidateSearchIterator() { - searchIterator = null; - searchIteratorQuery = ""; - searchIteratorText = ""; - } + public void invalidateSearchIterator() { + searchIterator = null; + searchIteratorQuery = ""; + searchIteratorText = ""; + } - public void recordUndoState() { - undoManager.record(editorBuffer.getText(), editorBuffer.getCursorPosition()); - } + public void recordUndoState() { + undoManager.record(editorBuffer.getText(), editorBuffer.getCursorPosition()); + } - public ModeState undoToNormalState() { - final UndoState previous = undoManager.undo(); - if (previous == null) { - return NormalState.DEFAULT; - } - applyState(previous); - return NormalState.DEFAULT; - } + public ModeState undoToNormalState() { + final UndoState previous = undoManager.undo(); + if (previous == null) { + return NormalState.DEFAULT; + } + applyState(previous); + return NormalState.DEFAULT; + } - public ModeState redoToNormalState() { - final UndoState target = undoManager.redo(); - if (target == null) { - return NormalState.DEFAULT; - } - applyState(target); - return NormalState.DEFAULT; - } + public ModeState redoToNormalState() { + final UndoState target = undoManager.redo(); + if (target == null) { + return NormalState.DEFAULT; + } + applyState(target); + return NormalState.DEFAULT; + } - public void maybeRecordInsertEdit(final InsertState state) { - final String currentText = editorBuffer.getText(); - final int currentCursor = editorBuffer.getCursorPosition(); - if (!currentText.equals(state.insertStartText()) || currentCursor != state.insertStartCursor()) { - recordUndoState(); - } - } + public void maybeRecordInsertEdit(final InsertState state) { + final String currentText = editorBuffer.getText(); + final int currentCursor = editorBuffer.getCursorPosition(); + if (!currentText.equals(state.insertStartText()) || currentCursor != state.insertStartCursor()) { + recordUndoState(); + } + } - private void applyState(final UndoState state) { - editorBuffer.setText(state.getText()); - editorBuffer.setCursorPosition(Math.min(state.getCursor(), state.getText().length())); - editorBuffer.clearSelection(); - clampCursorInNormalMode(); - invalidateSearchIterator(); - } + private void applyState(final UndoState state) { + editorBuffer.setText(state.getText()); + editorBuffer.setCursorPosition(Math.min(state.getCursor(), state.getText().length())); + editorBuffer.clearSelection(); + clampCursorInNormalMode(); + invalidateSearchIterator(); + } - public void clampCursorInNormalMode() { - final String text = editorBuffer.getText(); - if (text.isEmpty()) { - editorBuffer.setCursorPosition(0); - return; - } - final int maxCursor = navigation.maxCursorPositionInNormalMode(text); - final int cursor = Math.min(editorBuffer.getCursorPosition(), maxCursor); - final int lineStart = navigation.findLineStart(text, cursor); - if (cursor < text.length() && text.charAt(cursor) == '\n' && cursor > lineStart) { - editorBuffer.setCursorPosition(cursor - 1); - } else { - editorBuffer.setCursorPosition(cursor); - } - } + public void clampCursorInNormalMode() { + final String text = editorBuffer.getText(); + if (text.isEmpty()) { + editorBuffer.setCursorPosition(0); + return; + } + final int maxCursor = navigation.maxCursorPositionInNormalMode(text); + final int cursor = Math.min(editorBuffer.getCursorPosition(), maxCursor); + final int lineStart = navigation.findLineStart(text, cursor); + if (cursor < text.length() && text.charAt(cursor) == '\n' && cursor > lineStart) { + editorBuffer.setCursorPosition(cursor - 1); + } else { + editorBuffer.setCursorPosition(cursor); + } + } - public int getCursorPhysicalLine() { - return editorBuffer.getCursorLine(); - } + public int getCursorPhysicalLine() { + return editorBuffer.getCursorLine(); + } - public String text() { - return editorBuffer.getText(); - } + public String text() { + return editorBuffer.getText(); + } - public int cursor() { - return editorBuffer.getCursorPosition(); - } + public int cursor() { + return editorBuffer.getCursorPosition(); + } - public void setCursor(final int position) { - editorBuffer.setCursorPosition(position); - } + public void setCursor(final int position) { + editorBuffer.setCursorPosition(position); + } - public void setText(final String text) { - editorBuffer.setText(text); - invalidateSearchIterator(); - } + public void setText(final String text) { + editorBuffer.setText(text); + invalidateSearchIterator(); + } - public void clearSelection() { - editorBuffer.clearSelection(); - } + public void clearSelection() { + editorBuffer.clearSelection(); + } - public void setSelectionEndpoints(final int selectionStart, final int cursorPosition) { - editorBuffer.setSelectionEndpoints(selectionStart, cursorPosition); - } + public void setSelectionEndpoints(final int selectionStart, final int cursorPosition) { + editorBuffer.setSelectionEndpoints(selectionStart, cursorPosition); + } - public Register register() { - return register; - } + public Register register() { + return register; + } - private String normalizeLineRegister(final String lineText) { - if (lineText.isEmpty() || lineText.charAt(lineText.length() - 1) == '\n') { - return lineText; - } - return lineText + "\n"; - } + private String normalizeLineRegister(final String lineText) { + if (lineText.isEmpty() || lineText.charAt(lineText.length() - 1) == '\n') { + return lineText; + } + return lineText + "\n"; + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/CommandAction.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/CommandAction.java index 7a1b85c..9fcf694 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/CommandAction.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/CommandAction.java @@ -4,5 +4,5 @@ import coffee.liz.abstractionengine.app.ui.editor.vim.context.KeyContext; import coffee.liz.abstractionengine.app.ui.editor.vim.state.ModeState; public interface CommandAction<S extends ModeState> { - HandleResult execute(S state, KeyContext context); + HandleResult execute(S state, KeyContext context); } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/HandleResult.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/HandleResult.java index 9a4e2e4..b58fa77 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/HandleResult.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/HandleResult.java @@ -3,30 +3,30 @@ package coffee.liz.abstractionengine.app.ui.editor.vim.handler; import coffee.liz.abstractionengine.app.ui.editor.vim.state.ModeState; public record HandleResult(boolean consumed, boolean suppressTyped, ModeState nextState) { - public static HandleResult notHandled() { - return new HandleResult(false, false, null); - } + public static HandleResult notHandled() { + return new HandleResult(false, false, null); + } - public static HandleResult handled() { - return new HandleResult(true, false, null); - } + public static HandleResult handled() { + return new HandleResult(true, false, null); + } - public static HandleResult handled(final ModeState nextState) { - return new HandleResult(true, false, nextState); - } + public static HandleResult handled(final ModeState nextState) { + return new HandleResult(true, false, nextState); + } - public static HandleResult handledAndSuppressTyped() { - return new HandleResult(true, true, null); - } + public static HandleResult handledAndSuppressTyped() { + return new HandleResult(true, true, null); + } - public static HandleResult handledAndSuppressTyped(final ModeState nextState) { - return new HandleResult(true, true, nextState); - } + public static HandleResult handledAndSuppressTyped(final ModeState nextState) { + return new HandleResult(true, true, nextState); + } - public HandleResult withSuppressTyped() { - if (!consumed) { - return this; - } - return new HandleResult(true, true, nextState); - } + public HandleResult withSuppressTyped() { + if (!consumed) { + return this; + } + return new HandleResult(true, true, nextState); + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/InsertModeHandler.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/InsertModeHandler.java index 176664c..ff9c39f 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/InsertModeHandler.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/InsertModeHandler.java @@ -6,19 +6,19 @@ import coffee.liz.abstractionengine.app.ui.editor.vim.context.KeyContext; import com.badlogic.gdx.Input; public class InsertModeHandler implements ModeHandler<InsertState> { - @Override - public HandleResult onKeyDown(final InsertState state, final KeyContext context) { - if (context.currentKeyEvent().keycode() == Input.Keys.ESCAPE) { - context.maybeRecordInsertEdit(state); - context.clampCursorInNormalMode(); - context.clearSelection(); - return HandleResult.handled(NormalState.DEFAULT); - } - return HandleResult.notHandled(); - } + @Override + public HandleResult onKeyDown(final InsertState state, final KeyContext context) { + if (context.currentKeyEvent().keycode() == Input.Keys.ESCAPE) { + context.maybeRecordInsertEdit(state); + context.clampCursorInNormalMode(); + context.clearSelection(); + return HandleResult.handled(NormalState.DEFAULT); + } + return HandleResult.notHandled(); + } - @Override - public HandleResult onKeyTyped(final InsertState state, final char character, final KeyContext context) { - return HandleResult.notHandled(); - } + @Override + public HandleResult onKeyTyped(final InsertState state, final char character, final KeyContext context) { + return HandleResult.notHandled(); + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/ModeHandler.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/ModeHandler.java index ed04ce2..430e9c4 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/ModeHandler.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/ModeHandler.java @@ -4,7 +4,7 @@ import coffee.liz.abstractionengine.app.ui.editor.vim.context.KeyContext; import coffee.liz.abstractionengine.app.ui.editor.vim.state.ModeState; public interface ModeHandler<T extends ModeState> { - HandleResult onKeyDown(T state, KeyContext context); + HandleResult onKeyDown(T state, KeyContext context); - HandleResult onKeyTyped(T state, char character, KeyContext context); + HandleResult onKeyTyped(T state, char character, KeyContext context); } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/MotionAction.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/MotionAction.java index e6332dd..3e9a0bf 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/MotionAction.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/MotionAction.java @@ -3,5 +3,5 @@ package coffee.liz.abstractionengine.app.ui.editor.vim.handler; import coffee.liz.abstractionengine.app.ui.editor.vim.context.KeyContext; public interface MotionAction { - int execute(KeyContext context, int repeats, int preferredColumn, boolean clampInNormalMode); + int execute(KeyContext context, int repeats, int preferredColumn, boolean clampInNormalMode); } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/NormalModeHandler.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/NormalModeHandler.java index 65e6f71..e064b0e 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/NormalModeHandler.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/NormalModeHandler.java @@ -14,313 +14,313 @@ import java.util.HashMap; import java.util.Map; public class NormalModeHandler implements ModeHandler<NormalState> { - private final Map<Character, CommandAction<NormalState>> commandActions = createCommandActions(); - private final Map<Character, MotionAction> motionActions = createMotionActions(); + private final Map<Character, CommandAction<NormalState>> commandActions = createCommandActions(); + private final Map<Character, MotionAction> motionActions = createMotionActions(); - @Override - public HandleResult onKeyDown(final NormalState state, final KeyContext context) { - if (context.currentKeyEvent().keycode() == Input.Keys.ESCAPE) { - return HandleResult.handled(NormalState.DEFAULT); - } + @Override + public HandleResult onKeyDown(final NormalState state, final KeyContext context) { + if (context.currentKeyEvent().keycode() == Input.Keys.ESCAPE) { + return HandleResult.handled(NormalState.DEFAULT); + } - final Character commandCharacter = context.toCommandCharacter(); - if (commandCharacter != null) { - return onKeyTyped(state, commandCharacter.charValue(), context).withSuppressTyped(); - } + final Character commandCharacter = context.toCommandCharacter(); + if (commandCharacter != null) { + return onKeyTyped(state, commandCharacter.charValue(), context).withSuppressTyped(); + } - return HandleResult.handled(); - } + return HandleResult.handled(); + } - @Override - public HandleResult onKeyTyped(final NormalState state, final char character, final KeyContext context) { - if (character >= '1' && character <= '9') { - return HandleResult.handled(new NormalState(state.count() * 10 + (character - '0'), state.pendingOperator(), - state.preferredColumn())); - } + @Override + public HandleResult onKeyTyped(final NormalState state, final char character, final KeyContext context) { + if (character >= '1' && character <= '9') { + return HandleResult.handled(new NormalState(state.count() * 10 + (character - '0'), state.pendingOperator(), + state.preferredColumn())); + } - if (character == '0' && state.count() > 0) { - return HandleResult - .handled(new NormalState(state.count() * 10, state.pendingOperator(), state.preferredColumn())); - } + if (character == '0' && state.count() > 0) { + return HandleResult + .handled(new NormalState(state.count() * 10, state.pendingOperator(), state.preferredColumn())); + } - if (state.pendingOperator() != PendingOperator.NONE) { - return handlePendingOperator(state, character, context); - } + if (state.pendingOperator() != PendingOperator.NONE) { + return handlePendingOperator(state, character, context); + } - final NormalState normalizedState = new NormalState(state.count(), PendingOperator.NONE, - context.resetPreferredColumn(character, state.preferredColumn())); - final CommandAction<NormalState> action = commandActions.get(character); - if (action == null) { - return HandleResult.handled(new NormalState(0, PendingOperator.NONE, normalizedState.preferredColumn())); - } - return action.execute(normalizedState, context); - } + final NormalState normalizedState = new NormalState(state.count(), PendingOperator.NONE, + context.resetPreferredColumn(character, state.preferredColumn())); + final CommandAction<NormalState> action = commandActions.get(character); + if (action == null) { + return HandleResult.handled(new NormalState(0, PendingOperator.NONE, normalizedState.preferredColumn())); + } + return action.execute(normalizedState, context); + } - private HandleResult handlePendingOperator(final NormalState state, final char character, - final KeyContext context) { - if (state.pendingOperator() == PendingOperator.G_PREFIX) { - if (character == 'g') { - context.moveDocumentStart(); - } - return HandleResult.handled(NormalState.DEFAULT); - } + private HandleResult handlePendingOperator(final NormalState state, final char character, + final KeyContext context) { + if (state.pendingOperator() == PendingOperator.G_PREFIX) { + if (character == 'g') { + context.moveDocumentStart(); + } + return HandleResult.handled(NormalState.DEFAULT); + } - final PendingOperator operator = state.pendingOperator(); - final int repeats = context.readCount(state.count()); - if (character == 'd' && operator == PendingOperator.DELETE) { - context.deleteLines(repeats); - context.recordUndoState(); - return HandleResult.handled(NormalState.DEFAULT); - } - if (character == 'y' && operator == PendingOperator.YANK) { - context.yankLines(repeats); - return HandleResult.handled(NormalState.DEFAULT); - } - if (character == 'c' && operator == PendingOperator.CHANGE) { - context.changeLines(repeats); - context.recordUndoState(); - return HandleResult.handled(new InsertState(context.text(), context.cursor())); - } + final PendingOperator operator = state.pendingOperator(); + final int repeats = context.readCount(state.count()); + if (character == 'd' && operator == PendingOperator.DELETE) { + context.deleteLines(repeats); + context.recordUndoState(); + return HandleResult.handled(NormalState.DEFAULT); + } + if (character == 'y' && operator == PendingOperator.YANK) { + context.yankLines(repeats); + return HandleResult.handled(NormalState.DEFAULT); + } + if (character == 'c' && operator == PendingOperator.CHANGE) { + context.changeLines(repeats); + context.recordUndoState(); + return HandleResult.handled(new InsertState(context.text(), context.cursor())); + } - final int start = context.cursor(); - final int updatedPreferred = applyMotion(character, repeats, state.preferredColumn(), context, true); - int end = context.cursor(); - if (character == '$') { - end = Math.min(context.text().length(), end + 1); - } + final int start = context.cursor(); + final int updatedPreferred = applyMotion(character, repeats, state.preferredColumn(), context, true); + int end = context.cursor(); + if (character == '$') { + end = Math.min(context.text().length(), end + 1); + } - final int rangeStart = Math.min(start, end); - final int rangeEnd = Math.max(start, end); - if (rangeStart == rangeEnd) { - if (character == 'l') { - final String text = context.text(); - if (start < text.length() && text.charAt(start) != '\n') { - context.register().setText(text.substring(start, start + 1)); - context.register().setType(RegisterType.CHARACTER); - if (operator == PendingOperator.YANK) { - return HandleResult.handled(new NormalState(0, PendingOperator.NONE, updatedPreferred)); - } + final int rangeStart = Math.min(start, end); + final int rangeEnd = Math.max(start, end); + if (rangeStart == rangeEnd) { + if (character == 'l') { + final String text = context.text(); + if (start < text.length() && text.charAt(start) != '\n') { + context.register().setText(text.substring(start, start + 1)); + context.register().setType(RegisterType.CHARACTER); + if (operator == PendingOperator.YANK) { + return HandleResult.handled(new NormalState(0, PendingOperator.NONE, updatedPreferred)); + } - context.setText(text.substring(0, start) + text.substring(start + 1)); - context.setCursor(Math.min(start, context.text().length())); - context.clampCursorInNormalMode(); - context.recordUndoState(); - if (operator == PendingOperator.CHANGE) { - return HandleResult.handled(new InsertState(context.text(), context.cursor())); - } - } - } - return HandleResult.handled(new NormalState(0, PendingOperator.NONE, updatedPreferred)); - } + context.setText(text.substring(0, start) + text.substring(start + 1)); + context.setCursor(Math.min(start, context.text().length())); + context.clampCursorInNormalMode(); + context.recordUndoState(); + if (operator == PendingOperator.CHANGE) { + return HandleResult.handled(new InsertState(context.text(), context.cursor())); + } + } + } + return HandleResult.handled(new NormalState(0, PendingOperator.NONE, updatedPreferred)); + } - if (operator == PendingOperator.YANK) { - context.register().setText(context.text().substring(rangeStart, rangeEnd)); - context.register().setType(RegisterType.CHARACTER); - context.setCursor(start); - context.clampCursorInNormalMode(); - return HandleResult.handled(new NormalState(0, PendingOperator.NONE, updatedPreferred)); - } + if (operator == PendingOperator.YANK) { + context.register().setText(context.text().substring(rangeStart, rangeEnd)); + context.register().setType(RegisterType.CHARACTER); + context.setCursor(start); + context.clampCursorInNormalMode(); + return HandleResult.handled(new NormalState(0, PendingOperator.NONE, updatedPreferred)); + } - final String text = context.text(); - context.register().setText(text.substring(rangeStart, rangeEnd)); - context.register().setType(RegisterType.CHARACTER); - context.setText(text.substring(0, rangeStart) + text.substring(rangeEnd)); - context.setCursor(Math.min(rangeStart, context.text().length())); - context.clampCursorInNormalMode(); - context.recordUndoState(); + final String text = context.text(); + context.register().setText(text.substring(rangeStart, rangeEnd)); + context.register().setType(RegisterType.CHARACTER); + context.setText(text.substring(0, rangeStart) + text.substring(rangeEnd)); + context.setCursor(Math.min(rangeStart, context.text().length())); + context.clampCursorInNormalMode(); + context.recordUndoState(); - if (operator == PendingOperator.CHANGE) { - return HandleResult.handled(new InsertState(context.text(), context.cursor())); - } - return HandleResult.handled(new NormalState(0, PendingOperator.NONE, updatedPreferred)); - } + if (operator == PendingOperator.CHANGE) { + return HandleResult.handled(new InsertState(context.text(), context.cursor())); + } + return HandleResult.handled(new NormalState(0, PendingOperator.NONE, updatedPreferred)); + } - private int applyMotion(final char motion, final int repeats, final int preferredColumn, final KeyContext context, - final boolean clampInNormalMode) { - final MotionAction action = motionActions.get(motion); - if (action == null) { - return preferredColumn; - } - return action.execute(context, repeats, preferredColumn, clampInNormalMode); - } + private int applyMotion(final char motion, final int repeats, final int preferredColumn, final KeyContext context, + final boolean clampInNormalMode) { + final MotionAction action = motionActions.get(motion); + if (action == null) { + return preferredColumn; + } + return action.execute(context, repeats, preferredColumn, clampInNormalMode); + } - private Map<Character, CommandAction<NormalState>> createCommandActions() { - final Map<Character, CommandAction<NormalState>> map = new HashMap<>(); - map.put('h', (state, context) -> { - final int repeats = context.readCount(state.count()); - for (int i = 0; i < repeats; i++) { - context.moveLeft(); - } - return HandleResult.handled(new NormalState(0, PendingOperator.NONE, state.preferredColumn())); - }); - map.put('j', (state, context) -> { - final int repeats = context.readCount(state.count()); - final int preferred = context.moveVertical(1, repeats, state.preferredColumn(), true); - return HandleResult.handled(new NormalState(0, PendingOperator.NONE, preferred)); - }); - map.put('k', (state, context) -> { - final int repeats = context.readCount(state.count()); - final int preferred = context.moveVertical(-1, repeats, state.preferredColumn(), true); - return HandleResult.handled(new NormalState(0, PendingOperator.NONE, preferred)); - }); - map.put('l', (state, context) -> { - final int repeats = context.readCount(state.count()); - for (int i = 0; i < repeats; i++) { - context.moveRightNormal(); - } - return HandleResult.handled(new NormalState(0, PendingOperator.NONE, state.preferredColumn())); - }); - map.put('w', (state, context) -> { - final int repeats = context.readCount(state.count()); - for (int i = 0; i < repeats; i++) { - context.moveWordForward(); - } - context.clampCursorInNormalMode(); - return HandleResult.handled(new NormalState(0, PendingOperator.NONE, state.preferredColumn())); - }); - map.put('b', (state, context) -> { - final int repeats = context.readCount(state.count()); - for (int i = 0; i < repeats; i++) { - context.moveWordBackward(); - } - return HandleResult.handled(new NormalState(0, PendingOperator.NONE, state.preferredColumn())); - }); - map.put('0', (state, context) -> { - context.moveLineStart(); - return HandleResult.handled(new NormalState(0, PendingOperator.NONE, state.preferredColumn())); - }); - map.put('$', (state, context) -> { - context.moveLineEnd(); - return HandleResult.handled(new NormalState(0, PendingOperator.NONE, state.preferredColumn())); - }); - map.put('G', (state, context) -> { - context.moveDocumentEnd(); - return HandleResult.handled(new NormalState(0, PendingOperator.NONE, state.preferredColumn())); - }); - map.put('g', (state, context) -> HandleResult - .handled(new NormalState(0, PendingOperator.G_PREFIX, state.preferredColumn()))); - map.put('i', (state, context) -> HandleResult.handled(new InsertState(context.text(), context.cursor()))); - map.put('a', (state, context) -> { - context.moveRightForAppend(); - return HandleResult.handled(new InsertState(context.text(), context.cursor())); - }); - map.put('o', (state, context) -> { - context.openLineBelow(); - return HandleResult.handled(new InsertState(context.text(), context.cursor())); - }); - map.put('O', (state, context) -> { - context.openLineAbove(); - return HandleResult.handled(new InsertState(context.text(), context.cursor())); - }); - map.put('x', (state, context) -> { - final int repeats = context.readCount(state.count()); - for (int i = 0; i < repeats; i++) { - context.deleteCharUnderCursor(); - } - context.clampCursorInNormalMode(); - context.recordUndoState(); - return HandleResult.handled(new NormalState(0, PendingOperator.NONE, state.preferredColumn())); - }); - map.put('d', (state, context) -> HandleResult - .handled(new NormalState(0, PendingOperator.DELETE, state.preferredColumn()))); - map.put('y', (state, context) -> HandleResult - .handled(new NormalState(0, PendingOperator.YANK, state.preferredColumn()))); - map.put('c', (state, context) -> HandleResult - .handled(new NormalState(0, PendingOperator.CHANGE, state.preferredColumn()))); - map.put('p', (state, context) -> { - final int repeats = context.readCount(state.count()); - for (int i = 0; i < repeats; i++) { - context.pasteAfter(); - } - context.clampCursorInNormalMode(); - context.recordUndoState(); - return HandleResult.handled(new NormalState(0, PendingOperator.NONE, state.preferredColumn())); - }); - map.put('P', (state, context) -> { - final int repeats = context.readCount(state.count()); - for (int i = 0; i < repeats; i++) { - context.pasteBefore(); - } - context.clampCursorInNormalMode(); - context.recordUndoState(); - return HandleResult.handled(new NormalState(0, PendingOperator.NONE, state.preferredColumn())); - }); - map.put('v', (state, context) -> { - final int anchor = context.cursor(); - context.updateVisualSelection(anchor); - return HandleResult.handled(new VisualState(anchor, state.preferredColumn(), PendingOperator.NONE, 0)); - }); - map.put('V', (state, context) -> { - final int anchorLine = context.getCursorPhysicalLine(); - context.updateVisualLineSelection(anchorLine, anchorLine); - return HandleResult.handled( - new VisualLineState(anchorLine, anchorLine, state.preferredColumn(), PendingOperator.NONE, 0)); - }); - map.put('/', (state, context) -> HandleResult - .handled(new SearchState(new StringBuilder(), context.getLastSearchQuery(), SearchDirection.FORWARD))); - map.put('?', (state, context) -> HandleResult - .handled(new SearchState(new StringBuilder(), context.getLastSearchQuery(), SearchDirection.BACKWARD))); - map.put('n', (state, context) -> { - context.findNext(context.getLastSearchQuery(), SearchDirection.FORWARD); - return HandleResult.handled(new NormalState(0, PendingOperator.NONE, state.preferredColumn())); - }); - map.put('N', (state, context) -> { - context.findNext(context.getLastSearchQuery(), SearchDirection.BACKWARD); - return HandleResult.handled(new NormalState(0, PendingOperator.NONE, state.preferredColumn())); - }); - map.put('u', (state, context) -> HandleResult.handled(context.undoToNormalState())); - return map; - } + private Map<Character, CommandAction<NormalState>> createCommandActions() { + final Map<Character, CommandAction<NormalState>> map = new HashMap<>(); + map.put('h', (state, context) -> { + final int repeats = context.readCount(state.count()); + for (int i = 0; i < repeats; i++) { + context.moveLeft(); + } + return HandleResult.handled(new NormalState(0, PendingOperator.NONE, state.preferredColumn())); + }); + map.put('j', (state, context) -> { + final int repeats = context.readCount(state.count()); + final int preferred = context.moveVertical(1, repeats, state.preferredColumn(), true); + return HandleResult.handled(new NormalState(0, PendingOperator.NONE, preferred)); + }); + map.put('k', (state, context) -> { + final int repeats = context.readCount(state.count()); + final int preferred = context.moveVertical(-1, repeats, state.preferredColumn(), true); + return HandleResult.handled(new NormalState(0, PendingOperator.NONE, preferred)); + }); + map.put('l', (state, context) -> { + final int repeats = context.readCount(state.count()); + for (int i = 0; i < repeats; i++) { + context.moveRightNormal(); + } + return HandleResult.handled(new NormalState(0, PendingOperator.NONE, state.preferredColumn())); + }); + map.put('w', (state, context) -> { + final int repeats = context.readCount(state.count()); + for (int i = 0; i < repeats; i++) { + context.moveWordForward(); + } + context.clampCursorInNormalMode(); + return HandleResult.handled(new NormalState(0, PendingOperator.NONE, state.preferredColumn())); + }); + map.put('b', (state, context) -> { + final int repeats = context.readCount(state.count()); + for (int i = 0; i < repeats; i++) { + context.moveWordBackward(); + } + return HandleResult.handled(new NormalState(0, PendingOperator.NONE, state.preferredColumn())); + }); + map.put('0', (state, context) -> { + context.moveLineStart(); + return HandleResult.handled(new NormalState(0, PendingOperator.NONE, state.preferredColumn())); + }); + map.put('$', (state, context) -> { + context.moveLineEnd(); + return HandleResult.handled(new NormalState(0, PendingOperator.NONE, state.preferredColumn())); + }); + map.put('G', (state, context) -> { + context.moveDocumentEnd(); + return HandleResult.handled(new NormalState(0, PendingOperator.NONE, state.preferredColumn())); + }); + map.put('g', (state, context) -> HandleResult + .handled(new NormalState(0, PendingOperator.G_PREFIX, state.preferredColumn()))); + map.put('i', (state, context) -> HandleResult.handled(new InsertState(context.text(), context.cursor()))); + map.put('a', (state, context) -> { + context.moveRightForAppend(); + return HandleResult.handled(new InsertState(context.text(), context.cursor())); + }); + map.put('o', (state, context) -> { + context.openLineBelow(); + return HandleResult.handled(new InsertState(context.text(), context.cursor())); + }); + map.put('O', (state, context) -> { + context.openLineAbove(); + return HandleResult.handled(new InsertState(context.text(), context.cursor())); + }); + map.put('x', (state, context) -> { + final int repeats = context.readCount(state.count()); + for (int i = 0; i < repeats; i++) { + context.deleteCharUnderCursor(); + } + context.clampCursorInNormalMode(); + context.recordUndoState(); + return HandleResult.handled(new NormalState(0, PendingOperator.NONE, state.preferredColumn())); + }); + map.put('d', (state, context) -> HandleResult + .handled(new NormalState(0, PendingOperator.DELETE, state.preferredColumn()))); + map.put('y', (state, context) -> HandleResult + .handled(new NormalState(0, PendingOperator.YANK, state.preferredColumn()))); + map.put('c', (state, context) -> HandleResult + .handled(new NormalState(0, PendingOperator.CHANGE, state.preferredColumn()))); + map.put('p', (state, context) -> { + final int repeats = context.readCount(state.count()); + for (int i = 0; i < repeats; i++) { + context.pasteAfter(); + } + context.clampCursorInNormalMode(); + context.recordUndoState(); + return HandleResult.handled(new NormalState(0, PendingOperator.NONE, state.preferredColumn())); + }); + map.put('P', (state, context) -> { + final int repeats = context.readCount(state.count()); + for (int i = 0; i < repeats; i++) { + context.pasteBefore(); + } + context.clampCursorInNormalMode(); + context.recordUndoState(); + return HandleResult.handled(new NormalState(0, PendingOperator.NONE, state.preferredColumn())); + }); + map.put('v', (state, context) -> { + final int anchor = context.cursor(); + context.updateVisualSelection(anchor); + return HandleResult.handled(new VisualState(anchor, state.preferredColumn(), PendingOperator.NONE, 0)); + }); + map.put('V', (state, context) -> { + final int anchorLine = context.getCursorPhysicalLine(); + context.updateVisualLineSelection(anchorLine, anchorLine); + return HandleResult.handled( + new VisualLineState(anchorLine, anchorLine, state.preferredColumn(), PendingOperator.NONE, 0)); + }); + map.put('/', (state, context) -> HandleResult + .handled(new SearchState(new StringBuilder(), context.getLastSearchQuery(), SearchDirection.FORWARD))); + map.put('?', (state, context) -> HandleResult + .handled(new SearchState(new StringBuilder(), context.getLastSearchQuery(), SearchDirection.BACKWARD))); + map.put('n', (state, context) -> { + context.findNext(context.getLastSearchQuery(), SearchDirection.FORWARD); + return HandleResult.handled(new NormalState(0, PendingOperator.NONE, state.preferredColumn())); + }); + map.put('N', (state, context) -> { + context.findNext(context.getLastSearchQuery(), SearchDirection.BACKWARD); + return HandleResult.handled(new NormalState(0, PendingOperator.NONE, state.preferredColumn())); + }); + map.put('u', (state, context) -> HandleResult.handled(context.undoToNormalState())); + return map; + } - private Map<Character, MotionAction> createMotionActions() { - final Map<Character, MotionAction> map = new HashMap<>(); - map.put('h', (context, repeats, preferredColumn, clampInNormalMode) -> { - for (int i = 0; i < repeats; i++) { - context.moveLeft(); - } - return preferredColumn; - }); - map.put('j', (context, repeats, preferredColumn, clampInNormalMode) -> context.moveVertical(1, repeats, - preferredColumn, clampInNormalMode)); - map.put('k', (context, repeats, preferredColumn, clampInNormalMode) -> context.moveVertical(-1, repeats, - preferredColumn, clampInNormalMode)); - map.put('l', (context, repeats, preferredColumn, clampInNormalMode) -> { - for (int i = 0; i < repeats; i++) { - context.moveRightNormal(); - } - return preferredColumn; - }); - map.put('w', (context, repeats, preferredColumn, clampInNormalMode) -> { - for (int i = 0; i < repeats; i++) { - context.moveWordForward(); - } - if (clampInNormalMode) { - context.clampCursorInNormalMode(); - } - return preferredColumn; - }); - map.put('b', (context, repeats, preferredColumn, clampInNormalMode) -> { - for (int i = 0; i < repeats; i++) { - context.moveWordBackward(); - } - return preferredColumn; - }); - map.put('0', (context, repeats, preferredColumn, clampInNormalMode) -> { - context.moveLineStart(); - return preferredColumn; - }); - map.put('$', (context, repeats, preferredColumn, clampInNormalMode) -> { - context.moveLineEnd(); - return preferredColumn; - }); - map.put('G', (context, repeats, preferredColumn, clampInNormalMode) -> { - context.moveDocumentEnd(); - return preferredColumn; - }); - map.put('g', (context, repeats, preferredColumn, clampInNormalMode) -> { - context.moveDocumentStart(); - return preferredColumn; - }); - return map; - } + private Map<Character, MotionAction> createMotionActions() { + final Map<Character, MotionAction> map = new HashMap<>(); + map.put('h', (context, repeats, preferredColumn, clampInNormalMode) -> { + for (int i = 0; i < repeats; i++) { + context.moveLeft(); + } + return preferredColumn; + }); + map.put('j', (context, repeats, preferredColumn, clampInNormalMode) -> context.moveVertical(1, repeats, + preferredColumn, clampInNormalMode)); + map.put('k', (context, repeats, preferredColumn, clampInNormalMode) -> context.moveVertical(-1, repeats, + preferredColumn, clampInNormalMode)); + map.put('l', (context, repeats, preferredColumn, clampInNormalMode) -> { + for (int i = 0; i < repeats; i++) { + context.moveRightNormal(); + } + return preferredColumn; + }); + map.put('w', (context, repeats, preferredColumn, clampInNormalMode) -> { + for (int i = 0; i < repeats; i++) { + context.moveWordForward(); + } + if (clampInNormalMode) { + context.clampCursorInNormalMode(); + } + return preferredColumn; + }); + map.put('b', (context, repeats, preferredColumn, clampInNormalMode) -> { + for (int i = 0; i < repeats; i++) { + context.moveWordBackward(); + } + return preferredColumn; + }); + map.put('0', (context, repeats, preferredColumn, clampInNormalMode) -> { + context.moveLineStart(); + return preferredColumn; + }); + map.put('$', (context, repeats, preferredColumn, clampInNormalMode) -> { + context.moveLineEnd(); + return preferredColumn; + }); + map.put('G', (context, repeats, preferredColumn, clampInNormalMode) -> { + context.moveDocumentEnd(); + return preferredColumn; + }); + map.put('g', (context, repeats, preferredColumn, clampInNormalMode) -> { + context.moveDocumentStart(); + return preferredColumn; + }); + return map; + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/SearchModeHandler.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/SearchModeHandler.java index c3e20f2..1c0bdd5 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/SearchModeHandler.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/SearchModeHandler.java @@ -7,36 +7,36 @@ import coffee.liz.abstractionengine.app.ui.editor.vim.state.SearchState; import com.badlogic.gdx.Input; public class SearchModeHandler implements ModeHandler<SearchState> { - @Override - public HandleResult onKeyDown(final SearchState state, final KeyContext context) { - if (context.currentKeyEvent().keycode() == Input.Keys.ESCAPE) { - return HandleResult.handled(NormalState.DEFAULT); - } - return HandleResult.handled(); - } + @Override + public HandleResult onKeyDown(final SearchState state, final KeyContext context) { + if (context.currentKeyEvent().keycode() == Input.Keys.ESCAPE) { + return HandleResult.handled(NormalState.DEFAULT); + } + return HandleResult.handled(); + } - @Override - public HandleResult onKeyTyped(final SearchState state, final char character, final KeyContext context) { - if (character == '\r' || character == '\n') { - final String query = state.query().toString(); - context.setLastSearchQuery(query); - context.invalidateSearchIterator(); - context.findNext(query, state.direction()); - return HandleResult.handled(NormalState.DEFAULT); - } + @Override + public HandleResult onKeyTyped(final SearchState state, final char character, final KeyContext context) { + if (character == '\r' || character == '\n') { + final String query = state.query().toString(); + context.setLastSearchQuery(query); + context.invalidateSearchIterator(); + context.findNext(query, state.direction()); + return HandleResult.handled(NormalState.DEFAULT); + } - if (character == '\b') { - if (state.query().length() > 0) { - state.query().setLength(state.query().length() - 1); - } - return HandleResult.handled(state); - } + if (character == '\b') { + if (state.query().length() > 0) { + state.query().setLength(state.query().length() - 1); + } + return HandleResult.handled(state); + } - if (character < 32 || character == 127) { - return HandleResult.handled(state); - } + if (character < 32 || character == 127) { + return HandleResult.handled(state); + } - state.query().append(character); - return HandleResult.handled(new SearchState(state.query(), state.lastQuery(), state.direction())); - } + state.query().append(character); + return HandleResult.handled(new SearchState(state.query(), state.lastQuery(), state.direction())); + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/VisualLineModeHandler.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/VisualLineModeHandler.java index 7056e54..58617bd 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/VisualLineModeHandler.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/VisualLineModeHandler.java @@ -9,84 +9,84 @@ import java.util.HashMap; import java.util.Map; public class VisualLineModeHandler implements ModeHandler<VisualLineState> { - private final Map<Character, CommandAction<VisualLineState>> commandActions = createCommandActions(); + private final Map<Character, CommandAction<VisualLineState>> commandActions = createCommandActions(); - @Override - public HandleResult onKeyDown(final VisualLineState state, final KeyContext context) { - if (context.currentKeyEvent().keycode() == Input.Keys.ESCAPE) { - final int startLine = Math.min(state.anchorLine(), state.cursorLine()); - context.setCursor(context.navigation().findLineStartByLine(context.text(), startLine)); - context.clampCursorInNormalMode(); - context.clearSelection(); - return HandleResult.handled(NormalState.DEFAULT); - } + @Override + public HandleResult onKeyDown(final VisualLineState state, final KeyContext context) { + if (context.currentKeyEvent().keycode() == Input.Keys.ESCAPE) { + final int startLine = Math.min(state.anchorLine(), state.cursorLine()); + context.setCursor(context.navigation().findLineStartByLine(context.text(), startLine)); + context.clampCursorInNormalMode(); + context.clearSelection(); + return HandleResult.handled(NormalState.DEFAULT); + } - final Character commandCharacter = context.toCommandCharacter(); - if (commandCharacter != null) { - return onKeyTyped(state, commandCharacter.charValue(), context).withSuppressTyped(); - } + final Character commandCharacter = context.toCommandCharacter(); + if (commandCharacter != null) { + return onKeyTyped(state, commandCharacter.charValue(), context).withSuppressTyped(); + } - return HandleResult.handled(); - } + return HandleResult.handled(); + } - @Override - public HandleResult onKeyTyped(final VisualLineState state, final char character, final KeyContext context) { - if (character >= '1' && character <= '9') { - return HandleResult.handled(new VisualLineState(state.anchorLine(), state.cursorLine(), - state.preferredColumn(), state.pendingOperator(), state.count() * 10 + (character - '0'))); - } - if (character == '0' && state.count() > 0) { - return HandleResult.handled(new VisualLineState(state.anchorLine(), state.cursorLine(), - state.preferredColumn(), state.pendingOperator(), state.count() * 10)); - } + @Override + public HandleResult onKeyTyped(final VisualLineState state, final char character, final KeyContext context) { + if (character >= '1' && character <= '9') { + return HandleResult.handled(new VisualLineState(state.anchorLine(), state.cursorLine(), + state.preferredColumn(), state.pendingOperator(), state.count() * 10 + (character - '0'))); + } + if (character == '0' && state.count() > 0) { + return HandleResult.handled(new VisualLineState(state.anchorLine(), state.cursorLine(), + state.preferredColumn(), state.pendingOperator(), state.count() * 10)); + } - if (state.pendingOperator() == PendingOperator.G_PREFIX) { - if (character == 'g') { - final int cursorLine = 0; - context.updateVisualLineSelection(state.anchorLine(), cursorLine); - return HandleResult.handled(new VisualLineState(state.anchorLine(), cursorLine, state.preferredColumn(), - PendingOperator.NONE, 0)); - } - return HandleResult.handled(new VisualLineState(state.anchorLine(), state.cursorLine(), - state.preferredColumn(), PendingOperator.NONE, 0)); - } + if (state.pendingOperator() == PendingOperator.G_PREFIX) { + if (character == 'g') { + final int cursorLine = 0; + context.updateVisualLineSelection(state.anchorLine(), cursorLine); + return HandleResult.handled(new VisualLineState(state.anchorLine(), cursorLine, state.preferredColumn(), + PendingOperator.NONE, 0)); + } + return HandleResult.handled(new VisualLineState(state.anchorLine(), state.cursorLine(), + state.preferredColumn(), PendingOperator.NONE, 0)); + } - final CommandAction<VisualLineState> action = commandActions.get(character); - if (action == null) { - return HandleResult.handled(new VisualLineState(state.anchorLine(), state.cursorLine(), - state.preferredColumn(), PendingOperator.NONE, 0)); - } - return action.execute(new VisualLineState(state.anchorLine(), state.cursorLine(), - context.resetPreferredColumn(character, state.preferredColumn()), PendingOperator.NONE, state.count()), - context); - } + final CommandAction<VisualLineState> action = commandActions.get(character); + if (action == null) { + return HandleResult.handled(new VisualLineState(state.anchorLine(), state.cursorLine(), + state.preferredColumn(), PendingOperator.NONE, 0)); + } + return action.execute(new VisualLineState(state.anchorLine(), state.cursorLine(), + context.resetPreferredColumn(character, state.preferredColumn()), PendingOperator.NONE, state.count()), + context); + } - private Map<Character, CommandAction<VisualLineState>> createCommandActions() { - final Map<Character, CommandAction<VisualLineState>> map = new HashMap<>(); - map.put('j', (state, context) -> { - final int cursorLine = context.adjustVisualLineCursor(state.cursorLine(), context.readCount(state.count())); - context.updateVisualLineSelection(state.anchorLine(), cursorLine); - return HandleResult.handled(new VisualLineState(state.anchorLine(), cursorLine, state.preferredColumn(), - PendingOperator.NONE, 0)); - }); - map.put('k', (state, context) -> { - final int cursorLine = context.adjustVisualLineCursor(state.cursorLine(), - -context.readCount(state.count())); - context.updateVisualLineSelection(state.anchorLine(), cursorLine); - return HandleResult.handled(new VisualLineState(state.anchorLine(), cursorLine, state.preferredColumn(), - PendingOperator.NONE, 0)); - }); - map.put('G', (state, context) -> { - final int cursorLine = context.maxSelectableLineIndex(); - context.updateVisualLineSelection(state.anchorLine(), cursorLine); - return HandleResult.handled(new VisualLineState(state.anchorLine(), cursorLine, state.preferredColumn(), - PendingOperator.NONE, 0)); - }); - map.put('g', (state, context) -> HandleResult.handled(new VisualLineState(state.anchorLine(), - state.cursorLine(), state.preferredColumn(), PendingOperator.G_PREFIX, 0))); - map.put('d', (state, context) -> context.applyVisualLineOperator(state, PendingOperator.DELETE)); - map.put('y', (state, context) -> context.applyVisualLineOperator(state, PendingOperator.YANK)); - map.put('c', (state, context) -> context.applyVisualLineOperator(state, PendingOperator.CHANGE)); - return map; - } + private Map<Character, CommandAction<VisualLineState>> createCommandActions() { + final Map<Character, CommandAction<VisualLineState>> map = new HashMap<>(); + map.put('j', (state, context) -> { + final int cursorLine = context.adjustVisualLineCursor(state.cursorLine(), context.readCount(state.count())); + context.updateVisualLineSelection(state.anchorLine(), cursorLine); + return HandleResult.handled(new VisualLineState(state.anchorLine(), cursorLine, state.preferredColumn(), + PendingOperator.NONE, 0)); + }); + map.put('k', (state, context) -> { + final int cursorLine = context.adjustVisualLineCursor(state.cursorLine(), + -context.readCount(state.count())); + context.updateVisualLineSelection(state.anchorLine(), cursorLine); + return HandleResult.handled(new VisualLineState(state.anchorLine(), cursorLine, state.preferredColumn(), + PendingOperator.NONE, 0)); + }); + map.put('G', (state, context) -> { + final int cursorLine = context.maxSelectableLineIndex(); + context.updateVisualLineSelection(state.anchorLine(), cursorLine); + return HandleResult.handled(new VisualLineState(state.anchorLine(), cursorLine, state.preferredColumn(), + PendingOperator.NONE, 0)); + }); + map.put('g', (state, context) -> HandleResult.handled(new VisualLineState(state.anchorLine(), + state.cursorLine(), state.preferredColumn(), PendingOperator.G_PREFIX, 0))); + map.put('d', (state, context) -> context.applyVisualLineOperator(state, PendingOperator.DELETE)); + map.put('y', (state, context) -> context.applyVisualLineOperator(state, PendingOperator.YANK)); + map.put('c', (state, context) -> context.applyVisualLineOperator(state, PendingOperator.CHANGE)); + return map; + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/VisualModeHandler.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/VisualModeHandler.java index db794d5..7fe7d6a 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/VisualModeHandler.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/VisualModeHandler.java @@ -9,124 +9,124 @@ import java.util.HashMap; import java.util.Map; public class VisualModeHandler implements ModeHandler<VisualState> { - private final Map<Character, CommandAction<VisualState>> commandActions = createCommandActions(); + private final Map<Character, CommandAction<VisualState>> commandActions = createCommandActions(); - @Override - public HandleResult onKeyDown(final VisualState state, final KeyContext context) { - if (context.currentKeyEvent().keycode() == Input.Keys.ESCAPE) { - context.setCursor(Math.min(state.anchor(), context.cursor())); - context.clampCursorInNormalMode(); - context.clearSelection(); - return HandleResult.handled(NormalState.DEFAULT); - } + @Override + public HandleResult onKeyDown(final VisualState state, final KeyContext context) { + if (context.currentKeyEvent().keycode() == Input.Keys.ESCAPE) { + context.setCursor(Math.min(state.anchor(), context.cursor())); + context.clampCursorInNormalMode(); + context.clearSelection(); + return HandleResult.handled(NormalState.DEFAULT); + } - final Character commandCharacter = context.toCommandCharacter(); - if (commandCharacter != null) { - return onKeyTyped(state, commandCharacter.charValue(), context).withSuppressTyped(); - } + final Character commandCharacter = context.toCommandCharacter(); + if (commandCharacter != null) { + return onKeyTyped(state, commandCharacter.charValue(), context).withSuppressTyped(); + } - return HandleResult.handled(); - } + return HandleResult.handled(); + } - @Override - public HandleResult onKeyTyped(final VisualState state, final char character, final KeyContext context) { - if (character >= '1' && character <= '9') { - return HandleResult.handled(new VisualState(state.anchor(), state.preferredColumn(), - state.pendingOperator(), state.count() * 10 + (character - '0'))); - } - if (character == '0' && state.count() > 0) { - return HandleResult.handled(new VisualState(state.anchor(), state.preferredColumn(), - state.pendingOperator(), state.count() * 10)); - } + @Override + public HandleResult onKeyTyped(final VisualState state, final char character, final KeyContext context) { + if (character >= '1' && character <= '9') { + return HandleResult.handled(new VisualState(state.anchor(), state.preferredColumn(), + state.pendingOperator(), state.count() * 10 + (character - '0'))); + } + if (character == '0' && state.count() > 0) { + return HandleResult.handled(new VisualState(state.anchor(), state.preferredColumn(), + state.pendingOperator(), state.count() * 10)); + } - if (state.pendingOperator() == PendingOperator.G_PREFIX) { - if (character == 'g') { - context.moveDocumentStart(); - context.updateVisualSelection(state.anchor()); - } - return HandleResult - .handled(new VisualState(state.anchor(), state.preferredColumn(), PendingOperator.NONE, 0)); - } + if (state.pendingOperator() == PendingOperator.G_PREFIX) { + if (character == 'g') { + context.moveDocumentStart(); + context.updateVisualSelection(state.anchor()); + } + return HandleResult + .handled(new VisualState(state.anchor(), state.preferredColumn(), PendingOperator.NONE, 0)); + } - final VisualState normalizedState = new VisualState(state.anchor(), - context.resetPreferredColumn(character, state.preferredColumn()), PendingOperator.NONE, state.count()); - final CommandAction<VisualState> action = commandActions.get(character); - if (action == null) { - return HandleResult.handled( - new VisualState(state.anchor(), normalizedState.preferredColumn(), PendingOperator.NONE, 0)); - } - return action.execute(normalizedState, context); - } + final VisualState normalizedState = new VisualState(state.anchor(), + context.resetPreferredColumn(character, state.preferredColumn()), PendingOperator.NONE, state.count()); + final CommandAction<VisualState> action = commandActions.get(character); + if (action == null) { + return HandleResult.handled( + new VisualState(state.anchor(), normalizedState.preferredColumn(), PendingOperator.NONE, 0)); + } + return action.execute(normalizedState, context); + } - private Map<Character, CommandAction<VisualState>> createCommandActions() { - final Map<Character, CommandAction<VisualState>> map = new HashMap<>(); - map.put('h', (state, context) -> { - for (int i = 0; i < context.readCount(state.count()); i++) { - context.moveLeft(); - } - context.updateVisualSelection(state.anchor()); - return HandleResult - .handled(new VisualState(state.anchor(), state.preferredColumn(), PendingOperator.NONE, 0)); - }); - map.put('j', (state, context) -> { - final int preferred = context.moveVertical(1, context.readCount(state.count()), state.preferredColumn(), - false); - context.updateVisualSelection(state.anchor()); - return HandleResult.handled(new VisualState(state.anchor(), preferred, PendingOperator.NONE, 0)); - }); - map.put('k', (state, context) -> { - final int preferred = context.moveVertical(-1, context.readCount(state.count()), state.preferredColumn(), - false); - context.updateVisualSelection(state.anchor()); - return HandleResult.handled(new VisualState(state.anchor(), preferred, PendingOperator.NONE, 0)); - }); - map.put('l', (state, context) -> { - for (int i = 0; i < context.readCount(state.count()); i++) { - context.moveRightNormal(); - } - context.updateVisualSelection(state.anchor()); - return HandleResult - .handled(new VisualState(state.anchor(), state.preferredColumn(), PendingOperator.NONE, 0)); - }); - map.put('w', (state, context) -> { - for (int i = 0; i < context.readCount(state.count()); i++) { - context.moveWordForward(); - } - context.updateVisualSelection(state.anchor()); - return HandleResult - .handled(new VisualState(state.anchor(), state.preferredColumn(), PendingOperator.NONE, 0)); - }); - map.put('b', (state, context) -> { - for (int i = 0; i < context.readCount(state.count()); i++) { - context.moveWordBackward(); - } - context.updateVisualSelection(state.anchor()); - return HandleResult - .handled(new VisualState(state.anchor(), state.preferredColumn(), PendingOperator.NONE, 0)); - }); - map.put('0', (state, context) -> { - context.moveLineStart(); - context.updateVisualSelection(state.anchor()); - return HandleResult - .handled(new VisualState(state.anchor(), state.preferredColumn(), PendingOperator.NONE, 0)); - }); - map.put('$', (state, context) -> { - context.moveLineEnd(); - context.updateVisualSelection(state.anchor()); - return HandleResult - .handled(new VisualState(state.anchor(), state.preferredColumn(), PendingOperator.NONE, 0)); - }); - map.put('G', (state, context) -> { - context.moveDocumentEnd(); - context.updateVisualSelection(state.anchor()); - return HandleResult - .handled(new VisualState(state.anchor(), state.preferredColumn(), PendingOperator.NONE, 0)); - }); - map.put('g', (state, context) -> HandleResult - .handled(new VisualState(state.anchor(), state.preferredColumn(), PendingOperator.G_PREFIX, 0))); - map.put('d', (state, context) -> context.applyVisualOperator(state, PendingOperator.DELETE)); - map.put('y', (state, context) -> context.applyVisualOperator(state, PendingOperator.YANK)); - map.put('c', (state, context) -> context.applyVisualOperator(state, PendingOperator.CHANGE)); - return map; - } + private Map<Character, CommandAction<VisualState>> createCommandActions() { + final Map<Character, CommandAction<VisualState>> map = new HashMap<>(); + map.put('h', (state, context) -> { + for (int i = 0; i < context.readCount(state.count()); i++) { + context.moveLeft(); + } + context.updateVisualSelection(state.anchor()); + return HandleResult + .handled(new VisualState(state.anchor(), state.preferredColumn(), PendingOperator.NONE, 0)); + }); + map.put('j', (state, context) -> { + final int preferred = context.moveVertical(1, context.readCount(state.count()), state.preferredColumn(), + false); + context.updateVisualSelection(state.anchor()); + return HandleResult.handled(new VisualState(state.anchor(), preferred, PendingOperator.NONE, 0)); + }); + map.put('k', (state, context) -> { + final int preferred = context.moveVertical(-1, context.readCount(state.count()), state.preferredColumn(), + false); + context.updateVisualSelection(state.anchor()); + return HandleResult.handled(new VisualState(state.anchor(), preferred, PendingOperator.NONE, 0)); + }); + map.put('l', (state, context) -> { + for (int i = 0; i < context.readCount(state.count()); i++) { + context.moveRightNormal(); + } + context.updateVisualSelection(state.anchor()); + return HandleResult + .handled(new VisualState(state.anchor(), state.preferredColumn(), PendingOperator.NONE, 0)); + }); + map.put('w', (state, context) -> { + for (int i = 0; i < context.readCount(state.count()); i++) { + context.moveWordForward(); + } + context.updateVisualSelection(state.anchor()); + return HandleResult + .handled(new VisualState(state.anchor(), state.preferredColumn(), PendingOperator.NONE, 0)); + }); + map.put('b', (state, context) -> { + for (int i = 0; i < context.readCount(state.count()); i++) { + context.moveWordBackward(); + } + context.updateVisualSelection(state.anchor()); + return HandleResult + .handled(new VisualState(state.anchor(), state.preferredColumn(), PendingOperator.NONE, 0)); + }); + map.put('0', (state, context) -> { + context.moveLineStart(); + context.updateVisualSelection(state.anchor()); + return HandleResult + .handled(new VisualState(state.anchor(), state.preferredColumn(), PendingOperator.NONE, 0)); + }); + map.put('$', (state, context) -> { + context.moveLineEnd(); + context.updateVisualSelection(state.anchor()); + return HandleResult + .handled(new VisualState(state.anchor(), state.preferredColumn(), PendingOperator.NONE, 0)); + }); + map.put('G', (state, context) -> { + context.moveDocumentEnd(); + context.updateVisualSelection(state.anchor()); + return HandleResult + .handled(new VisualState(state.anchor(), state.preferredColumn(), PendingOperator.NONE, 0)); + }); + map.put('g', (state, context) -> HandleResult + .handled(new VisualState(state.anchor(), state.preferredColumn(), PendingOperator.G_PREFIX, 0))); + map.put('d', (state, context) -> context.applyVisualOperator(state, PendingOperator.DELETE)); + map.put('y', (state, context) -> context.applyVisualOperator(state, PendingOperator.YANK)); + map.put('c', (state, context) -> context.applyVisualOperator(state, PendingOperator.CHANGE)); + return map; + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/model/Register.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/model/Register.java index 9bacd59..44a3c4c 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/model/Register.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/model/Register.java @@ -6,6 +6,6 @@ import lombok.Setter; @Getter @Setter public class Register { - private String text = ""; - private RegisterType type = RegisterType.CHARACTER; + private String text = ""; + private RegisterType type = RegisterType.CHARACTER; } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/model/RegisterType.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/model/RegisterType.java index 9ecb900..71df9eb 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/model/RegisterType.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/model/RegisterType.java @@ -1,5 +1,5 @@ package coffee.liz.abstractionengine.app.ui.editor.vim.model; public enum RegisterType { - CHARACTER, LINE + CHARACTER, LINE } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/model/UndoManager.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/model/UndoManager.java index 34b2122..affb1a8 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/model/UndoManager.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/model/UndoManager.java @@ -6,47 +6,47 @@ import lombok.RequiredArgsConstructor; @RequiredArgsConstructor public class UndoManager { - private final int maxUndo; - private final Deque<UndoState> undoStack = new ArrayDeque<>(); - private final Deque<UndoState> redoStack = new ArrayDeque<>(); + private final int maxUndo; + private final Deque<UndoState> undoStack = new ArrayDeque<>(); + private final Deque<UndoState> redoStack = new ArrayDeque<>(); - public void record(final String text, final int cursor) { - final UndoState candidate = new UndoState(text, cursor); - final UndoState top = undoStack.peekLast(); - if (top != null && top.getCursor() == candidate.getCursor() && top.getText().equals(candidate.getText())) { - return; - } + public void record(final String text, final int cursor) { + final UndoState candidate = new UndoState(text, cursor); + final UndoState top = undoStack.peekLast(); + if (top != null && top.getCursor() == candidate.getCursor() && top.getText().equals(candidate.getText())) { + return; + } - undoStack.addLast(candidate); - while (undoStack.size() > maxUndo) { - undoStack.removeFirst(); - } - redoStack.clear(); - } + undoStack.addLast(candidate); + while (undoStack.size() > maxUndo) { + undoStack.removeFirst(); + } + redoStack.clear(); + } - public UndoState undo() { - if (undoStack.size() <= 1) { - return null; - } + public UndoState undo() { + if (undoStack.size() <= 1) { + return null; + } - final UndoState current = undoStack.removeLast(); - redoStack.addLast(current); - return undoStack.peekLast(); - } + final UndoState current = undoStack.removeLast(); + redoStack.addLast(current); + return undoStack.peekLast(); + } - public UndoState redo() { - if (redoStack.isEmpty()) { - return null; - } + public UndoState redo() { + if (redoStack.isEmpty()) { + return null; + } - final UndoState target = redoStack.removeLast(); - final UndoState top = undoStack.peekLast(); - if (top == null || !top.getText().equals(target.getText()) || top.getCursor() != target.getCursor()) { - undoStack.addLast(target); - while (undoStack.size() > maxUndo) { - undoStack.removeFirst(); - } - } - return target; - } + final UndoState target = redoStack.removeLast(); + final UndoState top = undoStack.peekLast(); + if (top == null || !top.getText().equals(target.getText()) || top.getCursor() != target.getCursor()) { + undoStack.addLast(target); + while (undoStack.size() > maxUndo) { + undoStack.removeFirst(); + } + } + return target; + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/model/UndoState.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/model/UndoState.java index c91ebcd..04375aa 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/model/UndoState.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/model/UndoState.java @@ -6,6 +6,6 @@ import lombok.RequiredArgsConstructor; @Getter @RequiredArgsConstructor public class UndoState { - private final String text; - private final int cursor; + private final String text; + private final int cursor; } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/navigation/TextNavigation.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/navigation/TextNavigation.java index c9ef857..b74c1df 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/navigation/TextNavigation.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/navigation/TextNavigation.java @@ -1,110 +1,110 @@ package coffee.liz.abstractionengine.app.ui.editor.vim.navigation; public class TextNavigation { - public int findLineStart(final String text, final int cursor) { - int index = Math.max(0, Math.min(cursor, text.length())); - while (index > 0 && text.charAt(index - 1) != '\n') { - index--; - } - return index; - } + public int findLineStart(final String text, final int cursor) { + int index = Math.max(0, Math.min(cursor, text.length())); + while (index > 0 && text.charAt(index - 1) != '\n') { + index--; + } + return index; + } - public int findLineEnd(final String text, final int cursor) { - final int lineStart = findLineStart(text, cursor); - int index = lineStart; - while (index < text.length() && text.charAt(index) != '\n') { - index++; - } - if (index == lineStart) { - return lineStart; - } - return index - 1; - } + public int findLineEnd(final String text, final int cursor) { + final int lineStart = findLineStart(text, cursor); + int index = lineStart; + while (index < text.length() && text.charAt(index) != '\n') { + index++; + } + if (index == lineStart) { + return lineStart; + } + return index - 1; + } - public int findLineEndExclusiveByStart(final String text, final int lineStart) { - int index = lineStart; - while (index < text.length() && text.charAt(index) != '\n') { - index++; - } - return index; - } + public int findLineEndExclusiveByStart(final String text, final int lineStart) { + int index = lineStart; + while (index < text.length() && text.charAt(index) != '\n') { + index++; + } + return index; + } - public int findNextLineStart(final String text, final int start) { - final int newline = text.indexOf('\n', Math.max(0, start)); - if (newline < 0) { - return -1; - } - return newline + 1; - } + public int findNextLineStart(final String text, final int start) { + final int newline = text.indexOf('\n', Math.max(0, start)); + if (newline < 0) { + return -1; + } + return newline + 1; + } - public int findPreviousLineStart(final String text, final int lineStart) { - final int clampedLineStart = Math.max(0, Math.min(lineStart, text.length())); - if (clampedLineStart <= 0) { - return -1; - } - final int previousNewline = text.lastIndexOf('\n', clampedLineStart - 2); - return previousNewline + 1; - } + public int findPreviousLineStart(final String text, final int lineStart) { + final int clampedLineStart = Math.max(0, Math.min(lineStart, text.length())); + if (clampedLineStart <= 0) { + return -1; + } + final int previousNewline = text.lastIndexOf('\n', clampedLineStart - 2); + return previousNewline + 1; + } - public int findBelowLineInsertionIndex(final String text, final int cursor) { - final int lineStart = findLineStart(text, cursor); - final int lineEnd = findLineEnd(text, cursor); - final boolean currentLineIsEmpty = findLineEndExclusiveByStart(text, lineStart) == lineStart; - int insertAt = Math.min(lineEnd + 1, text.length()); - if (!currentLineIsEmpty && insertAt < text.length() && text.charAt(insertAt) == '\n') { - insertAt++; - } - return insertAt; - } + public int findBelowLineInsertionIndex(final String text, final int cursor) { + final int lineStart = findLineStart(text, cursor); + final int lineEnd = findLineEnd(text, cursor); + final boolean currentLineIsEmpty = findLineEndExclusiveByStart(text, lineStart) == lineStart; + int insertAt = Math.min(lineEnd + 1, text.length()); + if (!currentLineIsEmpty && insertAt < text.length() && text.charAt(insertAt) == '\n') { + insertAt++; + } + return insertAt; + } - public int findLineStartByLine(final String text, final int lineNumber) { - int line = 0; - int index = 0; - while (line < lineNumber && index < text.length()) { - if (text.charAt(index) == '\n') { - line++; - } - index++; - } - return index; - } + public int findLineStartByLine(final String text, final int lineNumber) { + int line = 0; + int index = 0; + while (line < lineNumber && index < text.length()) { + if (text.charAt(index) == '\n') { + line++; + } + index++; + } + return index; + } - public int findLineSelectionEndByLine(final String text, final int lineNumber) { - int index = findLineStartByLine(text, lineNumber); - while (index < text.length() && text.charAt(index) != '\n') { - index++; - } - if (index < text.length() && text.charAt(index) == '\n') { - return index + 1; - } - return text.length(); - } + public int findLineSelectionEndByLine(final String text, final int lineNumber) { + int index = findLineStartByLine(text, lineNumber); + while (index < text.length() && text.charAt(index) != '\n') { + index++; + } + if (index < text.length() && text.charAt(index) == '\n') { + return index + 1; + } + return text.length(); + } - public int lastNavigablePosition(final String text) { - if (text.isEmpty()) { - return 0; - } + public int lastNavigablePosition(final String text) { + if (text.isEmpty()) { + return 0; + } - return text.length() - 1; - } + return text.length() - 1; + } - public int maxCursorPositionInNormalMode(final String text) { - if (text.isEmpty()) { - return 0; - } - return text.length() - 1; - } + public int maxCursorPositionInNormalMode(final String text) { + if (text.isEmpty()) { + return 0; + } + return text.length() - 1; + } - public boolean isWordCharacter(final char character) { - return Character.isLetterOrDigit(character) || character == '_'; - } + public boolean isWordCharacter(final char character) { + return Character.isLetterOrDigit(character) || character == '_'; + } - public boolean isInlineWhitespace(final char character) { - return Character.isWhitespace(character) && character != '\n' && character != '\r'; - } + public boolean isInlineWhitespace(final char character) { + return Character.isWhitespace(character) && character != '\n' && character != '\r'; + } - public boolean isPunctuationWord(final char character) { - return !isWordCharacter(character) && !Character.isWhitespace(character) && character != '\n' - && character != '\r'; - } + public boolean isPunctuationWord(final char character) { + return !isWordCharacter(character) && !Character.isWhitespace(character) && character != '\n' + && character != '\r'; + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/search/SearchDirection.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/search/SearchDirection.java index ca3d63e..b157d16 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/search/SearchDirection.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/search/SearchDirection.java @@ -1,5 +1,5 @@ package coffee.liz.abstractionengine.app.ui.editor.vim.search; public enum SearchDirection { - FORWARD, BACKWARD + FORWARD, BACKWARD } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/search/SearchIterator.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/search/SearchIterator.java index 50f0e1c..a5e078b 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/search/SearchIterator.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/search/SearchIterator.java @@ -3,56 +3,56 @@ package coffee.liz.abstractionengine.app.ui.editor.vim.search; import java.util.OptionalInt; public class SearchIterator { - private final String text; - private final String query; - private final SearchDirection direction; - private final boolean wrap; - private int nextStart; - private boolean wrapped; + private final String text; + private final String query; + private final SearchDirection direction; + private final boolean wrap; + private int nextStart; + private boolean wrapped; - public SearchIterator(final String text, final String query, final int startIndex, final SearchDirection direction, - final boolean wrap) { - this.text = text == null ? "" : text; - this.query = query == null ? "" : query; - this.direction = direction; - this.wrap = wrap; - if (direction == SearchDirection.FORWARD) { - this.nextStart = Math.min(this.text.length(), Math.max(0, startIndex + 1)); - } else { - this.nextStart = Math.max(0, startIndex - 1); - } - this.wrapped = false; - } + public SearchIterator(final String text, final String query, final int startIndex, final SearchDirection direction, + final boolean wrap) { + this.text = text == null ? "" : text; + this.query = query == null ? "" : query; + this.direction = direction; + this.wrap = wrap; + if (direction == SearchDirection.FORWARD) { + this.nextStart = Math.min(this.text.length(), Math.max(0, startIndex + 1)); + } else { + this.nextStart = Math.max(0, startIndex - 1); + } + this.wrapped = false; + } - public OptionalInt nextMatch() { - if (query.isEmpty() || text.isEmpty()) { - return OptionalInt.empty(); - } + public OptionalInt nextMatch() { + if (query.isEmpty() || text.isEmpty()) { + return OptionalInt.empty(); + } - if (direction == SearchDirection.FORWARD) { - final int found = text.indexOf(query, nextStart); - if (found >= 0) { - nextStart = Math.min(text.length(), found + 1); - return OptionalInt.of(found); - } - if (wrap && !wrapped) { - wrapped = true; - nextStart = 0; - return nextMatch(); - } - return OptionalInt.empty(); - } + if (direction == SearchDirection.FORWARD) { + final int found = text.indexOf(query, nextStart); + if (found >= 0) { + nextStart = Math.min(text.length(), found + 1); + return OptionalInt.of(found); + } + if (wrap && !wrapped) { + wrapped = true; + nextStart = 0; + return nextMatch(); + } + return OptionalInt.empty(); + } - final int found = text.lastIndexOf(query, nextStart); - if (found >= 0) { - nextStart = Math.max(0, found - 1); - return OptionalInt.of(found); - } - if (wrap && !wrapped) { - wrapped = true; - nextStart = Math.max(0, text.length() - 1); - return nextMatch(); - } - return OptionalInt.empty(); - } + final int found = text.lastIndexOf(query, nextStart); + if (found >= 0) { + nextStart = Math.max(0, found - 1); + return OptionalInt.of(found); + } + if (wrap && !wrapped) { + wrapped = true; + nextStart = Math.max(0, text.length() - 1); + return nextMatch(); + } + return OptionalInt.empty(); + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/state/ModeState.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/state/ModeState.java index 78d2fb8..e878dd7 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/state/ModeState.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/state/ModeState.java @@ -1,15 +1,15 @@ package coffee.liz.abstractionengine.app.ui.editor.vim.state; public sealed interface ModeState permits InsertState, NormalState, SearchState, VisualLineState, VisualState { - default PendingOperator pendingOperator() { - return PendingOperator.NONE; - } + default PendingOperator pendingOperator() { + return PendingOperator.NONE; + } - default int count() { - return 0; - } + default int count() { + return 0; + } - default int preferredColumn() { - return -1; - } + default int preferredColumn() { + return -1; + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/state/NormalState.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/state/NormalState.java index 42b1d99..d86e842 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/state/NormalState.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/state/NormalState.java @@ -1,5 +1,5 @@ package coffee.liz.abstractionengine.app.ui.editor.vim.state; public record NormalState(int count, PendingOperator pendingOperator, int preferredColumn) implements ModeState { - public static final NormalState DEFAULT = new NormalState(0, PendingOperator.NONE, -1); + public static final NormalState DEFAULT = new NormalState(0, PendingOperator.NONE, -1); } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/state/PendingOperator.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/state/PendingOperator.java index 1fc4425..60f22f0 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/state/PendingOperator.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/state/PendingOperator.java @@ -1,5 +1,5 @@ package coffee.liz.abstractionengine.app.ui.editor.vim.state; public enum PendingOperator { - NONE, DELETE, YANK, CHANGE, G_PREFIX + NONE, DELETE, YANK, CHANGE, G_PREFIX } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/state/SearchState.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/state/SearchState.java index 7de6811..fb2ba40 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/state/SearchState.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/state/SearchState.java @@ -3,9 +3,9 @@ package coffee.liz.abstractionengine.app.ui.editor.vim.state; import coffee.liz.abstractionengine.app.ui.editor.vim.search.SearchDirection; public record SearchState(StringBuilder query, String lastQuery, SearchDirection direction) implements ModeState { - public SearchState { - query = query == null ? new StringBuilder() : query; - lastQuery = lastQuery == null ? "" : lastQuery; - direction = direction == null ? SearchDirection.FORWARD : direction; - } + public SearchState { + query = query == null ? new StringBuilder() : query; + lastQuery = lastQuery == null ? "" : lastQuery; + direction = direction == null ? SearchDirection.FORWARD : direction; + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/state/VisualLineState.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/state/VisualLineState.java index b486223..19c74b7 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/state/VisualLineState.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/state/VisualLineState.java @@ -1,5 +1,5 @@ package coffee.liz.abstractionengine.app.ui.editor.vim.state; public record VisualLineState(int anchorLine, int cursorLine, int preferredColumn, PendingOperator pendingOperator, - int count) implements ModeState { + int count) implements ModeState { } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/state/VisualState.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/state/VisualState.java index 97604c8..373028c 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/state/VisualState.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/state/VisualState.java @@ -1,5 +1,5 @@ package coffee.liz.abstractionengine.app.ui.editor.vim.state; public record VisualState(int anchor, int preferredColumn, PendingOperator pendingOperator, - int count) implements ModeState { + int count) implements ModeState { } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/status/StatusFormatter.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/status/StatusFormatter.java index 4ef8c17..ab5321f 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/status/StatusFormatter.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/status/StatusFormatter.java @@ -10,47 +10,47 @@ import coffee.liz.abstractionengine.app.ui.editor.vim.state.VisualLineState; import coffee.liz.abstractionengine.app.ui.editor.vim.state.VisualState; public class StatusFormatter { - public String format(final ModeState state, final VimConfig config) { - return switch (state) { - case NormalState normalState -> formatNormal(normalState, config); - case InsertState ignored -> config.insertStatusLabel(); - case VisualState ignored -> config.visualStatusLabel(); - case VisualLineState ignored -> config.visualLineStatusLabel(); - case SearchState searchState -> '/' + searchState.query().toString(); - }; - } + public String format(final ModeState state, final VimConfig config) { + return switch (state) { + case NormalState normalState -> formatNormal(normalState, config); + case InsertState ignored -> config.insertStatusLabel(); + case VisualState ignored -> config.visualStatusLabel(); + case VisualLineState ignored -> config.visualLineStatusLabel(); + case SearchState searchState -> '/' + searchState.query().toString(); + }; + } - private String formatNormal(final NormalState state, final VimConfig config) { - final StringBuilder builder = new StringBuilder(config.normalStatusLabel()); - appendOperator(builder, state.pendingOperator()); - if (state.count() > 0) { - builder.append(" count=").append(state.count()); - } - return builder.toString(); - } + private String formatNormal(final NormalState state, final VimConfig config) { + final StringBuilder builder = new StringBuilder(config.normalStatusLabel()); + appendOperator(builder, state.pendingOperator()); + if (state.count() > 0) { + builder.append(" count=").append(state.count()); + } + return builder.toString(); + } - private void appendOperator(final StringBuilder builder, final PendingOperator pendingOperator) { - if (pendingOperator == PendingOperator.NONE) { - return; - } + private void appendOperator(final StringBuilder builder, final PendingOperator pendingOperator) { + if (pendingOperator == PendingOperator.NONE) { + return; + } - builder.append(" ["); - switch (pendingOperator) { - case DELETE : - builder.append('d'); - break; - case YANK : - builder.append('y'); - break; - case CHANGE : - builder.append('c'); - break; - case G_PREFIX : - builder.append('g'); - break; - default : - break; - } - builder.append(']'); - } + builder.append(" ["); + switch (pendingOperator) { + case DELETE : + builder.append('d'); + break; + case YANK : + builder.append('y'); + break; + case CHANGE : + builder.append('c'); + break; + case G_PREFIX : + builder.append('g'); + break; + default : + break; + } + builder.append(']'); + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/utils/FontHelper.java b/core/src/main/java/coffee/liz/abstractionengine/app/utils/FontHelper.java index b38f25c..8db56a7 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/utils/FontHelper.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/utils/FontHelper.java @@ -7,26 +7,26 @@ import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator; public final class FontHelper { - private FontHelper() { - } + private FontHelper() { + } - public static final String RENDERABLE_CHARS = "λ" + FreeTypeFontGenerator.DEFAULT_CHARS; - public static final String MONO = "fonts/JetBrainsMonoNerdFont-Regular.ttf"; + public static final String RENDERABLE_CHARS = "λ" + FreeTypeFontGenerator.DEFAULT_CHARS; + public static final String MONO = "fonts/JetBrainsMonoNerdFont-Regular.ttf"; - public static BitmapFont initFont(final int size, final String path, final float scale) { - final int scaleForHidpi = 4; - final FreeTypeFontGenerator gen = new FreeTypeFontGenerator(Gdx.files.internal(path)); - final FreeTypeFontGenerator.FreeTypeFontParameter params = new FreeTypeFontGenerator.FreeTypeFontParameter(); - params.characters = RENDERABLE_CHARS; - params.size = scaleForHidpi * size; + public static BitmapFont initFont(final int size, final String path, final float scale) { + final int scaleForHidpi = 4; + final FreeTypeFontGenerator gen = new FreeTypeFontGenerator(Gdx.files.internal(path)); + final FreeTypeFontGenerator.FreeTypeFontParameter params = new FreeTypeFontGenerator.FreeTypeFontParameter(); + params.characters = RENDERABLE_CHARS; + params.size = scaleForHidpi * size; - final BitmapFont font = gen.generateFont(params); - font.setFixedWidthGlyphs(RENDERABLE_CHARS); - font.setUseIntegerPositions(false); - font.getRegion().getTexture().setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear); - font.getData().setScale((1f / scaleForHidpi) * scale); + final BitmapFont font = gen.generateFont(params); + font.setFixedWidthGlyphs(RENDERABLE_CHARS); + font.setUseIntegerPositions(false); + font.getRegion().getTexture().setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear); + font.getData().setScale((1f / scaleForHidpi) * scale); - gen.dispose(); - return font; - } + gen.dispose(); + return font; + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/utils/FunctionUtils.java b/core/src/main/java/coffee/liz/abstractionengine/app/utils/FunctionUtils.java index c219df0..c985e48 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/utils/FunctionUtils.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/utils/FunctionUtils.java @@ -1,28 +1,28 @@ package coffee.liz.abstractionengine.app.utils; public final class FunctionUtils { - private FunctionUtils() { - } + private FunctionUtils() { + } - /** - * Runs the provided action and wraps failures into a {@link RuntimeException}. - * - * @param run - * action to execute - */ - public static <E extends Throwable> void wrapCheckedException(final ThrowableRunnable<E> run) { - try { - run.run(); - } catch (final Throwable e) { - throw new RuntimeException(e); - } - } + /** + * Runs the provided action and wraps failures into a {@link RuntimeException}. + * + * @param run + * action to execute + */ + public static <E extends Throwable> void wrapCheckedException(final ThrowableRunnable<E> run) { + try { + run.run(); + } catch (final Throwable e) { + throw new RuntimeException(e); + } + } - @FunctionalInterface - public interface ThrowableRunnable<E extends Throwable> { - /** - * Run. - */ - void run() throws E; - } + @FunctionalInterface + public interface ThrowableRunnable<E extends Throwable> { + /** + * Run. + */ + void run() throws E; + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/utils/TimerUtils.java b/core/src/main/java/coffee/liz/abstractionengine/app/utils/TimerUtils.java index dea7bd2..77f074b 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/utils/TimerUtils.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/utils/TimerUtils.java @@ -3,22 +3,22 @@ package coffee.liz.abstractionengine.app.utils; import com.badlogic.gdx.utils.Timer; public final class TimerUtils { - private TimerUtils() { - } + private TimerUtils() { + } - /** - * Creates a {@link Timer.Task} from a runnable. - * - * @param r - * action to run - * @return timer task that runs the action - */ - public static Timer.Task sideEffectTask(final Runnable r) { - return new Timer.Task() { - @Override - public void run() { - r.run(); - } - }; - } + /** + * Creates a {@link Timer.Task} from a runnable. + * + * @param r + * action to run + * @return timer task that runs the action + */ + public static Timer.Task sideEffectTask(final Runnable r) { + return new Timer.Task() { + @Override + public void run() { + r.run(); + } + }; + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/entity/EntityFactory.java b/core/src/main/java/coffee/liz/abstractionengine/entity/EntityFactory.java index 99a6eb7..d8299aa 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/entity/EntityFactory.java +++ b/core/src/main/java/coffee/liz/abstractionengine/entity/EntityFactory.java @@ -11,70 +11,70 @@ import java.util.List; import java.util.function.BiFunction; public final class EntityFactory { - private EntityFactory() { - } + private EntityFactory() { + } - /** - * {@link EntityType#PLAYER} factory. - */ - public final class Player { - private Player() { - } + /** + * {@link EntityType#PLAYER} factory. + */ + public final class Player { + private Player() { + } - public static Entity addToWorld(final World<?> world, final Vec2<Integer> position) { - return world.createEntity().add(new GridPosition(position)).add(new GridControllable()) - .add(EntityType.PLAYER).add(new EntityTypeGridCollidable()); - } - } + public static Entity addToWorld(final World<?> world, final Vec2<Integer> position) { + return world.createEntity().add(new GridPosition(position)).add(new GridControllable()) + .add(EntityType.PLAYER).add(new EntityTypeGridCollidable()); + } + } - /** - * {@link EntityType#WALL} factory. - */ - public final class Wall { - private Wall() { - } + /** + * {@link EntityType#WALL} factory. + */ + public final class Wall { + private Wall() { + } - public static Entity addToWorld(final World<?> world, final Vec2<Integer> position) { - return world.createEntity().add(new GridPosition(position)).add(EntityType.WALL) - .add(new EntityTypeGridCollidable()); - } - } + public static Entity addToWorld(final World<?> world, final Vec2<Integer> position) { + return world.createEntity().add(new GridPosition(position)).add(EntityType.WALL) + .add(new EntityTypeGridCollidable()); + } + } - /** - * {@link EntityType#ABSTRACTION} factory. - */ - public final class Abstraction { - private Abstraction() { - } + /** + * {@link EntityType#ABSTRACTION} factory. + */ + public final class Abstraction { + private Abstraction() { + } - public static Entity addToWorld(final World<?> world, final Vec2<Integer> position) { - return world.createEntity().add(new GridPosition(position)).add(EntityType.ABSTRACTION) - .add(new EntityTypeGridCollidable()); - } - } + public static Entity addToWorld(final World<?> world, final Vec2<Integer> position) { + return world.createEntity().add(new GridPosition(position)).add(EntityType.ABSTRACTION) + .add(new EntityTypeGridCollidable()); + } + } - /** - * Populates entities on the border of a rectangle of defined dimensions. - * - * @param dimensions - * are the world dimensions. - * @param world - * is the world to populate - * @param factory - * accepts the world and position in the world and creates the - * entity. - * @return 2d matrix of entities at each position in the world of populated - * entities. - */ - public static List<List<Entity>> populateRect(final Vec2<Integer> dimensions, final World<?> world, - final BiFunction<World<?>, Vec2<Integer>, Entity> factory) { - return Mat2.init(dimensions, pos -> { - final boolean isXBorder = pos.getX() == 0 || pos.getX() == dimensions.getX() - 1; - final boolean isYBorder = pos.getY() == 0 || pos.getY() == dimensions.getY() - 1; - if (isXBorder || isYBorder) { - return factory.apply(world, pos); - } - return null; - }); - } + /** + * Populates entities on the border of a rectangle of defined dimensions. + * + * @param dimensions + * are the world dimensions. + * @param world + * is the world to populate + * @param factory + * accepts the world and position in the world and creates the + * entity. + * @return 2d matrix of entities at each position in the world of populated + * entities. + */ + public static List<List<Entity>> populateRect(final Vec2<Integer> dimensions, final World<?> world, + final BiFunction<World<?>, Vec2<Integer>, Entity> factory) { + return Mat2.init(dimensions, pos -> { + final boolean isXBorder = pos.getX() == 0 || pos.getX() == dimensions.getX() - 1; + final boolean isYBorder = pos.getY() == 0 || pos.getY() == dimensions.getY() - 1; + if (isXBorder || isYBorder) { + return factory.apply(world, pos); + } + return null; + }); + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/entity/EntityType.java b/core/src/main/java/coffee/liz/abstractionengine/entity/EntityType.java index e0bf243..93fa160 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/entity/EntityType.java +++ b/core/src/main/java/coffee/liz/abstractionengine/entity/EntityType.java @@ -9,44 +9,44 @@ import coffee.liz.ecs.model.Component; * {@link coffee.liz.abstractionengine.AbstractionEngineGridWorld} */ public enum EntityType implements Component { - /** Player entity */ - PLAYER, - /** Wall entity. Nothing goes through this */ - WALL, - /** Lambda term producer */ - PRODUCER, - /** Lambda abstraction */ - ABSTRACTION, - /** Lambda application */ - APPLICATION, - /** Lava */ - LAVA, - /** Bridge */ - BRIDGE; + /** Player entity */ + PLAYER, + /** Wall entity. Nothing goes through this */ + WALL, + /** Lambda term producer */ + PRODUCER, + /** Lambda abstraction */ + ABSTRACTION, + /** Lambda application */ + APPLICATION, + /** Lava */ + LAVA, + /** Bridge */ + BRIDGE; - /** - * Gets the collision behavior to apply based on the colliding types. - * - * @param that - * is the {@link EntityType} colliding. - * @return collision behavior to resolve. - */ - public CollisionBehavior collideWith(final EntityType that) { - if (that.equals(WALL)) { - return CollisionBehavior.builder().collisionBehaviorType(CollisionBehaviorType.WALL).priority(0).build(); - } - if (this.equals(ABSTRACTION) && that.equals(APPLICATION)) { - return CollisionBehavior.builder().collisionBehaviorType(CollisionBehaviorType.SWALLOW).priority(0).build(); - } + /** + * Gets the collision behavior to apply based on the colliding types. + * + * @param that + * is the {@link EntityType} colliding. + * @return collision behavior to resolve. + */ + public CollisionBehavior collideWith(final EntityType that) { + if (that.equals(WALL)) { + return CollisionBehavior.builder().collisionBehaviorType(CollisionBehaviorType.WALL).priority(0).build(); + } + if (this.equals(ABSTRACTION) && that.equals(APPLICATION)) { + return CollisionBehavior.builder().collisionBehaviorType(CollisionBehaviorType.SWALLOW).priority(0).build(); + } - if (that.equals(LAVA)) { - return CollisionBehavior.builder().collisionBehaviorType(CollisionBehaviorType.SWALLOW).priority(1).build(); - } - if (that.equals(BRIDGE)) { // BRIDGE takes precedence over LAVA, so stuff can walk on a bridge over - // LAVA - return CollisionBehavior.builder().collisionBehaviorType(CollisionBehaviorType.SWALLOW).priority(0).build(); - } + if (that.equals(LAVA)) { + return CollisionBehavior.builder().collisionBehaviorType(CollisionBehaviorType.SWALLOW).priority(1).build(); + } + if (that.equals(BRIDGE)) { // BRIDGE takes precedence over LAVA, so stuff can walk on a bridge over + // LAVA + return CollisionBehavior.builder().collisionBehaviorType(CollisionBehaviorType.SWALLOW).priority(0).build(); + } - return CollisionBehavior.builder().collisionBehaviorType(CollisionBehaviorType.PROPAGATE).priority(0).build(); - } + return CollisionBehavior.builder().collisionBehaviorType(CollisionBehaviorType.PROPAGATE).priority(0).build(); + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/entity/EntityTypeGridCollidable.java b/core/src/main/java/coffee/liz/abstractionengine/entity/EntityTypeGridCollidable.java index c75b717..e7def51 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/entity/EntityTypeGridCollidable.java +++ b/core/src/main/java/coffee/liz/abstractionengine/entity/EntityTypeGridCollidable.java @@ -7,8 +7,8 @@ import lombok.Data; @Data public class EntityTypeGridCollidable implements GridCollidable { - @Override - public CollisionBehavior getCollisionBehaviorBetween(final Entity me, final Entity them) { - return me.get(EntityType.class).collideWith(them.get(EntityType.class)); - } + @Override + public CollisionBehavior getCollisionBehaviorBetween(final Entity me, final Entity them) { + return me.get(EntityType.class).collideWith(them.get(EntityType.class)); + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/grid/component/GridCollidable.java b/core/src/main/java/coffee/liz/abstractionengine/grid/component/GridCollidable.java index e72d4e8..44b36f2 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/grid/component/GridCollidable.java +++ b/core/src/main/java/coffee/liz/abstractionengine/grid/component/GridCollidable.java @@ -10,56 +10,56 @@ import lombok.RequiredArgsConstructor; /** {@link Component} for grid collision handling. */ public interface GridCollidable extends Component { - @RequiredArgsConstructor - @Data - @Builder - class CollisionBehavior implements Comparable<CollisionBehavior> { - private final CollisionBehaviorType collisionBehaviorType; - private final int priority; + @RequiredArgsConstructor + @Data + @Builder + class CollisionBehavior implements Comparable<CollisionBehavior> { + private final CollisionBehaviorType collisionBehaviorType; + private final int priority; - public int compareTo(final CollisionBehavior other) { - return Integer.compare(getPriority(), other.getPriority()); - } + public int compareTo(final CollisionBehavior other) { + return Integer.compare(getPriority(), other.getPriority()); + } - public enum CollisionBehaviorType { - /** Propagate collision to next entity. */ - PROPAGATE, - /** Block collision like a wall. */ - WALL, - /** Swallow the colliding entity. */ - SWALLOW; - } - } + public enum CollisionBehaviorType { + /** Propagate collision to next entity. */ + PROPAGATE, + /** Block collision like a wall. */ + WALL, + /** Swallow the colliding entity. */ + SWALLOW; + } + } - /** - * Get collision behavior for colliding entity. - * - * @param me - * the staring colliding {@link Entity} - * @param them - * the colliding {@link Entity} - * @return the {@link CollisionBehavior} - */ - CollisionBehavior getCollisionBehaviorBetween(final Entity me, final Entity them); + /** + * Get collision behavior for colliding entity. + * + * @param me + * the staring colliding {@link Entity} + * @param them + * the colliding {@link Entity} + * @return the {@link CollisionBehavior} + */ + CollisionBehavior getCollisionBehaviorBetween(final Entity me, final Entity them); - /** - * Handle swallowing an entity. - * - * @param them - * the {@link Entity} being swallowed - * @param world - * the {@link World} to modify - */ - default <T> void onSwallow(final Entity them, final World<T> world) { - throw new UnsupportedOperationException("Does not swallow"); // ...could not be me~ (つ﹏⊂) - } + /** + * Handle swallowing an entity. + * + * @param them + * the {@link Entity} being swallowed + * @param world + * the {@link World} to modify + */ + default <T> void onSwallow(final Entity them, final World<T> world) { + throw new UnsupportedOperationException("Does not swallow"); // ...could not be me~ (つ﹏⊂) + } - /** - * Anything that implements GridCollidable should be keyed by GridCollidable. - * - * @return Key type - */ - default Class<? extends Component> getKey() { - return GridCollidable.class; - } + /** + * Anything that implements GridCollidable should be keyed by GridCollidable. + * + * @return Key type + */ + default Class<? extends Component> getKey() { + return GridCollidable.class; + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/grid/component/GridInputState.java b/core/src/main/java/coffee/liz/abstractionengine/grid/component/GridInputState.java index 8fadb09..6ab0d2b 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/grid/component/GridInputState.java +++ b/core/src/main/java/coffee/liz/abstractionengine/grid/component/GridInputState.java @@ -16,22 +16,22 @@ import jakarta.annotation.Nullable; @AllArgsConstructor @Builder public class GridInputState { - /** Movement {@link Vec2} direction. */ - @Nullable - private final Vec2<Integer> movement; + /** Movement {@link Vec2} direction. */ + @Nullable + private final Vec2<Integer> movement; - /** Lambda term update to apply. */ - @Nullable - private final UpdateLambdaTerm updateLambdaTerm; + /** Lambda term update to apply. */ + @Nullable + private final UpdateLambdaTerm updateLambdaTerm; - /** Update lambda term with target entity. */ - @Getter - @RequiredArgsConstructor - public static class UpdateLambdaTerm { - /** {@link Entity} to update. */ - private final Entity toUpdate; + /** Update lambda term with target entity. */ + @Getter + @RequiredArgsConstructor + public static class UpdateLambdaTerm { + /** {@link Entity} to update. */ + private final Entity toUpdate; - /** {@link LambdaProgram} to apply. */ - private final LambdaProgram lambdaProgram; - } + /** {@link LambdaProgram} to apply. */ + private final LambdaProgram lambdaProgram; + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/grid/component/GridMomentum.java b/core/src/main/java/coffee/liz/abstractionengine/grid/component/GridMomentum.java index aec26b1..0281924 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/grid/component/GridMomentum.java +++ b/core/src/main/java/coffee/liz/abstractionengine/grid/component/GridMomentum.java @@ -1,8 +1,10 @@ package coffee.liz.abstractionengine.grid.component; import coffee.liz.ecs.math.Vec2; +import coffee.liz.ecs.math.Vec2i; import coffee.liz.ecs.model.Component; +import lombok.AllArgsConstructor; import lombok.Data; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -12,6 +14,6 @@ import lombok.RequiredArgsConstructor; @Data @Getter public class GridMomentum implements Component { - /** Velocity {@link Vec2}. */ - private final Vec2<Integer> velocity; + /** Current velocity {@link Vec2}. */ + private final Vec2<Integer> velocity; } diff --git a/core/src/main/java/coffee/liz/abstractionengine/grid/component/GridPosition.java b/core/src/main/java/coffee/liz/abstractionengine/grid/component/GridPosition.java index 4ec0557..7b1769e 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/grid/component/GridPosition.java +++ b/core/src/main/java/coffee/liz/abstractionengine/grid/component/GridPosition.java @@ -12,5 +12,5 @@ import lombok.RequiredArgsConstructor; @Data @Getter public class GridPosition implements Component { - private final Vec2<Integer> position; + private final Vec2<Integer> position; } diff --git a/core/src/main/java/coffee/liz/abstractionengine/grid/system/BaseGridIndexSystem.java b/core/src/main/java/coffee/liz/abstractionengine/grid/system/BaseGridIndexSystem.java index 4af8be0..ee77256 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/grid/system/BaseGridIndexSystem.java +++ b/core/src/main/java/coffee/liz/abstractionengine/grid/system/BaseGridIndexSystem.java @@ -20,36 +20,36 @@ import java.util.Set; @RequiredArgsConstructor @Getter public abstract class BaseGridIndexSystem<T> implements System<T> { - protected final Vec2<Integer> dimensions; - protected final List<List<Set<Entity>>> rows; + protected final Vec2<Integer> dimensions; + protected final List<List<Set<Entity>>> rows; - public BaseGridIndexSystem(final Vec2<Integer> dimensions) { - this.dimensions = dimensions; - this.rows = Mat2.init(dimensions, _ -> new HashSet<>()); - } + public BaseGridIndexSystem(final Vec2<Integer> dimensions) { + this.dimensions = dimensions; + this.rows = Mat2.init(dimensions, _ -> new HashSet<>()); + } - @Override - public Collection<Class<? extends System<T>>> getDependencies() { - return Set.of(); - } + @Override + public Collection<Class<? extends System<T>>> getDependencies() { + return Set.of(); + } - @Override - public void update(final World<T> world, final T state, final Duration dt) { - rows.forEach(row -> row.forEach(Set::clear)); - world.query(Set.of(GridPosition.class)).forEach(entity -> { - final GridPosition position = entity.get(GridPosition.class); - rows.get(position.getPosition().getY()).get(position.getPosition().getX()).add(entity); - }); - } + @Override + public void update(final World<T> world, final T state, final Duration dt) { + rows.forEach(row -> row.forEach(Set::clear)); + world.query(Set.of(GridPosition.class)).forEach(entity -> { + final GridPosition position = entity.get(GridPosition.class); + rows.get(position.getPosition().getY()).get(position.getPosition().getX()).add(entity); + }); + } - public Collection<Entity> entitiesAt(final Vec2<Integer> pos) { - if (!inBounds(pos)) { - return Collections.emptySet(); - } - return Set.copyOf(rows.get(pos.getY()).get(pos.getX())); - } + public Collection<Entity> entitiesAt(final Vec2<Integer> pos) { + if (!inBounds(pos)) { + return Collections.emptySet(); + } + return Set.copyOf(rows.get(pos.getY()).get(pos.getX())); + } - public boolean inBounds(final Vec2<Integer> pos) { - return pos.getX() >= 0 && pos.getX() < dimensions.getX() && pos.getY() >= 0 && pos.getY() < dimensions.getY(); - } + public boolean inBounds(final Vec2<Integer> pos) { + return pos.getX() >= 0 && pos.getX() < dimensions.getX() && pos.getY() >= 0 && pos.getY() < dimensions.getY(); + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/grid/system/GridCollisionPropagatationSystem.java b/core/src/main/java/coffee/liz/abstractionengine/grid/system/GridCollisionPropagatationSystem.java index 33c86c8..23a821d 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/grid/system/GridCollisionPropagatationSystem.java +++ b/core/src/main/java/coffee/liz/abstractionengine/grid/system/GridCollisionPropagatationSystem.java @@ -24,85 +24,85 @@ import java.util.Set; * System which resolves collisions between cells in the Grid. */ public class GridCollisionPropagatationSystem implements System<GridInputState> { - @Override - public Collection<Class<? extends System<GridInputState>>> getDependencies() { - return Set.of(GridMovementSystem.class, GridIndexSystem.class); - } + @Override + public Collection<Class<? extends System<GridInputState>>> getDependencies() { + return Set.of(GridMovementSystem.class, GridIndexSystem.class); + } - @Override - public void update(final World<GridInputState> world, final GridInputState state, final Duration dt) { - world.query(Set.of(GridMomentum.class, GridPosition.class, GridCollidable.class)).forEach(pusher -> { - final Vec2<Integer> velocity = pusher.get(GridMomentum.class).getVelocity(); - if (velocity.equals(Vec2i.ZERO)) { - return; - } + @Override + public void update(final World<GridInputState> world, final GridInputState state, final Duration dt) { + world.query(Set.of(GridMomentum.class, GridPosition.class, GridCollidable.class)).forEach(pusher -> { + final Vec2<Integer> velocity = pusher.get(GridMomentum.class).getVelocity(); + if (velocity.equals(Vec2i.ZERO)) { + return; + } - final Vec2<Integer> position = pusher.get(GridPosition.class).getPosition(); - final CollisionRayResult result = resolveCollisionRay(world, pusher, position, velocity); + final Vec2<Integer> position = pusher.get(GridPosition.class).getPosition(); + final CollisionRayResult result = resolveCollisionRay(world, pusher, position, velocity); - pusher.add(new GridMomentum(Vec2i.ZERO)); - if (result instanceof CollisionRayResult.Blocked) { - return; - } + pusher.add(new GridMomentum(Vec2i.ZERO)); + if (result instanceof CollisionRayResult.Blocked) { + return; + } - final CollisionRayResult.Propagated propagated = (CollisionRayResult.Propagated) result; - propagated.ray().forEach(e -> e.add(new GridMomentum(propagated.direction()))); - propagated.swallows() - .forEach(entry -> entry.getKey().get(GridCollidable.class).onSwallow(entry.getValue(), world)); - }); - } + final CollisionRayResult.Propagated propagated = (CollisionRayResult.Propagated) result; + propagated.ray().forEach(e -> e.add(new GridMomentum(propagated.direction()))); + propagated.swallows() + .forEach(entry -> entry.getKey().get(GridCollidable.class).onSwallow(entry.getValue(), world)); + }); + } - private CollisionRayResult resolveCollisionRay(World<GridInputState> world, Entity pusher, Vec2<Integer> start, - Vec2<Integer> direction) { - final GridIndexSystem gridIndex = world.getSystem(GridIndexSystem.class); + private CollisionRayResult resolveCollisionRay(World<GridInputState> world, Entity pusher, Vec2<Integer> start, + Vec2<Integer> direction) { + final GridIndexSystem gridIndex = world.getSystem(GridIndexSystem.class); - final List<List<Entity>> ray = new ArrayList<>(); - ray.add(List.of(pusher)); - final Set<Map.Entry<Entity, Entity>> swallows = new HashSet<>(); + final List<List<Entity>> ray = new ArrayList<>(); + ray.add(List.of(pusher)); + final Set<Map.Entry<Entity, Entity>> swallows = new HashSet<>(); - Vec2<Integer> gridPosition = start.plus(direction); - while (!ray.getLast().isEmpty()) { - if (!gridIndex.inBounds(gridPosition)) { - return new CollisionRayResult.Blocked(); - } + Vec2<Integer> gridPosition = start.plus(direction); + while (!ray.getLast().isEmpty()) { + if (!gridIndex.inBounds(gridPosition)) { + return new CollisionRayResult.Blocked(); + } - final List<Entity> collidables = gridIndex.entitiesAt(gridPosition).stream() - .filter(e -> e.has(GridCollidable.class)).toList(); - if (collidables.isEmpty()) { - break; - } + final List<Entity> collidables = gridIndex.entitiesAt(gridPosition).stream() + .filter(e -> e.has(GridCollidable.class)).toList(); + if (collidables.isEmpty()) { + break; + } - final List<Entity> nextRay = new ArrayList<>(); + final List<Entity> nextRay = new ArrayList<>(); - for (final Entity push : ray.getLast()) { - final Map.Entry<Entity, CollisionBehavior> behavior = collidables.stream() - .map(c -> Map.entry(c, c.get(GridCollidable.class).getCollisionBehaviorBetween(push, c))) - .min(Comparator.comparing(e -> e.getValue().getPriority())).orElseThrow(); + for (final Entity push : ray.getLast()) { + final Map.Entry<Entity, CollisionBehavior> behavior = collidables.stream() + .map(c -> Map.entry(c, c.get(GridCollidable.class).getCollisionBehaviorBetween(push, c))) + .min(Comparator.comparing(e -> e.getValue().getPriority())).orElseThrow(); - switch (behavior.getValue().getCollisionBehaviorType()) { - case PROPAGATE -> nextRay.add(behavior.getKey()); - case SWALLOW -> swallows.add(Map.entry(behavior.getKey(), push)); - case WALL -> { - return new CollisionRayResult.Blocked(); - } - } - } + switch (behavior.getValue().getCollisionBehaviorType()) { + case PROPAGATE -> nextRay.add(behavior.getKey()); + case SWALLOW -> swallows.add(Map.entry(behavior.getKey(), push)); + case WALL -> { + return new CollisionRayResult.Blocked(); + } + } + } - ray.add(nextRay); - gridPosition = gridPosition.plus(direction); - } + ray.add(nextRay); + gridPosition = gridPosition.plus(direction); + } - final Collection<Entity> rayEntities = ray.stream().flatMap(Collection::stream).toList(); - return new CollisionRayResult.Propagated(rayEntities, swallows, direction); - } + final Collection<Entity> rayEntities = ray.stream().flatMap(Collection::stream).toList(); + return new CollisionRayResult.Propagated(rayEntities, swallows, direction); + } - private sealed interface CollisionRayResult permits CollisionRayResult.Propagated, CollisionRayResult.Blocked { + private sealed interface CollisionRayResult permits CollisionRayResult.Propagated, CollisionRayResult.Blocked { - record Propagated(Collection<Entity> ray, Set<Map.Entry<Entity, Entity>> swallows, - Vec2<Integer> direction) implements CollisionRayResult { - } + record Propagated(Collection<Entity> ray, Set<Map.Entry<Entity, Entity>> swallows, + Vec2<Integer> direction) implements CollisionRayResult { + } - record Blocked() implements CollisionRayResult { - } - } + record Blocked() implements CollisionRayResult { + } + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/grid/system/GridIndexSystem.java b/core/src/main/java/coffee/liz/abstractionengine/grid/system/GridIndexSystem.java index ca2279f..aea669b 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/grid/system/GridIndexSystem.java +++ b/core/src/main/java/coffee/liz/abstractionengine/grid/system/GridIndexSystem.java @@ -4,7 +4,7 @@ import coffee.liz.abstractionengine.grid.component.GridInputState; import coffee.liz.ecs.math.Vec2; public class GridIndexSystem extends BaseGridIndexSystem<GridInputState> { - public GridIndexSystem(final Vec2<Integer> dimensions) { - super(dimensions); - } + public GridIndexSystem(final Vec2<Integer> dimensions) { + super(dimensions); + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/grid/system/GridMovementSystem.java b/core/src/main/java/coffee/liz/abstractionengine/grid/system/GridMovementSystem.java index f5f1089..02f012b 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/grid/system/GridMovementSystem.java +++ b/core/src/main/java/coffee/liz/abstractionengine/grid/system/GridMovementSystem.java @@ -18,14 +18,14 @@ import java.util.Set; @RequiredArgsConstructor @Getter public class GridMovementSystem implements System<GridInputState> { - @Override - public Collection<Class<? extends System<GridInputState>>> getDependencies() { - return Set.of(); - } + @Override + public Collection<Class<? extends System<GridInputState>>> getDependencies() { + return Set.of(); + } - @Override - public void update(final World<GridInputState> world, final GridInputState state, final Duration dt) { - world.query(Set.of(GridControllable.class, GridPosition.class)) - .forEach(entity -> entity.add(new GridMomentum(state.getMovement()))); - } + @Override + public void update(final World<GridInputState> world, final GridInputState state, final Duration dt) { + world.query(Set.of(GridControllable.class, GridPosition.class)) + .forEach(entity -> entity.add(new GridMomentum(state.getMovement()))); + } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/grid/system/GridPhysicsSystem.java b/core/src/main/java/coffee/liz/abstractionengine/grid/system/GridPhysicsSystem.java index 88f8d57..5e0e632 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/grid/system/GridPhysicsSystem.java +++ b/core/src/main/java/coffee/liz/abstractionengine/grid/system/GridPhysicsSystem.java @@ -13,19 +13,19 @@ import java.util.Set; /** System applying physics/position updates from momentum. */ public class GridPhysicsSystem implements System<GridInputState> { - @Override - public Collection<Class<? extends System<GridInputState>>> getDependencies() { - return Set.of(GridCollisionPropagatationSystem.class); - } + @Override + public Collection<Class<? extends System<GridInputState>>> getDependencies() { + return Set.of(GridCollisionPropagatationSystem.class); + } - @Override - public void update(final World<GridInputState> world, final GridInputState state, final Duration dt) { - world.query(Set.of(GridMomentum.class, GridPosition.class)).forEach(entity -> { - final GridMomentum momentum = entity.get(GridMomentum.class); - final GridPosition position = entity.get(GridPosition.class); + @Override + public void update(final World<GridInputState> world, final GridInputState state, final Duration dt) { + world.query(Set.of(GridMomentum.class, GridPosition.class)).forEach(entity -> { + final GridMomentum momentum = entity.get(GridMomentum.class); + final GridPosition position = entity.get(GridPosition.class); - entity.add(new GridPosition(position.getPosition().plus(momentum.getVelocity()))); - entity.add(new GridMomentum(Vec2i.ZERO)); - }); - } + entity.add(new GridPosition(position.getPosition().plus(momentum.getVelocity()))); + entity.add(new GridMomentum(Vec2i.ZERO)); + }); + } } diff --git a/core/src/main/java/coffee/liz/ecs/DAGWorld.java b/core/src/main/java/coffee/liz/ecs/DAGWorld.java index b5b54c2..24d5adc 100644 --- a/core/src/main/java/coffee/liz/ecs/DAGWorld.java +++ b/core/src/main/java/coffee/liz/ecs/DAGWorld.java @@ -26,140 +26,140 @@ import java.util.stream.Collectors; @Log4j2 @RequiredArgsConstructor public class DAGWorld<T> implements World<T> { - /** All entities in the world. */ - protected final Set<Entity> entities = Collections.synchronizedSet(new HashSet<>()); + /** All entities in the world. */ + protected final Set<Entity> entities = Collections.synchronizedSet(new HashSet<>()); - /** Cache mapping component types to entities having that component. */ - private final Map<Class<? extends Component>, Set<Entity>> componentCache = Collections - .synchronizedMap(new HashMap<>()); + /** Cache mapping component types to entities having that component. */ + private final Map<Class<? extends Component>, Set<Entity>> componentCache = Collections + .synchronizedMap(new HashMap<>()); - /** Deterministic ID's for spawned entities. */ - private final AtomicInteger nextEntityId = new AtomicInteger(0); + /** Deterministic ID's for spawned entities. */ + private final AtomicInteger nextEntityId = new AtomicInteger(0); - /** All registered systems. */ - protected final Map<Class<? extends System<T>>, System<T>> systems; + /** All registered systems. */ + protected final Map<Class<? extends System<T>>, System<T>> systems; - /** Ordered list of systems for execution. */ - private final List<System<T>> systemExecutionOrder; + /** Ordered list of systems for execution. */ + private final List<System<T>> systemExecutionOrder; - public DAGWorld(final Map<Class<? extends System<T>>, System<T>> systems) { - this.systems = systems; - this.systemExecutionOrder = buildExecutionOrder(systems.values().stream().toList()); - log.debug("Executing in order: {}", systemExecutionOrder); - } + public DAGWorld(final Map<Class<? extends System<T>>, System<T>> systems) { + this.systems = systems; + this.systemExecutionOrder = buildExecutionOrder(systems.values().stream().toList()); + log.debug("Executing in order: {}", systemExecutionOrder); + } - @Override - public Entity createEntity() { - final Entity entity = Entity.builder().id(nextEntityId.incrementAndGet()).build(); - entities.add(entity); - return entity; - } + @Override + public Entity createEntity() { + final Entity entity = Entity.builder().id(nextEntityId.incrementAndGet()).build(); + entities.add(entity); + return entity; + } - @Override - public void removeEntity(final Entity entity) { - entity.getComponentMap().keySet().forEach(componentType -> { - final Set<Entity> cachedEntities = componentCache.get(componentType); - if (cachedEntities != null) { - cachedEntities.remove(entity); - } - }); - entities.remove(entity); - } + @Override + public void removeEntity(final Entity entity) { + entity.getComponentMap().keySet().forEach(componentType -> { + final Set<Entity> cachedEntities = componentCache.get(componentType); + if (cachedEntities != null) { + cachedEntities.remove(entity); + } + }); + entities.remove(entity); + } - @Override - public Set<Entity> query(final Collection<Class<? extends Component>> components) { - if (components.isEmpty()) { - return Set.copyOf(entities); - } + @Override + public Set<Entity> query(final Collection<Class<? extends Component>> components) { + if (components.isEmpty()) { + return Set.copyOf(entities); + } - final Class<? extends Component> firstType = components.iterator().next(); - final Set<Entity> candidates = componentCache.get(firstType); - if (candidates == null) { - return Collections.emptySet(); - } + final Class<? extends Component> firstType = components.iterator().next(); + final Set<Entity> candidates = componentCache.get(firstType); + if (candidates == null) { + return Collections.emptySet(); + } - return candidates.stream().filter(entity -> components.stream().allMatch(entity::has)) - .collect(Collectors.toSet()); - } + return candidates.stream().filter(entity -> components.stream().allMatch(entity::has)) + .collect(Collectors.toSet()); + } - @Override - public void update(final T state, final Duration duration) { - systemExecutionOrder.forEach(system -> { - refreshComponentCache(); - system.update(this, state, duration); - }); - refreshComponentCache(); - } + @Override + public void update(final T state, final Duration duration) { + systemExecutionOrder.forEach(system -> { + refreshComponentCache(); + system.update(this, state, duration); + }); + refreshComponentCache(); + } - @SuppressWarnings("unchecked") - @Override - public <S extends System<T>> S getSystem(final Class<S> system) { - return (S) systems.get(system); - } + @SuppressWarnings("unchecked") + @Override + public <S extends System<T>> S getSystem(final Class<S> system) { + return (S) systems.get(system); + } - private void refreshComponentCache() { - componentCache.clear(); - entities.forEach(entity -> entity.getComponentMap().keySet().forEach( - componentType -> componentCache.computeIfAbsent(componentType, _ -> new HashSet<>()).add(entity))); - } + private void refreshComponentCache() { + componentCache.clear(); + entities.forEach(entity -> entity.getComponentMap().keySet().forEach( + componentType -> componentCache.computeIfAbsent(componentType, _ -> new HashSet<>()).add(entity))); + } - private List<System<T>> buildExecutionOrder(final Collection<System<T>> systems) { - if (systems.isEmpty()) { - return Collections.emptyList(); - } + private List<System<T>> buildExecutionOrder(final Collection<System<T>> systems) { + if (systems.isEmpty()) { + return Collections.emptyList(); + } - final Map<Class<?>, System<T>> systemMap = systems.stream() - .collect(Collectors.toMap(System::getClass, system -> system)); - final Map<Class<?>, Integer> inDegree = new HashMap<>(); - final Map<Class<?>, Set<Class<?>>> adjacencyList = new HashMap<>(); + final Map<Class<?>, System<T>> systemMap = systems.stream() + .collect(Collectors.toMap(System::getClass, system -> system)); + final Map<Class<?>, Integer> inDegree = new HashMap<>(); + final Map<Class<?>, Set<Class<?>>> adjacencyList = new HashMap<>(); - systems.forEach(system -> { - final Class<?> systemClass = system.getClass(); - inDegree.put(systemClass, 0); - adjacencyList.put(systemClass, new HashSet<>()); - }); + systems.forEach(system -> { + final Class<?> systemClass = system.getClass(); + inDegree.put(systemClass, 0); + adjacencyList.put(systemClass, new HashSet<>()); + }); - systems.forEach(system -> { - system.getDependencies().forEach(dependency -> { - if (systemMap.containsKey(dependency)) { - adjacencyList.get(dependency).add(system.getClass()); - inDegree.merge(system.getClass(), 1, Integer::sum); - } - }); - }); + systems.forEach(system -> { + system.getDependencies().forEach(dependency -> { + if (systemMap.containsKey(dependency)) { + adjacencyList.get(dependency).add(system.getClass()); + inDegree.merge(system.getClass(), 1, Integer::sum); + } + }); + }); - // Kahn's algorithm - final List<System<T>> result = new ArrayList<>(); + // Kahn's algorithm + final List<System<T>> result = new ArrayList<>(); - final Queue<Class<?>> queue = new LinkedList<>( - inDegree.entrySet().stream().filter(entry -> entry.getValue() == 0).map(Map.Entry::getKey).toList()); + final Queue<Class<?>> queue = new LinkedList<>( + inDegree.entrySet().stream().filter(entry -> entry.getValue() == 0).map(Map.Entry::getKey).toList()); - while (!queue.isEmpty()) { - final Class<?> currentClass = queue.poll(); - result.add(systemMap.get(currentClass)); + while (!queue.isEmpty()) { + final Class<?> currentClass = queue.poll(); + result.add(systemMap.get(currentClass)); - adjacencyList.getOrDefault(currentClass, Collections.emptySet()).forEach(dependent -> { - final int newInDegree = inDegree.get(dependent) - 1; - inDegree.put(dependent, newInDegree); - if (newInDegree == 0) { - queue.add(dependent); - } - }); - } + adjacencyList.getOrDefault(currentClass, Collections.emptySet()).forEach(dependent -> { + final int newInDegree = inDegree.get(dependent) - 1; + inDegree.put(dependent, newInDegree); + if (newInDegree == 0) { + queue.add(dependent); + } + }); + } - if (result.size() != systems.size()) { - throw new IllegalStateException("Circular dependency detected in systems"); - } + if (result.size() != systems.size()) { + throw new IllegalStateException("Circular dependency detected in systems"); + } - return Collections.unmodifiableList(result); - } + return Collections.unmodifiableList(result); + } - @Override - public void close() throws Exception { - for (final System<T> system : systemExecutionOrder) { - system.close(); - } - componentCache.clear(); - entities.clear(); - } + @Override + public void close() throws Exception { + for (final System<T> system : systemExecutionOrder) { + system.close(); + } + componentCache.clear(); + entities.clear(); + } } diff --git a/core/src/main/java/coffee/liz/ecs/math/Mat2.java b/core/src/main/java/coffee/liz/ecs/math/Mat2.java index 9975227..ed9493c 100644 --- a/core/src/main/java/coffee/liz/ecs/math/Mat2.java +++ b/core/src/main/java/coffee/liz/ecs/math/Mat2.java @@ -7,82 +7,82 @@ import java.util.function.Function; import java.util.function.Supplier; public final class Mat2 { - private Mat2() { - } + private Mat2() { + } - /** - * Initializes a mutable 2d matrix of given type. - * - * @param dimensions - * the dimensions - * @param constructor - * the constructor - * @return row-indexed 2d matrix of {@param dimensions} - */ - public static <T> List<List<T>> init(final Vec2<Integer> dimensions, final Function<Vec2<Integer>, T> constructor) { - final List<List<T>> rows = new ArrayList<>(); - for (int y = 0; y < dimensions.getY(); y++) { - final List<T> row = new ArrayList<>(dimensions.getX()); - for (int x = 0; x < dimensions.getX(); x++) - row.add(constructor.apply(Vec2i.builder().y(y).x(x).build())); - rows.add(row); - } - return rows; - } + /** + * Initializes a mutable 2d matrix of given type. + * + * @param dimensions + * the dimensions + * @param constructor + * the constructor + * @return row-indexed 2d matrix of {@param dimensions} + */ + public static <T> List<List<T>> init(final Vec2<Integer> dimensions, final Function<Vec2<Integer>, T> constructor) { + final List<List<T>> rows = new ArrayList<>(); + for (int y = 0; y < dimensions.getY(); y++) { + final List<T> row = new ArrayList<>(dimensions.getX()); + for (int x = 0; x < dimensions.getX(); x++) + row.add(constructor.apply(Vec2i.builder().y(y).x(x).build())); + rows.add(row); + } + return rows; + } - /** - * Convolves a {@link Convolver} across a matrix reaching neighbors around the - * grid like a torus. - * - * @param mat - * is the row-indexed 2d matrix to convolve. - * @param axes - * are the x/y major/minor axes. - * @param init - * is the initial value of the convolution. - * @param convolver - * is the {@link Convolver}. - * @param finalReduction - * to apply after {@param convolver}. - * @return result of {@param convolver} applied along axes at each cell. - * @param <T> - * is the type of the matrix to convolve. - * @param <R> - * is the type of the resulting type of each convolution. - */ - public static <T, R, U> List<List<U>> convolveTorus(final List<List<T>> mat, final Vec2<Integer> axes, - final Supplier<R> init, final Convolver<T, R> convolver, final BiFunction<T, R, U> finalReduction) { - final List<List<R>> rows = new ArrayList<>(); - for (int y = 0; y < mat.size(); y++) { - final List<R> row = new ArrayList<>(mat.get(y).size()); - for (int x = 0; x < mat.get(y).size(); x++) { - final T center = mat.get(y).get(x); - R result = init.get(); - for (int dy = -axes.getY(); dy <= axes.getY(); dy++) { - final int ry = Math.floorMod(y + dy, mat.size()); - for (int dx = -axes.getX(); dx <= axes.getX(); dx++) { - final int rx = Math.floorMod(x + dx, mat.get(ry).size()); - result = convolver.convolve(mat.get(ry).get(rx), Vec2i.builder().x(dx).y(dy).build(), result); - } - } - row.add(result); - } - rows.add(row); - } + /** + * Convolves a {@link Convolver} across a matrix reaching neighbors around the + * grid like a torus. + * + * @param mat + * is the row-indexed 2d matrix to convolve. + * @param axes + * are the x/y major/minor axes. + * @param init + * is the initial value of the convolution. + * @param convolver + * is the {@link Convolver}. + * @param finalReduction + * to apply after {@param convolver}. + * @return result of {@param convolver} applied along axes at each cell. + * @param <T> + * is the type of the matrix to convolve. + * @param <R> + * is the type of the resulting type of each convolution. + */ + public static <T, R, U> List<List<U>> convolveTorus(final List<List<T>> mat, final Vec2<Integer> axes, + final Supplier<R> init, final Convolver<T, R> convolver, final BiFunction<T, R, U> finalReduction) { + final List<List<R>> rows = new ArrayList<>(); + for (int y = 0; y < mat.size(); y++) { + final List<R> row = new ArrayList<>(mat.get(y).size()); + for (int x = 0; x < mat.get(y).size(); x++) { + final T center = mat.get(y).get(x); + R result = init.get(); + for (int dy = -axes.getY(); dy <= axes.getY(); dy++) { + final int ry = Math.floorMod(y + dy, mat.size()); + for (int dx = -axes.getX(); dx <= axes.getX(); dx++) { + final int rx = Math.floorMod(x + dx, mat.get(ry).size()); + result = convolver.convolve(mat.get(ry).get(rx), Vec2i.builder().x(dx).y(dy).build(), result); + } + } + row.add(result); + } + rows.add(row); + } - final List<List<U>> reductions = new ArrayList<>(); - for (int y = 0; y < mat.size(); y++) { - final List<U> reduction = new ArrayList<>(mat.get(y).size()); - for (int x = 0; x < mat.get(y).size(); x++) { - reduction.add(finalReduction.apply(mat.get(y).get(x), rows.get(y).get(x))); - } - reductions.add(reduction); - } - return reductions; - } + final List<List<U>> reductions = new ArrayList<>(); + for (int y = 0; y < mat.size(); y++) { + final List<U> reduction = new ArrayList<>(mat.get(y).size()); + for (int x = 0; x < mat.get(y).size(); x++) { + reduction.add(finalReduction.apply(mat.get(y).get(x), rows.get(y).get(x))); + } + reductions.add(reduction); + } + return reductions; + } - @FunctionalInterface - public interface Convolver<T, R> { - R convolve(final T center, final Vec2<Integer> rel, final R reduction); - } + @FunctionalInterface + public interface Convolver<T, R> { + R convolve(final T center, final Vec2<Integer> rel, final R reduction); + } } diff --git a/core/src/main/java/coffee/liz/ecs/math/Vec2.java b/core/src/main/java/coffee/liz/ecs/math/Vec2.java index 7620395..dde6e51 100644 --- a/core/src/main/java/coffee/liz/ecs/math/Vec2.java +++ b/core/src/main/java/coffee/liz/ecs/math/Vec2.java @@ -9,80 +9,80 @@ import java.util.function.Function; * the numeric type of vector components */ public interface Vec2<T> { - /** - * @return the x coordinate - */ - T getX(); + /** + * @return the x coordinate + */ + T getX(); - /** - * @return the y coordinate - */ - T getY(); + /** + * @return the y coordinate + */ + T getY(); - /** - * Adds another vector to this vector. - * - * @param other - * the vector to add - * @return a new vector with the result - */ - Vec2<T> plus(final Vec2<T> other); + /** + * Adds another vector to this vector. + * + * @param other + * the vector to add + * @return a new vector with the result + */ + Vec2<T> plus(final Vec2<T> other); - /** - * Subtracts another vector from this vector. - * - * @param other - * the vector to subtract - * @return a new vector with the result - */ - Vec2<T> minus(final Vec2<T> other); + /** + * Subtracts another vector from this vector. + * + * @param other + * the vector to subtract + * @return a new vector with the result + */ + Vec2<T> minus(final Vec2<T> other); - /** - * Scales this vector by the given factors. - * - * @param scaleX - * the x scale factor - * @param scaleY - * the y scale factor - * @return a new scaled vector - */ - Vec2<T> scale(final T scaleX, final T scaleY); + /** + * Scales this vector by the given factors. + * + * @param scaleX + * the x scale factor + * @param scaleY + * the y scale factor + * @return a new scaled vector + */ + Vec2<T> scale(final T scaleX, final T scaleY); - /** - * Scales this vector by the given vector. - * - * @param scale - * scale vec. - * @return a new scaled vector - */ - default Vec2<T> scale(final Vec2<T> scale) { - return scale(scale.getX(), scale.getY()); - } + /** + * Scales this vector by the given vector. + * + * @param scale + * scale vec. + * @return a new scaled vector + */ + default Vec2<T> scale(final Vec2<T> scale) { + return scale(scale.getX(), scale.getY()); + } - /** - * Length of the vector. - * - * @return length. - */ - float length(); + /** + * Length of the vector. + * + * @return length. + */ + float length(); - /** - * @return Vec2<Integer> components of {@link Vec2<T>} - */ - Vec2<Integer> intValue(); + /** + * @return Vec2<Integer> components of {@link Vec2<T>} + */ + Vec2<Integer> intValue(); - /** - * @return Vec2<Float> components of {@link Vec2<T>} - */ - Vec2<Float> floatValue(); + /** + * @return Vec2<Float> components of {@link Vec2<T>} + */ + Vec2<Float> floatValue(); - /** - * @param xTransform - * transform of x component. - * @param yTransform - * transform of y component. - * @return transformed vec applying {@param xTransform} to x component, - * {@param yTransform} to y component. - */ - Vec2<T> transform(final Function<T, T> xTransform, final Function<T, T> yTransform); + /** + * @param xTransform + * transform of x component. + * @param yTransform + * transform of y component. + * @return transformed vec applying {@param xTransform} to x component, + * {@param yTransform} to y component. + */ + Vec2<T> transform(final Function<T, T> xTransform, final Function<T, T> yTransform); } diff --git a/core/src/main/java/coffee/liz/ecs/math/Vec2f.java b/core/src/main/java/coffee/liz/ecs/math/Vec2f.java index 5b91a2d..42b73e7 100644 --- a/core/src/main/java/coffee/liz/ecs/math/Vec2f.java +++ b/core/src/main/java/coffee/liz/ecs/math/Vec2f.java @@ -15,47 +15,47 @@ import java.util.function.Function; @Data @Builder public final class Vec2f implements Vec2<Float> { - /** X coordinate. */ - private final Float x; + /** X coordinate. */ + private final Float x; - /** Y coordinate. */ - private final Float y; + /** Y coordinate. */ + private final Float y; - @Override - public Vec2<Float> plus(final Vec2<Float> other) { - return new Vec2f(x + other.getX(), y + other.getY()); - } + @Override + public Vec2<Float> plus(final Vec2<Float> other) { + return new Vec2f(x + other.getX(), y + other.getY()); + } - @Override - public Vec2<Float> minus(final Vec2<Float> other) { - return new Vec2f(x - other.getX(), y - other.getY()); - } + @Override + public Vec2<Float> minus(final Vec2<Float> other) { + return new Vec2f(x - other.getX(), y - other.getY()); + } - @Override - public Vec2<Float> scale(final Float scaleX, final Float scaleY) { - return new Vec2f(x * scaleX, y * scaleY); - } + @Override + public Vec2<Float> scale(final Float scaleX, final Float scaleY) { + return new Vec2f(x * scaleX, y * scaleY); + } - @Override - public float length() { - return (float) sqrt(x * x + y * y); - } + @Override + public float length() { + return (float) sqrt(x * x + y * y); + } - @Override - public Vec2<Float> floatValue() { - return this; - } + @Override + public Vec2<Float> floatValue() { + return this; + } - @Override - public Vec2<Float> transform(final Function<Float, Float> xTransform, final Function<Float, Float> yTransform) { - return new Vec2f(xTransform.apply(getX()), yTransform.apply(getY())); - } + @Override + public Vec2<Float> transform(final Function<Float, Float> xTransform, final Function<Float, Float> yTransform) { + return new Vec2f(xTransform.apply(getX()), yTransform.apply(getY())); + } - @Override - public Vec2<Integer> intValue() { - return Vec2i.builder().x(this.x.intValue()).y(this.y.intValue()).build(); - } + @Override + public Vec2<Integer> intValue() { + return Vec2i.builder().x(this.x.intValue()).y(this.y.intValue()).build(); + } - /** Zero float vec */ - public static Vec2<Float> ZERO = Vec2f.builder().x(0f).y(0f).build(); + /** Zero float vec */ + public static Vec2<Float> ZERO = Vec2f.builder().x(0f).y(0f).build(); } diff --git a/core/src/main/java/coffee/liz/ecs/math/Vec2i.java b/core/src/main/java/coffee/liz/ecs/math/Vec2i.java index b07e481..326a5df 100644 --- a/core/src/main/java/coffee/liz/ecs/math/Vec2i.java +++ b/core/src/main/java/coffee/liz/ecs/math/Vec2i.java @@ -15,51 +15,51 @@ import java.util.function.Function; @Builder @Data public final class Vec2i implements Vec2<Integer> { - /** X coordinate. */ - private final Integer x; + /** X coordinate. */ + private final Integer x; - /** Y coordinate. */ - private final Integer y; + /** Y coordinate. */ + private final Integer y; - @Override - public Vec2<Integer> plus(final Vec2<Integer> other) { - return new Vec2i(x + other.getX(), y + other.getY()); - } + @Override + public Vec2<Integer> plus(final Vec2<Integer> other) { + return new Vec2i(x + other.getX(), y + other.getY()); + } - @Override - public Vec2<Integer> minus(final Vec2<Integer> other) { - return new Vec2i(x - other.getX(), y - other.getY()); - } + @Override + public Vec2<Integer> minus(final Vec2<Integer> other) { + return new Vec2i(x - other.getX(), y - other.getY()); + } - @Override - public Vec2<Integer> scale(final Integer scaleX, final Integer scaleY) { - return new Vec2i(x * scaleX, y * scaleY); - } + @Override + public Vec2<Integer> scale(final Integer scaleX, final Integer scaleY) { + return new Vec2i(x * scaleX, y * scaleY); + } - @Override - public Vec2<Float> floatValue() { - return Vec2f.builder().x(this.x.floatValue()).y(this.y.floatValue()).build(); - } + @Override + public Vec2<Float> floatValue() { + return Vec2f.builder().x(this.x.floatValue()).y(this.y.floatValue()).build(); + } - @Override - public Vec2<Integer> transform(final Function<Integer, Integer> xTransform, - final Function<Integer, Integer> yTransform) { - return new Vec2i(xTransform.apply(getX()), yTransform.apply(getY())); - } + @Override + public Vec2<Integer> transform(final Function<Integer, Integer> xTransform, + final Function<Integer, Integer> yTransform) { + return new Vec2i(xTransform.apply(getX()), yTransform.apply(getY())); + } - @Override - public Vec2<Integer> intValue() { - return this; - } + @Override + public Vec2<Integer> intValue() { + return this; + } - @Override - public float length() { - return (float) sqrt(x * x + y * y); - } + @Override + public float length() { + return (float) sqrt(x * x + y * y); + } - public static final Vec2<Integer> NORTH = new Vec2i(0, 1); - public static final Vec2<Integer> SOUTH = new Vec2i(0, -1); - public static final Vec2<Integer> EAST = new Vec2i(1, 0); - public static final Vec2<Integer> WEST = new Vec2i(-1, 0); - public static final Vec2<Integer> ZERO = new Vec2i(0, 0); + public static final Vec2<Integer> NORTH = new Vec2i(0, 1); + public static final Vec2<Integer> SOUTH = new Vec2i(0, -1); + public static final Vec2<Integer> EAST = new Vec2i(1, 0); + public static final Vec2<Integer> WEST = new Vec2i(-1, 0); + public static final Vec2<Integer> ZERO = new Vec2i(0, 0); } diff --git a/core/src/main/java/coffee/liz/ecs/model/Component.java b/core/src/main/java/coffee/liz/ecs/model/Component.java index f96ba95..2d3a8e7 100644 --- a/core/src/main/java/coffee/liz/ecs/model/Component.java +++ b/core/src/main/java/coffee/liz/ecs/model/Component.java @@ -2,7 +2,7 @@ package coffee.liz.ecs.model; /** Component of an {@link Entity}. */ public interface Component { - default Class<? extends Component> getKey() { - return getClass(); - } + default Class<? extends Component> getKey() { + return getClass(); + } } diff --git a/core/src/main/java/coffee/liz/ecs/model/Entity.java b/core/src/main/java/coffee/liz/ecs/model/Entity.java index e820e57..7dab667 100644 --- a/core/src/main/java/coffee/liz/ecs/model/Entity.java +++ b/core/src/main/java/coffee/liz/ecs/model/Entity.java @@ -18,88 +18,88 @@ import java.util.Set; @AllArgsConstructor @Data public class Entity { - /** Unique id. */ - private final int id; + /** Unique id. */ + private final int id; - /** Instances of {@link Component}s. */ - @Builder.Default - private Map<Class<? extends Component>, Component> componentMap = Collections.synchronizedMap(new HashMap<>()); + /** Instances of {@link Component}s. */ + @Builder.Default + private Map<Class<? extends Component>, Component> componentMap = Collections.synchronizedMap(new HashMap<>()); - /** - * Check if entity has component type. - * - * @param componentType - * the {@link Component} class - * @return true if component exists - */ - public boolean has(final Class<? extends Component> componentType) { - return componentMap.containsKey(componentType); - } + /** + * Check if entity has component type. + * + * @param componentType + * the {@link Component} class + * @return true if component exists + */ + public boolean has(final Class<? extends Component> componentType) { + return componentMap.containsKey(componentType); + } - /** - * Check if entity has all component types. - * - * @param components - * collection of {@link Component} classes - * @return true if all components exist - */ - public boolean hasAll(final Collection<Class<? extends Component>> components) { - return components.stream().allMatch(this::has); - } + /** + * Check if entity has all component types. + * + * @param components + * collection of {@link Component} classes + * @return true if all components exist + */ + public boolean hasAll(final Collection<Class<? extends Component>> components) { + return components.stream().allMatch(this::has); + } - /** - * Get component by type. - * - * @param componentType - * the {@link Component} class - * @param <C> - * component type - * @return the component or throw {@link IllegalArgumentException} - */ - @SuppressWarnings("unchecked") - public <C extends Component> C get(final Class<C> componentType) { - final C component = (C) componentMap.get(componentType); - if (component == null) { - throw new IllegalArgumentException( - "Entity with id " + getId() + " does not have required component " + componentType.getSimpleName()); - } - return component; - } + /** + * Get component by type. + * + * @param componentType + * the {@link Component} class + * @param <C> + * component type + * @return the component or throw {@link IllegalArgumentException} + */ + @SuppressWarnings("unchecked") + public <C extends Component> C get(final Class<C> componentType) { + final C component = (C) componentMap.get(componentType); + if (component == null) { + throw new IllegalArgumentException( + "Entity with id " + getId() + " does not have required component " + componentType.getSimpleName()); + } + return component; + } - /** - * Add component to entity. - * - * @param component - * the {@link Component} to add - * @param <C> - * component type - * @return this {@link Entity} for chaining - */ - public <C extends Component> Entity add(final C component) { - componentMap.put(component.getKey(), component); - return this; - } + /** + * Add component to entity. + * + * @param component + * the {@link Component} to add + * @param <C> + * component type + * @return this {@link Entity} for chaining + */ + public <C extends Component> Entity add(final C component) { + componentMap.put(component.getKey(), component); + return this; + } - /** - * Remove component from entity. - * - * @param componentType - * the {@link Component} class to remove - * @param <C> - * component type - * @return this {@link Entity} for chaining - */ - public <C extends Component> Entity remove(final Class<C> componentType) { - componentMap.remove(componentType); - return this; - } + /** + * Remove component from entity. + * + * @param componentType + * the {@link Component} class to remove + * @param <C> + * component type + * @return this {@link Entity} for chaining + */ + public <C extends Component> Entity remove(final Class<C> componentType) { + componentMap.remove(componentType); + return this; + } - /** - * Get all component types. - * - * @return set of {@link Component} classes - */ - public Set<Class<? extends Component>> componentTypes() { - return componentMap.keySet(); - } + /** + * Get all component types. + * + * @return set of {@link Component} classes + */ + public Set<Class<? extends Component>> componentTypes() { + return componentMap.keySet(); + } } diff --git a/core/src/main/java/coffee/liz/ecs/model/System.java b/core/src/main/java/coffee/liz/ecs/model/System.java index ac99818..c4b0bc0 100644 --- a/core/src/main/java/coffee/liz/ecs/model/System.java +++ b/core/src/main/java/coffee/liz/ecs/model/System.java @@ -10,24 +10,24 @@ import java.util.Collection; * is the state of the stuff outside the {@link World}. */ public interface System<T> extends AutoCloseable { - /** - * {@link System} clazzes that must run before this system. - * - * @return {@link Collection} of dependencies. - */ - Collection<Class<? extends System<T>>> getDependencies(); + /** + * {@link System} clazzes that must run before this system. + * + * @return {@link Collection} of dependencies. + */ + Collection<Class<? extends System<T>>> getDependencies(); - /** - * @param world - * Is the {@link World}. - * @param state - * Is the {@link T} state outside the {@param world}. - * @param dt - * Is the timestep. - */ - void update(final World<T> world, final T state, final Duration dt); + /** + * @param world + * Is the {@link World}. + * @param state + * Is the {@link T} state outside the {@param world}. + * @param dt + * Is the timestep. + */ + void update(final World<T> world, final T state, final Duration dt); - @Override - default void close() { - } + @Override + default void close() { + } } diff --git a/core/src/main/java/coffee/liz/ecs/model/World.java b/core/src/main/java/coffee/liz/ecs/model/World.java index 05363e8..c59a0d7 100644 --- a/core/src/main/java/coffee/liz/ecs/model/World.java +++ b/core/src/main/java/coffee/liz/ecs/model/World.java @@ -11,48 +11,48 @@ import java.util.Set; * is the state of the stuff outside the world. */ public interface World<T> extends AutoCloseable { - /** - * Create unique {@link Entity} in the {@link World}. - * - * @return created {@link Entity}. - */ - Entity createEntity(); + /** + * Create unique {@link Entity} in the {@link World}. + * + * @return created {@link Entity}. + */ + Entity createEntity(); - /** - * Remove an entity from the {@link World}. - * - * @param entity - * to remove. - */ - void removeEntity(final Entity entity); + /** + * Remove an entity from the {@link World}. + * + * @param entity + * to remove. + */ + void removeEntity(final Entity entity); - /** - * Get entities with {@link Component}s. - * - * @param components - * to query for. - * @return All entities with all {@param components}. - */ - Set<Entity> query(final Collection<Class<? extends Component>> components); + /** + * Get entities with {@link Component}s. + * + * @param components + * to query for. + * @return All entities with all {@param components}. + */ + Set<Entity> query(final Collection<Class<? extends Component>> components); - /** - * Integrate the {@link World}. - * - * @param state - * Is the state outside the world. - * @param duration - * Is the time step. - */ - void update(final T state, final Duration duration); + /** + * Integrate the {@link World}. + * + * @param state + * Is the state outside the world. + * @param duration + * Is the time step. + */ + void update(final T state, final Duration duration); - /** - * Get world {@link System}. - * - * @param system - * is the Clazz. - * @param <S> - * is the {@link System} type. - * @return {@link System} instance of {@param system}. - */ - <S extends System<T>> S getSystem(final Class<S> system); + /** + * Get world {@link System}. + * + * @param system + * is the Clazz. + * @param <S> + * is the {@link System} type. + * @return {@link System} instance of {@param system}. + */ + <S extends System<T>> S getSystem(final Class<S> system); } diff --git a/core/src/main/java/coffee/liz/lambda/LambdaDriver.java b/core/src/main/java/coffee/liz/lambda/LambdaDriver.java index 2ba8c70..59cee25 100644 --- a/core/src/main/java/coffee/liz/lambda/LambdaDriver.java +++ b/core/src/main/java/coffee/liz/lambda/LambdaDriver.java @@ -20,60 +20,60 @@ import java.util.List; */ public class LambdaDriver { - /** - * Parses source code into an AST. - * - * @param sourceCode - * the source code (either Lambda or Arrow syntax) - * @return the parsed program - */ - public static LambdaProgram parse(final SourceCode sourceCode) { - return switch (sourceCode) { - case SourceCode.Lambda(String source) -> parseLambda(source); - case SourceCode.Arrow(String source) -> parseArrow(source); - }; - } + /** + * Parses source code into an AST. + * + * @param sourceCode + * the source code (either Lambda or Arrow syntax) + * @return the parsed program + */ + public static LambdaProgram parse(final SourceCode sourceCode) { + return switch (sourceCode) { + case SourceCode.Lambda(String source) -> parseLambda(source); + case SourceCode.Arrow(String source) -> parseArrow(source); + }; + } - private static LambdaProgram parseLambda(final String source) { - try (final StringReader reader = new StringReader(source)) { - return new LambdaParser(reader).Program(); - } catch (final ParseException parseException) { - throw new RuntimeException("Failed to parse program", parseException); - } - } + private static LambdaProgram parseLambda(final String source) { + try (final StringReader reader = new StringReader(source)) { + return new LambdaParser(reader).Program(); + } catch (final ParseException parseException) { + throw new RuntimeException("Failed to parse program", parseException); + } + } - private static LambdaProgram parseArrow(final String source) { - try (final StringReader reader = new StringReader(source)) { - return new ArrowParser(reader).Program(); - } catch (final ParseException parseException) { - throw new RuntimeException("Failed to parse program", parseException); - } - } + private static LambdaProgram parseArrow(final String source) { + try (final StringReader reader = new StringReader(source)) { + return new ArrowParser(reader).Program(); + } catch (final ParseException parseException) { + throw new RuntimeException("Failed to parse program", parseException); + } + } - /** - * Parses and evaluates lambda calculus programs. - * - * @param sourceCode - * the source code - * @return the evaluated result - */ - public static Value interpret(final SourceCode sourceCode) { - return interpret(sourceCode, List.of()); - } + /** + * Parses and evaluates lambda calculus programs. + * + * @param sourceCode + * the source code + * @return the evaluated result + */ + public static Value interpret(final SourceCode sourceCode) { + return interpret(sourceCode, List.of()); + } - /** - * Parses and evaluates lambda calculus programs with "FFI"'s. - * - * @param sourceCode - * the source code - * @param bindings - * external Java functions available during evaluation - * @return the evaluated result - */ - public static Value interpret(final SourceCode sourceCode, final List<ExternalBinding> bindings) { - final LambdaProgram program = parse(sourceCode); - final Expression expression = program.expression(); - final List<Macro> macros = program.macros(); - return NormalOrderEvaluator.evaluate(expression, Environment.from(macros, bindings)); - } + /** + * Parses and evaluates lambda calculus programs with "FFI"'s. + * + * @param sourceCode + * the source code + * @param bindings + * external Java functions available during evaluation + * @return the evaluated result + */ + public static Value interpret(final SourceCode sourceCode, final List<ExternalBinding> bindings) { + final LambdaProgram program = parse(sourceCode); + final Expression expression = program.expression(); + final List<Macro> macros = program.macros(); + return NormalOrderEvaluator.evaluate(expression, Environment.from(macros, bindings)); + } } diff --git a/core/src/main/java/coffee/liz/lambda/ast/Expression.java b/core/src/main/java/coffee/liz/lambda/ast/Expression.java index 6d75a08..c2bdd39 100644 --- a/core/src/main/java/coffee/liz/lambda/ast/Expression.java +++ b/core/src/main/java/coffee/liz/lambda/ast/Expression.java @@ -8,21 +8,21 @@ import java.util.Optional; * Represents an expression in the untyped lambda calculus. */ public sealed interface Expression - permits Expression.AbstractionExpression, Expression.IdentifierExpression, Expression.ApplicationExpression { + permits Expression.AbstractionExpression, Expression.IdentifierExpression, Expression.ApplicationExpression { - Optional<SourceComment> comment(); + Optional<SourceComment> comment(); - SourceSpan span(); + SourceSpan span(); - record AbstractionExpression(@NonNull Optional<SourceComment> comment, @NonNull SourceSpan span, - @NonNull String parameter, @NonNull Expression body) implements Expression { - } + record AbstractionExpression(@NonNull Optional<SourceComment> comment, @NonNull SourceSpan span, + @NonNull String parameter, @NonNull Expression body) implements Expression { + } - record ApplicationExpression(@NonNull Optional<SourceComment> comment, @NonNull SourceSpan span, - @NonNull Expression applicable, @NonNull Expression argument) implements Expression { - } + record ApplicationExpression(@NonNull Optional<SourceComment> comment, @NonNull SourceSpan span, + @NonNull Expression applicable, @NonNull Expression argument) implements Expression { + } - record IdentifierExpression(@NonNull Optional<SourceComment> comment, @NonNull SourceSpan span, - @NonNull String name) implements Expression { - } + record IdentifierExpression(@NonNull Optional<SourceComment> comment, @NonNull SourceSpan span, + @NonNull String name) implements Expression { + } } diff --git a/core/src/main/java/coffee/liz/lambda/ast/Macro.java b/core/src/main/java/coffee/liz/lambda/ast/Macro.java index 07ba911..4a485fc 100644 --- a/core/src/main/java/coffee/liz/lambda/ast/Macro.java +++ b/core/src/main/java/coffee/liz/lambda/ast/Macro.java @@ -8,5 +8,5 @@ import java.util.Optional; * A named macro definition that maps an identifier to an expression. */ public record Macro(@NonNull Optional<SourceComment> comment, @NonNull SourceSpan span, @NonNull String name, - @NonNull Expression expression) { + @NonNull Expression expression) { } diff --git a/core/src/main/java/coffee/liz/lambda/ast/SourceCode.java b/core/src/main/java/coffee/liz/lambda/ast/SourceCode.java index 200c45e..c2112ad 100644 --- a/core/src/main/java/coffee/liz/lambda/ast/SourceCode.java +++ b/core/src/main/java/coffee/liz/lambda/ast/SourceCode.java @@ -4,24 +4,24 @@ package coffee.liz.lambda.ast; * Represents source code in one of the supported lambda calculus syntaxes. */ public sealed interface SourceCode { - static SourceCode ofLambda(final String source) { - return new Lambda(source); - } + static SourceCode ofLambda(final String source) { + return new Lambda(source); + } - static SourceCode ofArrow(final String source) { - return new Arrow(source); - } + static SourceCode ofArrow(final String source) { + return new Arrow(source); + } - record Lambda(String source) implements SourceCode { - } + record Lambda(String source) implements SourceCode { + } - record Arrow(String source) implements SourceCode { - } + record Arrow(String source) implements SourceCode { + } - /** - * Supported syntax types for {@link SourceCode}. - */ - enum Syntax { - LAMBDA, ARROW - } + /** + * Supported syntax types for {@link SourceCode}. + */ + enum Syntax { + LAMBDA, ARROW + } } diff --git a/core/src/main/java/coffee/liz/lambda/ast/SourceComment.java b/core/src/main/java/coffee/liz/lambda/ast/SourceComment.java index da6b5ab..8e50f5f 100644 --- a/core/src/main/java/coffee/liz/lambda/ast/SourceComment.java +++ b/core/src/main/java/coffee/liz/lambda/ast/SourceComment.java @@ -12,10 +12,10 @@ import lombok.NonNull; */ public record SourceComment(@NonNull String text, @NonNull SourceSpan span) { - /** - * Returns true if this comment is on the same line as the given span's end. - */ - public boolean isInlineAfter(final SourceSpan previous) { - return previous != null && previous.endLine() == this.span.startLine(); - } + /** + * Returns true if this comment is on the same line as the given span's end. + */ + public boolean isInlineAfter(final SourceSpan previous) { + return previous != null && previous.endLine() == this.span.startLine(); + } } diff --git a/core/src/main/java/coffee/liz/lambda/ast/SourceSpan.java b/core/src/main/java/coffee/liz/lambda/ast/SourceSpan.java index 7df9bcd..80e06f9 100644 --- a/core/src/main/java/coffee/liz/lambda/ast/SourceSpan.java +++ b/core/src/main/java/coffee/liz/lambda/ast/SourceSpan.java @@ -13,12 +13,12 @@ package coffee.liz.lambda.ast; * 1-based column number where the span ends */ public record SourceSpan(int startLine, int startColumn, int endLine, int endColumn) { - public static final SourceSpan UNKNOWN = new SourceSpan(0, 0, 0, 0); + public static final SourceSpan UNKNOWN = new SourceSpan(0, 0, 0, 0); - /** - * Returns true if this span ends on the same line that the other span starts. - */ - public boolean isOnSameLine(final SourceSpan other) { - return this.endLine == other.startLine; - } + /** + * Returns true if this span ends on the same line that the other span starts. + */ + public boolean isOnSameLine(final SourceSpan other) { + return this.endLine == other.startLine; + } } diff --git a/core/src/main/java/coffee/liz/lambda/bind/ExternalBinding.java b/core/src/main/java/coffee/liz/lambda/bind/ExternalBinding.java index 4895972..7a8ae40 100644 --- a/core/src/main/java/coffee/liz/lambda/bind/ExternalBinding.java +++ b/core/src/main/java/coffee/liz/lambda/bind/ExternalBinding.java @@ -14,10 +14,10 @@ import java.util.function.BiFunction; */ public interface ExternalBinding extends BiFunction<Environment, Value, Value> { - /** - * Returns the name used to reference this binding in environment. - * - * @return the binding name - */ - String getName(); + /** + * Returns the name used to reference this binding in environment. + * + * @return the binding name + */ + String getName(); } diff --git a/core/src/main/java/coffee/liz/lambda/bind/Tick.java b/core/src/main/java/coffee/liz/lambda/bind/Tick.java index 0fa4de5..c4ad8ee 100644 --- a/core/src/main/java/coffee/liz/lambda/bind/Tick.java +++ b/core/src/main/java/coffee/liz/lambda/bind/Tick.java @@ -11,12 +11,12 @@ import java.util.concurrent.atomic.AtomicInteger; */ @Getter public class Tick implements ExternalBinding { - private final String name = "Tick"; - private final AtomicInteger counter = new AtomicInteger(0); + private final String name = "Tick"; + private final AtomicInteger counter = new AtomicInteger(0); - @Override - public Value apply(final Environment environment, final Value value) { - counter.incrementAndGet(); - return value; - } + @Override + public Value apply(final Environment environment, final Value value) { + counter.incrementAndGet(); + return value; + } } diff --git a/core/src/main/java/coffee/liz/lambda/bind/ToChurch.java b/core/src/main/java/coffee/liz/lambda/bind/ToChurch.java index bfddd86..315622a 100644 --- a/core/src/main/java/coffee/liz/lambda/bind/ToChurch.java +++ b/core/src/main/java/coffee/liz/lambda/bind/ToChurch.java @@ -22,28 +22,28 @@ import lombok.Getter; */ @Getter public class ToChurch implements ExternalBinding { - private final String name = "ToChurch"; + private final String name = "ToChurch"; - /** - * Converts a free variable containing an integer string to a Church numeral. - * - * @param env - * the current environment - * @param val - * a Free value whose name is an integer string - * @return a Closure representing the Church numeral - */ - @Override - public Value apply(final Environment env, final Value val) { - final Free free = (Free) val; - final int n = Integer.parseInt(free.name()); + /** + * Converts a free variable containing an integer string to a Church numeral. + * + * @param env + * the current environment + * @param val + * a Free value whose name is an integer string + * @return a Closure representing the Church numeral + */ + @Override + public Value apply(final Environment env, final Value val) { + final Free free = (Free) val; + final int n = Integer.parseInt(free.name()); - Expression body = new IdentifierExpression(Optional.empty(), SourceSpan.UNKNOWN, "x"); - for (int i = 0; i < n; i++) { - body = new ApplicationExpression(Optional.empty(), SourceSpan.UNKNOWN, - new IdentifierExpression(Optional.empty(), SourceSpan.UNKNOWN, "f"), body); - } + Expression body = new IdentifierExpression(Optional.empty(), SourceSpan.UNKNOWN, "x"); + for (int i = 0; i < n; i++) { + body = new ApplicationExpression(Optional.empty(), SourceSpan.UNKNOWN, + new IdentifierExpression(Optional.empty(), SourceSpan.UNKNOWN, "f"), body); + } - return new Closure(env, "f", new AbstractionExpression(Optional.empty(), SourceSpan.UNKNOWN, "x", body)); - } + return new Closure(env, "f", new AbstractionExpression(Optional.empty(), SourceSpan.UNKNOWN, "x", body)); + } } diff --git a/core/src/main/java/coffee/liz/lambda/eval/Environment.java b/core/src/main/java/coffee/liz/lambda/eval/Environment.java index 8854719..a4ec24a 100644 --- a/core/src/main/java/coffee/liz/lambda/eval/Environment.java +++ b/core/src/main/java/coffee/liz/lambda/eval/Environment.java @@ -18,92 +18,92 @@ import java.util.stream.Collectors; */ @RequiredArgsConstructor public final class Environment { - /** Named expansions */ - private final Map<String, Expression> macros; + /** Named expansions */ + private final Map<String, Expression> macros; - /** "FFI" */ - private final Map<String, ExternalBinding> externalBindings; + /** "FFI" */ + private final Map<String, ExternalBinding> externalBindings; - /** Variable name bound at this scope level. Null for root. */ - @Nullable - private final String boundName; + /** Variable name bound at this scope level. Null for root. */ + @Nullable + private final String boundName; - /** Lazily-evaluated value for boundName, or null for root. */ - @Nullable - private final Supplier<Value> boundValue; + /** Lazily-evaluated value for boundName, or null for root. */ + @Nullable + private final Supplier<Value> boundValue; - /** Enclosing scope, or null for root. Forms a linked list of bindings. */ - @Nullable - private final Environment parent; + /** Enclosing scope, or null for root. Forms a linked list of bindings. */ + @Nullable + private final Environment parent; - /** - * Creates an environment from macro and external binding lists. - * - * @param macros - * program macro definitions - * @param externalBindings - * external Java bindings for FFI - * @return the new environment - */ - public static Environment from(final List<Macro> macros, final List<ExternalBinding> externalBindings) { - return new Environment(macros.stream().collect(Collectors.toMap(Macro::name, Macro::expression)), - externalBindings.stream().collect(Collectors.toMap(ExternalBinding::getName, Function.identity())), - null, null, null); - } + /** + * Creates an environment from macro and external binding lists. + * + * @param macros + * program macro definitions + * @param externalBindings + * external Java bindings for FFI + * @return the new environment + */ + public static Environment from(final List<Macro> macros, final List<ExternalBinding> externalBindings) { + return new Environment(macros.stream().collect(Collectors.toMap(Macro::name, Macro::expression)), + externalBindings.stream().collect(Collectors.toMap(ExternalBinding::getName, Function.identity())), + null, null, null); + } - /** - * Creates a child scope. - * - * @param name - * the variable name - * @param value - * the value supplier (thunk) - * @return a new environment with the binding added - */ - public Environment extend(final String name, final Supplier<Value> value) { - return new Environment(macros, externalBindings, name, value, this); - } + /** + * Creates a child scope. + * + * @param name + * the variable name + * @param value + * the value supplier (thunk) + * @return a new environment with the binding added + */ + public Environment extend(final String name, final Supplier<Value> value) { + return new Environment(macros, externalBindings, name, value, this); + } - /** - * Looks up a name, checking bindings, then macros, then external bindings. - * - * @param name - * the name to look up - * @return the lookup result, or empty if not found - */ - public Optional<LookupResult> lookup(final String name) { - for (Environment env = this; env != null; env = env.parent) { - if (!name.equals(env.boundName)) { - continue; - } - return Optional.of(new LookupResult.Binding(env.boundValue)); - } + /** + * Looks up a name, checking bindings, then macros, then external bindings. + * + * @param name + * the name to look up + * @return the lookup result, or empty if not found + */ + public Optional<LookupResult> lookup(final String name) { + for (Environment env = this; env != null; env = env.parent) { + if (!name.equals(env.boundName)) { + continue; + } + return Optional.of(new LookupResult.Binding(env.boundValue)); + } - final Expression macro = macros.get(name); - if (macro != null) { - return Optional.of(new LookupResult.Macro(macro)); - } + final Expression macro = macros.get(name); + if (macro != null) { + return Optional.of(new LookupResult.Macro(macro)); + } - final ExternalBinding external = externalBindings.get(name); - if (external != null) { - return Optional.of(new LookupResult.External(external)); - } + final ExternalBinding external = externalBindings.get(name); + if (external != null) { + return Optional.of(new LookupResult.External(external)); + } - return Optional.empty(); - } + return Optional.empty(); + } - /** - * Result of looking up a name in the environment. - */ - public sealed interface LookupResult { - /** A local variable binding. */ - record Binding(Supplier<Value> value) implements LookupResult { - } - /** A macro definition. */ - record Macro(Expression expression) implements LookupResult { - } - /** An external Java binding. */ - record External(ExternalBinding binding) implements LookupResult { - } - } + /** + * Result of looking up a name in the environment. + */ + public sealed interface LookupResult { + /** A local variable binding. */ + record Binding(Supplier<Value> value) implements LookupResult { + } + /** A macro definition. */ + record Macro(Expression expression) implements LookupResult { + } + /** An external Java binding. */ + record External(ExternalBinding binding) implements LookupResult { + } + } } diff --git a/core/src/main/java/coffee/liz/lambda/eval/EvaluationDepthExceededException.java b/core/src/main/java/coffee/liz/lambda/eval/EvaluationDepthExceededException.java index 7926004..8f2ad2e 100644 --- a/core/src/main/java/coffee/liz/lambda/eval/EvaluationDepthExceededException.java +++ b/core/src/main/java/coffee/liz/lambda/eval/EvaluationDepthExceededException.java @@ -7,10 +7,10 @@ import lombok.Getter; */ @Getter public final class EvaluationDepthExceededException extends RuntimeException { - private final int maxDepth; + private final int maxDepth; - public EvaluationDepthExceededException(final int maxDepth) { - super("Evaluation exceeded maximum depth of " + maxDepth); - this.maxDepth = maxDepth; - } + public EvaluationDepthExceededException(final int maxDepth) { + super("Evaluation exceeded maximum depth of " + maxDepth); + this.maxDepth = maxDepth; + } } diff --git a/core/src/main/java/coffee/liz/lambda/eval/NormalOrderEvaluator.java b/core/src/main/java/coffee/liz/lambda/eval/NormalOrderEvaluator.java index 28eb69b..d9bcba3 100644 --- a/core/src/main/java/coffee/liz/lambda/eval/NormalOrderEvaluator.java +++ b/core/src/main/java/coffee/liz/lambda/eval/NormalOrderEvaluator.java @@ -18,84 +18,84 @@ import coffee.liz.lambda.eval.Value.Free; */ public final class NormalOrderEvaluator { - public static final int DEFAULT_MAX_DEPTH = 140; + public static final int DEFAULT_MAX_DEPTH = 140; - /** - * Evaluates an expression in the given environment with default depth limit. - * - * @param term - * the expression to evaluate - * @param env - * the environment containing bindings - * @return the resulting value - * @throws EvaluationDepthExceededException - * if evaluation exceeds {@link #DEFAULT_MAX_DEPTH} - */ - public static Value evaluate(final Expression term, final Environment env) { - return evaluate(term, env, DEFAULT_MAX_DEPTH); - } + /** + * Evaluates an expression in the given environment with default depth limit. + * + * @param term + * the expression to evaluate + * @param env + * the environment containing bindings + * @return the resulting value + * @throws EvaluationDepthExceededException + * if evaluation exceeds {@link #DEFAULT_MAX_DEPTH} + */ + public static Value evaluate(final Expression term, final Environment env) { + return evaluate(term, env, DEFAULT_MAX_DEPTH); + } - /** - * Evaluates an expression in the given environment with specified depth limit. - * - * @param term - * the expression to evaluate - * @param env - * the environment containing bindings - * @param maxDepth - * maximum evaluation depth - * @return the resulting value - * @throws EvaluationDepthExceededException - * if evaluation exceeds maxDepth - */ - public static Value evaluate(final Expression term, final Environment env, final int maxDepth) { - return evaluate(term, env, maxDepth, 0); - } + /** + * Evaluates an expression in the given environment with specified depth limit. + * + * @param term + * the expression to evaluate + * @param env + * the environment containing bindings + * @param maxDepth + * maximum evaluation depth + * @return the resulting value + * @throws EvaluationDepthExceededException + * if evaluation exceeds maxDepth + */ + public static Value evaluate(final Expression term, final Environment env, final int maxDepth) { + return evaluate(term, env, maxDepth, 0); + } - private static Value evaluate(final Expression term, final Environment env, final int maxDepth, final int depth) { - if (depth > maxDepth) { - throw new EvaluationDepthExceededException(maxDepth); - } + private static Value evaluate(final Expression term, final Environment env, final int maxDepth, final int depth) { + if (depth > maxDepth) { + throw new EvaluationDepthExceededException(maxDepth); + } - return switch (term) { - case IdentifierExpression(var _, var _, final String name) -> - env.lookup(name).map(result -> switch (result) { - case LookupResult.Binding(final var value) -> value.get(); - case LookupResult.Macro(final var expr) -> evaluate(expr, env, maxDepth, depth + 1); - case LookupResult.External(final var binding) -> new Free(binding.getName()); - }).orElseGet(() -> new Free(name)); + return switch (term) { + case IdentifierExpression(var _, var _, final String name) -> + env.lookup(name).map(result -> switch (result) { + case LookupResult.Binding(final var value) -> value.get(); + case LookupResult.Macro(final var expr) -> evaluate(expr, env, maxDepth, depth + 1); + case LookupResult.External(final var binding) -> new Free(binding.getName()); + }).orElseGet(() -> new Free(name)); - case AbstractionExpression(var _, var _, final String parameter, final Expression body) -> - new Closure(env, parameter, body); + case AbstractionExpression(var _, var _, final String parameter, final Expression body) -> + new Closure(env, parameter, body); - case ApplicationExpression(var _, var _, final Expression func, final Expression arg) -> - apply(evaluate(func, env, maxDepth, depth + 1), arg, env, maxDepth, depth + 1); - }; - } + case ApplicationExpression(var _, var _, final Expression func, final Expression arg) -> + apply(evaluate(func, env, maxDepth, depth + 1), arg, env, maxDepth, depth + 1); + }; + } - private static Value apply(final Value funcVal, final Expression arg, final Environment argEnv, final int maxDepth, - final int depth) { - if (depth > maxDepth) { - throw new EvaluationDepthExceededException(maxDepth); - } + private static Value apply(final Value funcVal, final Expression arg, final Environment argEnv, final int maxDepth, + final int depth) { + if (depth > maxDepth) { + throw new EvaluationDepthExceededException(maxDepth); + } - return switch (funcVal) { - case Closure(final Environment closureEnv, final String parameter, final Expression body) -> { - final Thunk<Value> thunk = new Thunk<>(() -> evaluate(arg, argEnv, maxDepth, depth + 1)); - final Environment newEnv = closureEnv.extend(parameter, thunk); - yield evaluate(body, newEnv, maxDepth, depth + 1); - } + return switch (funcVal) { + case Closure(final Environment closureEnv, final String parameter, final Expression body) -> { + final Thunk<Value> thunk = new Thunk<>(() -> evaluate(arg, argEnv, maxDepth, depth + 1)); + final Environment newEnv = closureEnv.extend(parameter, thunk); + yield evaluate(body, newEnv, maxDepth, depth + 1); + } - case Free(final String name) -> - argEnv.lookup(name).filter(r -> r instanceof LookupResult.External).map(r -> { - final ExternalBinding binding = ((LookupResult.External) r).binding(); - return binding.apply(argEnv, evaluate(arg, argEnv, maxDepth, depth + 1)); - }).orElseGet(() -> new Application(funcVal, evaluate(arg, argEnv, maxDepth, depth + 1))); + case Free(final String name) -> + argEnv.lookup(name).filter(r -> r instanceof LookupResult.External).map(r -> { + final ExternalBinding binding = ((LookupResult.External) r).binding(); + return binding.apply(argEnv, evaluate(arg, argEnv, maxDepth, depth + 1)); + }).orElseGet(() -> new Application(funcVal, evaluate(arg, argEnv, maxDepth, depth + 1))); - case Application app -> { - final Value argVal = evaluate(arg, argEnv, maxDepth, depth + 1); - yield new Application(app, argVal); - } - }; - } + case Application app -> { + final Value argVal = evaluate(arg, argEnv, maxDepth, depth + 1); + yield new Application(app, argVal); + } + }; + } } diff --git a/core/src/main/java/coffee/liz/lambda/eval/Thunk.java b/core/src/main/java/coffee/liz/lambda/eval/Thunk.java index 07c89bf..fdbda93 100644 --- a/core/src/main/java/coffee/liz/lambda/eval/Thunk.java +++ b/core/src/main/java/coffee/liz/lambda/eval/Thunk.java @@ -12,16 +12,16 @@ import java.util.function.Supplier; */ @RequiredArgsConstructor public final class Thunk<T> implements Supplier<T> { - private final Supplier<T> thinker; // https://www.youtube.com/shorts/Dzksib8YxSY - private T cached = null; - private boolean evaluated = false; + private final Supplier<T> thinker; // https://www.youtube.com/shorts/Dzksib8YxSY + private T cached = null; + private boolean evaluated = false; - @Override - public T get() { - if (!evaluated) { - cached = thinker.get(); - evaluated = true; - } - return cached; - } + @Override + public T get() { + if (!evaluated) { + cached = thinker.get(); + evaluated = true; + } + return cached; + } }
\ No newline at end of file diff --git a/core/src/main/java/coffee/liz/lambda/eval/Value.java b/core/src/main/java/coffee/liz/lambda/eval/Value.java index 58b9ba4..dc459ff 100644 --- a/core/src/main/java/coffee/liz/lambda/eval/Value.java +++ b/core/src/main/java/coffee/liz/lambda/eval/Value.java @@ -7,36 +7,36 @@ import coffee.liz.lambda.ast.Expression; */ public sealed interface Value permits Value.Closure, Value.Application, Value.Free { - /** - * A closure capturing an environment, parameter, and body. - * - * @param env - * the captured environment - * @param parameter - * the bound parameter name - * @param body - * the lambda body expression - */ - record Closure(Environment env, String parameter, Expression body) implements Value { - } + /** + * A closure capturing an environment, parameter, and body. + * + * @param env + * the captured environment + * @param parameter + * the bound parameter name + * @param body + * the lambda body expression + */ + record Closure(Environment env, String parameter, Expression body) implements Value { + } - /** - * A symbolic application of a function to an argument. - * - * @param function - * the function - * @param argument - * the argument - */ - record Application(Value function, Value argument) implements Value { - } + /** + * A symbolic application of a function to an argument. + * + * @param function + * the function + * @param argument + * the argument + */ + record Application(Value function, Value argument) implements Value { + } - /** - * A free variable. - * - * @param name - * the variable name - */ - record Free(String name) implements Value { - } + /** + * A free variable. + * + * @param name + * the variable name + */ + record Free(String name) implements Value { + } } diff --git a/core/src/main/java/coffee/liz/lambda/format/Formatter.java b/core/src/main/java/coffee/liz/lambda/format/Formatter.java index 1ac3e95..e4fbc0c 100644 --- a/core/src/main/java/coffee/liz/lambda/format/Formatter.java +++ b/core/src/main/java/coffee/liz/lambda/format/Formatter.java @@ -21,218 +21,218 @@ import java.util.Optional; @RequiredArgsConstructor public final class Formatter { - private final Syntax syntax; + private final Syntax syntax; - public String format(final LambdaProgram program) { - final StringBuilder sb = new StringBuilder(); - SourceSpan previousEnd = null; + public String format(final LambdaProgram program) { + final StringBuilder sb = new StringBuilder(); + SourceSpan previousEnd = null; - for (int i = 0; i < program.macros().size(); i++) { - final Macro macro = program.macros().get(i); + for (int i = 0; i < program.macros().size(); i++) { + final Macro macro = program.macros().get(i); - if (macro.comment().isPresent()) { - final SourceComment comment = macro.comment().get(); - final String leadingPart = getLeadingCommentPart(comment, previousEnd); - if (!leadingPart.isEmpty()) { - sb.append(leadingPart); - if (!leadingPart.endsWith("\n")) { - sb.append("\n"); - } - } - } + if (macro.comment().isPresent()) { + final SourceComment comment = macro.comment().get(); + final String leadingPart = getLeadingCommentPart(comment, previousEnd); + if (!leadingPart.isEmpty()) { + sb.append(leadingPart); + if (!leadingPart.endsWith("\n")) { + sb.append("\n"); + } + } + } - sb.append("let ").append(macro.name()).append(" = "); - sb.append(formatExpression(macro.expression(), false)); - sb.append(";"); + sb.append("let ").append(macro.name()).append(" = "); + sb.append(formatExpression(macro.expression(), false)); + sb.append(";"); - final Optional<String> inlineComment = getInlineCommentAfter(macro.span(), program, i); - inlineComment.ifPresent(c -> sb.append(" ").append(c)); - sb.append("\n"); + final Optional<String> inlineComment = getInlineCommentAfter(macro.span(), program, i); + inlineComment.ifPresent(c -> sb.append(" ").append(c)); + sb.append("\n"); - previousEnd = macro.span(); - } + previousEnd = macro.span(); + } - boolean isTrailingComment = false; - boolean inlinePartAlreadyOutput = false; + boolean isTrailingComment = false; + boolean inlinePartAlreadyOutput = false; - if (program.expression().comment().isPresent()) { - final SourceComment comment = program.expression().comment().get(); + if (program.expression().comment().isPresent()) { + final SourceComment comment = program.expression().comment().get(); - isTrailingComment = comment.span().startLine() >= program.expression().span().startLine() - && comment.span().startColumn() > program.expression().span().startColumn(); + isTrailingComment = comment.span().startLine() >= program.expression().span().startLine() + && comment.span().startColumn() > program.expression().span().startColumn(); - if (!isTrailingComment) { - inlinePartAlreadyOutput = previousEnd != null && comment.isInlineAfter(previousEnd); + if (!isTrailingComment) { + inlinePartAlreadyOutput = previousEnd != null && comment.isInlineAfter(previousEnd); - final String leadingPart = getLeadingCommentPart(comment, previousEnd); - if (!leadingPart.isEmpty()) { - sb.append(leadingPart); - if (!leadingPart.endsWith("\n")) { - sb.append("\n"); - } - } else if (!program.macros().isEmpty()) { - sb.append("\n"); - } - } else if (!program.macros().isEmpty()) { - sb.append("\n"); - } - } else if (!program.macros().isEmpty()) { - sb.append("\n"); - } + final String leadingPart = getLeadingCommentPart(comment, previousEnd); + if (!leadingPart.isEmpty()) { + sb.append(leadingPart); + if (!leadingPart.endsWith("\n")) { + sb.append("\n"); + } + } else if (!program.macros().isEmpty()) { + sb.append("\n"); + } + } else if (!program.macros().isEmpty()) { + sb.append("\n"); + } + } else if (!program.macros().isEmpty()) { + sb.append("\n"); + } - sb.append(formatExpression(program.expression(), false)); + sb.append(formatExpression(program.expression(), false)); - if (program.expression().comment().isPresent() && !inlinePartAlreadyOutput && isTrailingComment) { - sb.append(" ").append(program.expression().comment().get().text()); - } + if (program.expression().comment().isPresent() && !inlinePartAlreadyOutput && isTrailingComment) { + sb.append(" ").append(program.expression().comment().get().text()); + } - return sb.toString(); - } + return sb.toString(); + } - /** - * Gets the inline portion of a comment (first line if on same line as previous - * element). - */ - private String getInlineCommentPart(final SourceComment comment, final SourceSpan previousEnd) { - if (previousEnd == null || !comment.isInlineAfter(previousEnd)) { - return ""; - } - final String text = comment.text(); - final int newlineIndex = text.indexOf('\n'); - return newlineIndex == -1 ? text : text.substring(0, newlineIndex); - } + /** + * Gets the inline portion of a comment (first line if on same line as previous + * element). + */ + private String getInlineCommentPart(final SourceComment comment, final SourceSpan previousEnd) { + if (previousEnd == null || !comment.isInlineAfter(previousEnd)) { + return ""; + } + final String text = comment.text(); + final int newlineIndex = text.indexOf('\n'); + return newlineIndex == -1 ? text : text.substring(0, newlineIndex); + } - /** - * Gets the leading portion of a comment (all lines except inline first line). - */ - private String getLeadingCommentPart(final SourceComment comment, final SourceSpan previousEnd) { - if (previousEnd == null || !comment.isInlineAfter(previousEnd)) { - return comment.text(); - } - final String text = comment.text(); - final int newlineIndex = text.indexOf('\n'); - return newlineIndex == -1 ? "" : text.substring(newlineIndex + 1); - } + /** + * Gets the leading portion of a comment (all lines except inline first line). + */ + private String getLeadingCommentPart(final SourceComment comment, final SourceSpan previousEnd) { + if (previousEnd == null || !comment.isInlineAfter(previousEnd)) { + return comment.text(); + } + final String text = comment.text(); + final int newlineIndex = text.indexOf('\n'); + return newlineIndex == -1 ? "" : text.substring(newlineIndex + 1); + } - /** - * Gets the inline comment that appears after the given span, if any. - */ - private Optional<String> getInlineCommentAfter(final SourceSpan span, final LambdaProgram program, - final int macroIndex) { - if (macroIndex + 1 < program.macros().size()) { - final Macro nextMacro = program.macros().get(macroIndex + 1); - if (nextMacro.comment().isPresent() && nextMacro.comment().get().isInlineAfter(span)) { - final String inlinePart = getInlineCommentPart(nextMacro.comment().get(), span); - if (!inlinePart.isEmpty()) { - return Optional.of(inlinePart); - } - } - } - if (macroIndex == program.macros().size() - 1 && program.expression().comment().isPresent() - && program.expression().comment().get().isInlineAfter(span)) { - final String inlinePart = getInlineCommentPart(program.expression().comment().get(), span); - if (!inlinePart.isEmpty()) { - return Optional.of(inlinePart); - } - } - return Optional.empty(); - } + /** + * Gets the inline comment that appears after the given span, if any. + */ + private Optional<String> getInlineCommentAfter(final SourceSpan span, final LambdaProgram program, + final int macroIndex) { + if (macroIndex + 1 < program.macros().size()) { + final Macro nextMacro = program.macros().get(macroIndex + 1); + if (nextMacro.comment().isPresent() && nextMacro.comment().get().isInlineAfter(span)) { + final String inlinePart = getInlineCommentPart(nextMacro.comment().get(), span); + if (!inlinePart.isEmpty()) { + return Optional.of(inlinePart); + } + } + } + if (macroIndex == program.macros().size() - 1 && program.expression().comment().isPresent() + && program.expression().comment().get().isInlineAfter(span)) { + final String inlinePart = getInlineCommentPart(program.expression().comment().get(), span); + if (!inlinePart.isEmpty()) { + return Optional.of(inlinePart); + } + } + return Optional.empty(); + } - private String formatExpression(final Expression expr, final boolean needsParens) { - return switch (expr) { - case AbstractionExpression abs -> formatAbstraction(abs, needsParens); - case ApplicationExpression app -> formatApplication(app, needsParens); - case IdentifierExpression id -> id.name(); - }; - } + private String formatExpression(final Expression expr, final boolean needsParens) { + return switch (expr) { + case AbstractionExpression abs -> formatAbstraction(abs, needsParens); + case ApplicationExpression app -> formatApplication(app, needsParens); + case IdentifierExpression id -> id.name(); + }; + } - private String formatAbstraction(final AbstractionExpression abs, final boolean needsParens) { - final StringBuilder sb = new StringBuilder(); + private String formatAbstraction(final AbstractionExpression abs, final boolean needsParens) { + final StringBuilder sb = new StringBuilder(); - if (needsParens) { - sb.append("("); - } - switch (syntax) { - case LAMBDA -> { - sb.append("λ").append(abs.parameter()).append("."); - sb.append(formatExpression(abs.body(), false)); - } - case ARROW -> { - sb.append(abs.parameter()).append(" -> "); - sb.append(formatExpression(abs.body(), false)); - } - } - if (needsParens) { - sb.append(")"); - } + if (needsParens) { + sb.append("("); + } + switch (syntax) { + case LAMBDA -> { + sb.append("λ").append(abs.parameter()).append("."); + sb.append(formatExpression(abs.body(), false)); + } + case ARROW -> { + sb.append(abs.parameter()).append(" -> "); + sb.append(formatExpression(abs.body(), false)); + } + } + if (needsParens) { + sb.append(")"); + } - return sb.toString(); - } + return sb.toString(); + } - private String formatApplication(final ApplicationExpression app, final boolean needsParens) { - return switch (syntax) { - case LAMBDA -> formatLambdaApplication(app, needsParens); - case ARROW -> formatArrowApplication(app); - }; - } + private String formatApplication(final ApplicationExpression app, final boolean needsParens) { + return switch (syntax) { + case LAMBDA -> formatLambdaApplication(app, needsParens); + case ARROW -> formatArrowApplication(app); + }; + } - private String formatLambdaApplication(final ApplicationExpression app, final boolean needsParens) { - final StringBuilder sb = new StringBuilder(); + private String formatLambdaApplication(final ApplicationExpression app, final boolean needsParens) { + final StringBuilder sb = new StringBuilder(); - if (needsParens) { - sb.append("("); - } + if (needsParens) { + sb.append("("); + } - final boolean funcNeedsParens = app.applicable() instanceof AbstractionExpression; - sb.append(formatExpression(app.applicable(), funcNeedsParens)); + final boolean funcNeedsParens = app.applicable() instanceof AbstractionExpression; + sb.append(formatExpression(app.applicable(), funcNeedsParens)); - sb.append(" "); + sb.append(" "); - final boolean argNeedsParens = app.argument() instanceof ApplicationExpression - || app.argument() instanceof AbstractionExpression; - sb.append(formatExpression(app.argument(), argNeedsParens)); + final boolean argNeedsParens = app.argument() instanceof ApplicationExpression + || app.argument() instanceof AbstractionExpression; + sb.append(formatExpression(app.argument(), argNeedsParens)); - if (needsParens) { - sb.append(")"); - } + if (needsParens) { + sb.append(")"); + } - return sb.toString(); - } + return sb.toString(); + } - private String formatArrowApplication(final ApplicationExpression app) { - final StringBuilder sb = new StringBuilder(); + private String formatArrowApplication(final ApplicationExpression app) { + final StringBuilder sb = new StringBuilder(); - Expression func = app.applicable(); - final List<Expression> args = new ArrayList<>(); - args.add(app.argument()); + Expression func = app.applicable(); + final List<Expression> args = new ArrayList<>(); + args.add(app.argument()); - while (func instanceof ApplicationExpression appFunc) { - args.addFirst(appFunc.argument()); - func = appFunc.applicable(); - } + while (func instanceof ApplicationExpression appFunc) { + args.addFirst(appFunc.argument()); + func = appFunc.applicable(); + } - final boolean funcNeedsParens = func instanceof AbstractionExpression; - if (funcNeedsParens) { - sb.append("("); - } - sb.append(formatExpression(func, false)); - if (funcNeedsParens) { - sb.append(")"); - } + final boolean funcNeedsParens = func instanceof AbstractionExpression; + if (funcNeedsParens) { + sb.append("("); + } + sb.append(formatExpression(func, false)); + if (funcNeedsParens) { + sb.append(")"); + } - for (final Expression arg : args) { - sb.append("("); - sb.append(formatExpression(arg, false)); - sb.append(")"); - } + for (final Expression arg : args) { + sb.append("("); + sb.append(formatExpression(arg, false)); + sb.append(")"); + } - return sb.toString(); - } + return sb.toString(); + } - /** - * Formats a program in the specified syntax. - */ - public static String emit(final LambdaProgram program, final Syntax syntax) { - return new Formatter(syntax).format(program); - } + /** + * Formats a program in the specified syntax. + */ + public static String emit(final LambdaProgram program, final Syntax syntax) { + return new Formatter(syntax).format(program); + } } diff --git a/core/src/main/java/com/kotcrab/vis/ui/widget/EditorHighlightTextAreaBridge.java b/core/src/main/java/com/kotcrab/vis/ui/widget/EditorHighlightTextAreaBridge.java index a14c6a7..81cb1d3 100644 --- a/core/src/main/java/com/kotcrab/vis/ui/widget/EditorHighlightTextAreaBridge.java +++ b/core/src/main/java/com/kotcrab/vis/ui/widget/EditorHighlightTextAreaBridge.java @@ -1,33 +1,33 @@ package com.kotcrab.vis.ui.widget; public class EditorHighlightTextAreaBridge extends HighlightTextArea { - public EditorHighlightTextAreaBridge(final String text, final VisTextFieldStyle style) { - super(text, style); - } + public EditorHighlightTextAreaBridge(final String text, final VisTextFieldStyle style) { + super(text, style); + } - public void editorSyncCursorState() { - updateCurrentLine(); - showCursor(); - } + public void editorSyncCursorState() { + updateCurrentLine(); + showCursor(); + } - public void editorSetSelectionEndpoints(final int selectionStart, final int cursorPosition) { - final int clampedSelectionStart = Math.max(0, Math.min(selectionStart, getText().length())); - final int clampedCursorPosition = Math.max(0, Math.min(cursorPosition, getText().length())); - if (clampedSelectionStart == clampedCursorPosition) { - clearSelection(); - cursor = clampedCursorPosition; - editorSyncCursorState(); - return; - } + public void editorSetSelectionEndpoints(final int selectionStart, final int cursorPosition) { + final int clampedSelectionStart = Math.max(0, Math.min(selectionStart, getText().length())); + final int clampedCursorPosition = Math.max(0, Math.min(cursorPosition, getText().length())); + if (clampedSelectionStart == clampedCursorPosition) { + clearSelection(); + cursor = clampedCursorPosition; + editorSyncCursorState(); + return; + } - hasSelection = true; - this.selectionStart = clampedSelectionStart; - cursor = clampedCursorPosition; - editorSyncCursorState(); - } + hasSelection = true; + this.selectionStart = clampedSelectionStart; + cursor = clampedCursorPosition; + editorSyncCursorState(); + } - public void editorSetCursorKeepingSelection(final int cursorPosition) { - cursor = Math.max(0, Math.min(cursorPosition, getText().length())); - editorSyncCursorState(); - } + public void editorSetCursorKeepingSelection(final int cursorPosition) { + cursor = Math.max(0, Math.min(cursorPosition, getText().length())); + editorSyncCursorState(); + } } |
