Pharo:如何使Cmd + d(“do it”)默认执行整个多行语句而不仅仅是当前行

时间:2016-10-16 11:01:32

标签: smalltalk pharo

假设我有这样的多行语句:

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

目前,当我将文本光标放在某行上(没有选择任何内容)并按下Cmd + d时,Pharo会尝试执行当前行。但是如果默认情况下(当没有选择任何内容时)对我来说会更方便Pharo将执行当前语句(即所有这三行语句),而不仅仅是当前行。因为这是一个更频繁的情况(“我想执行整个语句”)而不是“我想在一个语句中执行这个特定的行”(在大多数情况下,这在语法上没有意义,因为第1行和第3行这里)。在这些后方场合(当我需要在声明中执行一行时)我会手动预先选择这一行。

我怎样才能做到这一点?

2 个答案:

答案 0 :(得分:1)

回答你的问题:看看文本组件。它有一些评估选择和做的方法。如果未选择任何内容,则会尝试选择当前行。 您可以更改此实现以查找最顶层的语句“范围”。 如果您使用代码AST而不是文本,则可能。我用这个工作了一次,使注释中的代码表达式变得更聪明。(这对所有情况都不起作用,因为在不同的工具(浏览器)中获取方法AST的上下文对于这个文本组件并不总是相同的/ workspace / and other))

答案 1 :(得分:0)

这是算法的想法。您需要改进并完善它。

定义一个类ExpressionFinder,以便在文本中找到正确的表达式。

在我的草图中,这个类有以下ivars

  • string:   窗格中的完整字符串(playground / transcript / whatever)
  • compiler:   窗格用于评估文本的编译器
  • lines:关联pos->line的集合,其中poslinestring
  • 的位置
  • index:算法使用的lines集合的当前索引
  • interval:输出间隔(如果有),否则为nil

假设您在string上获得了compilerposition和当前string光标。执行以下操作:

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

以下是计算线条集合的方法:

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

有了这一切,您就拥有了找到合适片段所需的一切。这是一个简单的想法(你应该改进):

从当前行索引开始,通过一次添加一行来查找片段。如果找到,结束。如果没有,请降低索引并从上面的行重试。

这是代码

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

,其中

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

canCompile: fragment的代码依赖于方言,在

canCompile: fragment
  ^(compiler compileExpression: fragment) notNil

如果编译器发出CompilationErrors信号,则需要在canCompile:中放置一个处理程序以避免它们。您也可以利用此类错误。例如,如果编译错误引用了未声明的变量,您知道在下面的行中找不到它的定义,因此您应该在findFrom:中退出循环,以便尝试使用上面的行等等。