Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 11 additions & 13 deletions src/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,19 +80,17 @@ export class TidalEditor {

public getTidalExpressionUnderCursor(getMultiline: boolean): TidalExpression | null {
const document = this.editor.document;
const position = this.editor.selection.active;

const line = document.lineAt(position);

// If there is a single-line expression
// TODO: decide the behaviour in case in multi-line selections
if (!getMultiline) {
if (this.isEmpty(document, position.line)) { return null; }
let range = new Range(line.lineNumber, 0, line.lineNumber, line.text.length);
return new TidalExpression(line.text, range);
const startLine = document.lineAt(this.editor.selection.start);
const endLine = document.lineAt(this.editor.selection.end);

// If there is a single-line expression or selection
if (!getMultiline || startLine.lineNumber !== endLine.lineNumber) {
const range = new Range(startLine.lineNumber, 0, endLine.lineNumber, endLine.text.length);
const text = document.getText(range);
if (text.trim().length === 0) { return null; }
return new TidalExpression(text, range);
}

// If there is a multi-line expression
// If there is a multi-line expression without selection
const selectedRange = new Range(this.editor.selection.anchor, this.editor.selection.active);
const startLineNumber = this.getStartLineNumber(document, selectedRange);
if (startLineNumber === null) {
Expand All @@ -102,7 +100,7 @@ export class TidalEditor {
const endLineNumber = this.getEndLineNumber(document, startLineNumber);
const endCol = document.lineAt(endLineNumber).text.length;

let range = new Range(startLineNumber, 0, endLineNumber, endCol);
const range = new Range(startLineNumber, 0, endLineNumber, endCol);

return new TidalExpression(document.getText(range), range);
}
Expand Down
4 changes: 2 additions & 2 deletions src/repl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export class Repl implements IRepl {
return;
}

await this.tidal.sendTidalExpression('hush');
await this.tidal.sendTidalExpression('hush', false);
this.history.log(new TidalExpression('hush', new vscode.Range(0, 0, 0, 0)));
}

Expand All @@ -43,7 +43,7 @@ export class Repl implements IRepl {
const block = new TidalEditor(this.textEditor).getTidalExpressionUnderCursor(isMultiline);

if (block) {
await this.tidal.sendTidalExpression(block.expression);
await this.tidal.sendTidalExpression(block.expression, isMultiline);
this.feedback(block.range);
this.history.log(block);
}
Expand Down
57 changes: 52 additions & 5 deletions src/tidal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import * as vscode from 'vscode';
* Provides an interface to send instructions to the current Tidal instance.
*/
export interface ITidal {
sendTidalExpression(expression: string): Promise<void>;
sendTidalExpression(expression: string, isMultilineStatement: boolean): Promise<void>;
}

export class Tidal implements ITidal {
Expand Down Expand Up @@ -60,18 +60,65 @@ export class Tidal implements ITidal {
return true;
}

public async sendTidalExpression(expression: string) {
public async sendTidalExpression(expression: string, isMultilineStatement: boolean) {
if (!await this.bootTidal()) {
this.logger.error('Could not boot Tidal');
return;
}

this.ghci.writeLn(':{');
const splits = expression.split(/[\r\n]+/);
// nothing to evaluate
if (splits.length === 0 || splits[0] === null) { return; }

// directly write single line to ghci
if (splits.length === 1) {
this.ghci.writeLn(splits[0]);
return;
}

// if user requested multiline eval using ctrl+enter
// wrap lines in multiline ghci-operator
// required for multiline statements without indentation like function declarations
if (isMultilineStatement) {
this.ghci.writeLn(":{");
for (let i = 0; i < splits.length; i++) {
this.ghci.writeLn(splits[i]);
}
this.ghci.writeLn(":}");
return;
}

// if user requested single line eval of a selection using shift+enter
// keep track of current indent level and wrap in multi-line ops if required
let indent = splits[0].replace(/^(\s*).*$/,"$1").length;
let multilineOpen = false; // keep track of open multi-line operator
let multilineMustClose = false; // if op must be closed after writing current statement
for (let i = 0; i < splits.length; i++) {
this.ghci.writeLn(splits[i]);
const currentSplit = splits[i]
if (i + 1 < splits.length) {
const futureIndent = splits[i + 1].replace(/^(\s*).*$/,"$1").length;
// next statement is deeper indented, guessing it to be part of current statement, opening
if (futureIndent > indent && !multilineOpen) {
multilineOpen = true;
this.ghci.writeLn(":{");
// next statement is shallower indented, guessing statement is done, closing
} else if (futureIndent < indent && multilineOpen) {
multilineMustClose = true;
}
indent = futureIndent;
}
this.ghci.writeLn(currentSplit);
// close open op before next, shallower indented statement
if (multilineMustClose) {
multilineMustClose = false;
multilineOpen = false;
this.ghci.writeLn(":}")
}
}
// close remaining open op
if (multilineOpen) {
this.ghci.writeLn(":}");
}
this.ghci.writeLn(':}');
}

private async getBootCommandsFromFile(uri: vscode.Uri): Promise<string[] | null> {
Expand Down
4 changes: 2 additions & 2 deletions test/expression.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ suite("Editor", () => {

assert.isNotNull(expression);
if (expression !== null) {
expect(expression.expression).to.be.equal("Hello world");
expect(expression.expression).to.be.equal("Hello world\r\n");
}
});

Expand All @@ -26,7 +26,7 @@ suite("Editor", () => {

assert.isNotNull(expression);
if (expression !== null) {
expect(expression.expression).to.be.equal("Hello world");
expect(expression.expression).to.be.equal("Hello world\r\n");
}
});

Expand Down
11 changes: 6 additions & 5 deletions test/repl.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ suite('Repl', () => {
mockConfig.object, mockCreateTextEditorDecorationType.object);
await repl.hush();

mockTidal.verify(t => t.sendTidalExpression('hush'), TypeMoq.Times.once());
mockTidal.verify(t => t.sendTidalExpression('hush', false), TypeMoq.Times.once());
mockHistory.verify(h => h.log(TypeMoq.It.isAny()), TypeMoq.Times.once());
});

Expand All @@ -40,7 +40,7 @@ suite('Repl', () => {
mockConfig.object, mockCreateTextEditorDecorationType.object);
await repl.hush();

mockTidal.verify(t => t.sendTidalExpression(TypeMoq.It.isAnyString()), TypeMoq.Times.never());
mockTidal.verify(t => t.sendTidalExpression(TypeMoq.It.isAnyString(), false), TypeMoq.Times.never());
mockHistory.verify(h => h.log(TypeMoq.It.isAny()), TypeMoq.Times.never());
});

Expand All @@ -58,7 +58,7 @@ suite('Repl', () => {
mockConfig.object, mockCreateTextEditorDecorationType.object);
await repl.evaluate(false);

mockTidal.verify(t => t.sendTidalExpression(TypeMoq.It.isAnyString()), TypeMoq.Times.never());
mockTidal.verify(t => t.sendTidalExpression(TypeMoq.It.isAnyString(), false), TypeMoq.Times.never());
mockHistory.verify(h => h.log(TypeMoq.It.isAny()), TypeMoq.Times.never());
});

Expand All @@ -76,7 +76,7 @@ suite('Repl', () => {
mockConfig.object, mockCreateTextEditorDecorationType.object);
await repl.evaluate(true);

mockTidal.verify(t => t.sendTidalExpression('Foo\r\nbar'), TypeMoq.Times.once());
mockTidal.verify(t => t.sendTidalExpression('Foo\r\nbar', true), TypeMoq.Times.once());
mockHistory.verify(h => h.log(TypeMoq.It.isAny()), TypeMoq.Times.once());
});

Expand All @@ -94,7 +94,8 @@ suite('Repl', () => {
mockConfig.object, mockCreateTextEditorDecorationType.object);
await repl.evaluate(false);

mockTidal.verify(t => t.sendTidalExpression('bar'), TypeMoq.Times.once());
mockTidal.verify(t => t.sendTidalExpression('bar', false), TypeMoq.Times.once());
mockHistory.verify(h => h.log(TypeMoq.It.isAny()), TypeMoq.Times.once());
});

});
4 changes: 2 additions & 2 deletions test/tidal.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ suite("Tidal", () => {
mockedGhci.setup(ghci => ghci.writeLn('d1 $ sound "bd"')).verifiable(TypeMoq.Times.once());
mockedGhci.setup(ghci => ghci.writeLn(':}')).verifiable(TypeMoq.Times.once());

return tidal.sendTidalExpression('d1 $ sound "bd"').then(() => {
return tidal.sendTidalExpression('d1 $ sound "bd"', false).then(() => {
mockedGhci.verifyAll();
});
});
Expand All @@ -31,7 +31,7 @@ suite("Tidal", () => {
mockedGhci.setup(ghci => ghci.writeLn('sound "bd"')).verifiable(TypeMoq.Times.once());
mockedGhci.setup(ghci => ghci.writeLn(':}')).verifiable(TypeMoq.Times.once());

return tidal.sendTidalExpression(`d1 $${lineEnding}sound "bd"`).then(() => {
return tidal.sendTidalExpression(`d1 $${lineEnding}sound "bd"`, true).then(() => {
mockedGhci.verifyAll();
});
});
Expand Down