From e363906397b82f0d0d3b77488b6b80a6bd2da403 Mon Sep 17 00:00:00 2001 From: lopho Date: Sat, 9 Feb 2019 23:37:50 +0100 Subject: [PATCH 1/6] evaluate selected range using shift-enter --- src/editor.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/editor.ts b/src/editor.ts index 931b6e6..c4928f0 100644 --- a/src/editor.ts +++ b/src/editor.ts @@ -82,14 +82,13 @@ export class TidalEditor { 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 there is a single-line expression or selection 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); + let range = new Range(startLine.lineNumber, 0, endLine.lineNumber, endLine.text.length); + return new TidalExpression(document.getText(range), range); } // If there is a multi-line expression From 2e125029fd23652e096838ac237aa066913872ed Mon Sep 17 00:00:00 2001 From: lopho Date: Sat, 9 Feb 2019 23:44:38 +0100 Subject: [PATCH 2/6] eval selection even when caret is on empty line --- src/editor.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/editor.ts b/src/editor.ts index c4928f0..41422f0 100644 --- a/src/editor.ts +++ b/src/editor.ts @@ -84,7 +84,6 @@ export class TidalEditor { // If there is a single-line expression or selection if (!getMultiline) { - if (this.isEmpty(document, position.line)) { return null; } const startLine = document.lineAt(this.editor.selection.start); const endLine = document.lineAt(this.editor.selection.end); let range = new Range(startLine.lineNumber, 0, endLine.lineNumber, endLine.text.length); From c208acb254e18218c7e6938fb3d1ed0caa5e205b Mon Sep 17 00:00:00 2001 From: lopho Date: Tue, 12 Feb 2019 18:13:14 +0100 Subject: [PATCH 3/6] evaluation of a selection of multi-line statements evaluation of a selection of single line statements --- src/editor.ts | 23 ++++++++++++++++------- src/repl.ts | 4 ++-- src/tidal.ts | 10 +++++++--- test/expression.test.ts | 4 ++-- test/repl.test.ts | 11 ++++++----- test/tidal.test.ts | 4 ++-- 6 files changed, 35 insertions(+), 21 deletions(-) diff --git a/src/editor.ts b/src/editor.ts index 41422f0..e25c81b 100644 --- a/src/editor.ts +++ b/src/editor.ts @@ -80,17 +80,26 @@ export class TidalEditor { public getTidalExpressionUnderCursor(getMultiline: boolean): TidalExpression | null { const document = this.editor.document; - const position = this.editor.selection.active; + 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) { - const startLine = document.lineAt(this.editor.selection.start); - const endLine = document.lineAt(this.editor.selection.end); - let range = new Range(startLine.lineNumber, 0, endLine.lineNumber, endLine.text.length); - return new TidalExpression(document.getText(range), range); + 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 + // Selection + if (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); + } + // Block const selectedRange = new Range(this.editor.selection.anchor, this.editor.selection.active); const startLineNumber = this.getStartLineNumber(document, selectedRange); if (startLineNumber === null) { @@ -100,7 +109,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); } diff --git a/src/repl.ts b/src/repl.ts index 407ceef..af48c23 100644 --- a/src/repl.ts +++ b/src/repl.ts @@ -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))); } @@ -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); } diff --git a/src/tidal.ts b/src/tidal.ts index 3da9bc3..9003c77 100644 --- a/src/tidal.ts +++ b/src/tidal.ts @@ -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; + sendTidalExpression(expression: string, isMultilineStatement: boolean): Promise; } export class Tidal implements ITidal { @@ -60,14 +60,18 @@ 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]+/); + if (splits.length === 0 || splits[0] === null) { return; } + this.ghci.writeLn(':{'); + if (!isMultilineStatement && splits[0].trim().substring(0, 2) !== "do") { + this.ghci.writeLn("do"); + } for (let i = 0; i < splits.length; i++) { this.ghci.writeLn(splits[i]); } diff --git a/test/expression.test.ts b/test/expression.test.ts index abb3a00..6196b58 100644 --- a/test/expression.test.ts +++ b/test/expression.test.ts @@ -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"); } }); @@ -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"); } }); diff --git a/test/repl.test.ts b/test/repl.test.ts index c6eae68..a3745f7 100644 --- a/test/repl.test.ts +++ b/test/repl.test.ts @@ -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()); }); @@ -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()); }); @@ -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()); }); @@ -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()); }); @@ -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()); }); + }); diff --git a/test/tidal.test.ts b/test/tidal.test.ts index 5111542..5450035 100644 --- a/test/tidal.test.ts +++ b/test/tidal.test.ts @@ -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(); }); }); @@ -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(); }); }); From c44e9f69fbd36d918146490136dd24dc341c035e Mon Sep 17 00:00:00 2001 From: lopho Date: Tue, 12 Feb 2019 19:22:02 +0100 Subject: [PATCH 4/6] deduplicated selection code --- src/editor.ts | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/editor.ts b/src/editor.ts index e25c81b..fb5af76 100644 --- a/src/editor.ts +++ b/src/editor.ts @@ -84,22 +84,13 @@ export class TidalEditor { const endLine = document.lineAt(this.editor.selection.end); // If there is a single-line expression or selection - if (!getMultiline) { + 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 - // Selection - if (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); - } - // Block + // 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) { From aaa9c04b75ea27cca5e96e48bc29dba141817def Mon Sep 17 00:00:00 2001 From: lopho Date: Tue, 12 Feb 2019 22:35:21 +0100 Subject: [PATCH 5/6] guess if multiline by keeping track of indentation on eval --- src/tidal.ts | 52 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/src/tidal.ts b/src/tidal.ts index 9003c77..7a05158 100644 --- a/src/tidal.ts +++ b/src/tidal.ts @@ -67,15 +67,57 @@ export class Tidal implements ITidal { } const splits = expression.split(/[\r\n]+/); + // nothing to evaluate if (splits.length === 0 || splits[0] === null) { return; } - this.ghci.writeLn(':{'); - if (!isMultilineStatement && splits[0].trim().substring(0, 2) !== "do") { - this.ghci.writeLn("do"); + + // directly write single line to ghci + if (splits.length === 1) { + this.ghci.writeLn(splits[0]); + } + + // 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 { From eb245e94e13be066b5c6de860eb5e7a0ab71cd5f Mon Sep 17 00:00:00 2001 From: lopho Date: Tue, 12 Feb 2019 22:52:44 +0100 Subject: [PATCH 6/6] missed a return --- src/tidal.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tidal.ts b/src/tidal.ts index 7a05158..f397055 100644 --- a/src/tidal.ts +++ b/src/tidal.ts @@ -73,6 +73,7 @@ export class Tidal implements ITidal { // directly write single line to ghci if (splits.length === 1) { this.ghci.writeLn(splits[0]); + return; } // if user requested multiline eval using ctrl+enter