Grigory Hatsevich
Grigory Hatsevich

Reputation: 419

Pharo: how to make Cmd+d ("do it") execute the whole multi-line statement by default instead of just the current line

Suppose I have a multi-line statement like this:

1 to: 5 do: [:i|
  Transcript show: i.
  Transcript cr].

Currently, when I put a text cursor on some line (without selecting anything) and press Cmd+d, Pharo tries to execute the current line. But it would be more convenient for me if by default (when nothing is selected) Pharo would execute the current statement (i.e. all this three-line statement), not just the current line. Because this is a much more frequent case ("I want to execute the whole statement") than "I want to execute this particular line inside a statement" (which in most cases just doesn't make sense syntactically, as 1st and 3rd lines here). And in these rear occasions (when I need to execute a line inside a statement) I would pre-select this line manually.

How can I achieve this?

Upvotes: 5

Views: 274

Answers (2)

Leandro Caniglia
Leandro Caniglia

Reputation: 14843

Here is the idea for an algorithm. You will need to improve and complete it.

Define a class ExpressionFinder for finding the proper expression in your text.

In my sketch this class has the following ivars

  • string: the complete string in your pane (playground/transcript/whatever)
  • compiler: the compiler used by your pane to evaluate text
  • lines: the collection of associations pos->line, where pos is the position of the line inside string
  • index: current index to the lines collection used by the algorithm
  • interval: the output interval if any, otherwise nil

Assume you are given the string, the compiler and the current position of the cursor on string. Do the following:

string: aString position: anInteger compiler: aCompiler
  string := aString.
  compiler := aCompiler.
  self computeLines.
  index := lines findLast: [:assoc | assoc key <= anInteger]

Here is how you compute the collection of lines:

computeLines
  | reader |
  lines := OrderedCollection new.
  reader := string readStream.
  [reader atEnd]
    whileFalse: [lines add: reader position + 1 -> reader nextLine]

With all of this you have everything you need to find the appropriate fragment. Here is a simple idea (which you should improve):

Start at the current line index and find the fragment by adding a line at a time. If found, end. If not, decrease the index and try again from the line above.

Here is the code

find
  | i |
  i := index.
  [
    i <= 0 ifTrue: [^self].
    assoc := lines at: i.
    self findFrom: assoc key]
    whileFalse: [i := i - 1]

where

findFrom: start
  | i end success |
  i := index.
  [| assoc fragment |
    assoc := lines at: i + 1 ifAbsent: [string size + 1 -> nil]. 
    end := assoc key - 1.
    fragment := string copyFrom: start to: end.
    success := self canCompile: fragment.
    success not and: [end < string size]]
    whileTrue: [i := i + 1].
  success ifTrue: [interval := start to: end].
  ^success

The code for canCompile: fragment is dialect-dependent, on the lines of

canCompile: fragment
  ^(compiler compileExpression: fragment) notNil

If your compiler signals CompilationErrors, you will need to put a handler in canCompile: to avoid them. Also you might take advantage of such errors. For instance, if the compilation error refers to an undeclared variable, you know that you will not find its definition in the lines below, so you should exit the loop in findFrom: so to try with the line above and so on.

Upvotes: 0

Nicolai Hess
Nicolai Hess

Reputation: 376

To answer your question: Take a look at the text component. It has some method for evaluate-selection-and-do. And if nothing is selected, it tries to select the current line. You may change this implementation to find the top most statement "scope". It could be possible if you work with the code AST instead of the text. I worked once with this, to make it smarter for code expressions inside of comments.(that didn't work for all situations because the context for getting the method AST isn't always the same for this text component,in different tools (browser/workspace/and other))

Upvotes: 1

Related Questions