Skip to content

Commit fe4abb7

Browse files
committed
Adding search/find to the code editor
1 parent 7a845c8 commit fe4abb7

File tree

3 files changed

+133
-35
lines changed

3 files changed

+133
-35
lines changed

CHANGELOG.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@ Bug fixes / fixed issues:
1616
stuck (e.g. because of non-matching parentheses and brackets), which should now be avoided;
1717

1818
Known issues:
19-
- Search/find and replace are still missing;
2019
- Code completion is not implemented, yet;
21-
- It seems possible to have two programs with the same name in the system;
2220

2321
### 3.0 Beta 1 (January 2023)
2422

src/main/scala/tigerjython/files/Documents.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ object Documents {
102102

103103
def makeDocumentNameUnique(name: String): String =
104104
if (findDocumentWithName(name).isDefined) {
105-
if (name.toLowerCase.endsWith("(copy)") || name.toLowerCase.endsWith("(kopie)")) {
105+
if (name.toLowerCase.endsWith("(copy)")) {
106106
var i = 2
107107
val n = name.dropRight(1) + " %d)"
108108
while (findDocumentWithName(n.format(i)).isDefined)
@@ -122,7 +122,7 @@ object Documents {
122122
val n =
123123
if (index > 0) {
124124
if (name.toLowerCase.endsWith("(copy)"))
125-
"%s %d)".format(name.dropRight(5), index)
125+
"%s %d)".format(name.dropRight(1), index)
126126
else
127127
"%s (%d)".format(name, index)
128128
} else

src/main/scala/tigerjython/ui/editor/FindBar.scala

Lines changed: 131 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@ package tigerjython.ui.editor
99

1010
import javafx.application.Platform
1111
import javafx.beans.value.{ChangeListener, ObservableValue}
12-
import javafx.scene.Scene
13-
import javafx.scene.control.{TextField, ToolBar}
12+
import javafx.scene.{Group, Node, Scene}
13+
import javafx.scene.control.{Button, Label, TextField, ToolBar}
1414
import javafx.scene.input.KeyCode
15+
import javafx.scene.shape.{Line, StrokeLineCap}
16+
17+
import java.lang
1518

1619
/**
1720
* A simple bar that lets you enter something to be found in the document.
@@ -20,19 +23,44 @@ class FindBar(val editorTab: EditorTab) extends ToolBar {
2023

2124
private val editor = editorTab.editor
2225
val findTextField: TextField = new TextField()
26+
private val countLabel: Label = new Label()
27+
private val findNextButton = new Button()
28+
private val findPrevButton = new Button()
29+
30+
private var startPos: Int = 0
31+
private var endPos: Int = 0
2332

24-
private var startIndex: Int = 0
25-
private var endIndex: Int = 0
2633
private var currentIndex: Int = 0
2734
private var searchText: String = _
35+
private var fullText: String = _
36+
private var fullTextLC: String = _
37+
38+
private var occurrences: Array[Int] = _
2839

2940
{
30-
getItems.add(findTextField)
41+
getItems.addAll(findTextField, findNextButton, findPrevButton, countLabel)
3142
setPrefHeight(48)
3243
setMinHeight(32)
33-
setPrefWidth(300)
44+
findTextField.setPrefWidth(300)
45+
findTextField.prefWidthProperty().bind(this.widthProperty().divide(3))
46+
findNextButton.setGraphic(createDownImage)
47+
findNextButton.getStyleClass.add("download-text-btn")
48+
findNextButton.setOnAction(_ => findNext())
49+
findNextButton.setFocusTraversable(false)
50+
findPrevButton.setGraphic(createUpImage)
51+
findPrevButton.getStyleClass.add("download-text-btn")
52+
findPrevButton.setOnAction(_ => findPrev())
53+
findPrevButton.setFocusTraversable(false)
3454
}
3555

56+
focusedProperty().addListener(new ChangeListener[java.lang.Boolean] {
57+
override def changed(observableValue: ObservableValue[_ <: lang.Boolean], oldValue: lang.Boolean, newValue: lang.Boolean): Unit = {
58+
if (newValue && !oldValue) {
59+
updateText()
60+
}
61+
}
62+
})
63+
3664
findTextField.sceneProperty().addListener(new ChangeListener[Scene] {
3765
override def changed(observableValue: ObservableValue[_ <: Scene], oldValue: Scene, newValue: Scene): Unit = {
3866
if (oldValue != newValue && newValue != null)
@@ -57,50 +85,122 @@ class FindBar(val editorTab: EditorTab) extends ToolBar {
5785
}
5886
})
5987

88+
private def createDownImage: Node = {
89+
val result = new Group()
90+
val lines = Array(
91+
new Line(-4, -2, 0, 2),
92+
new Line(0, 2, 4, -2),
93+
)
94+
for (line <- lines)
95+
line.setStrokeLineCap(StrokeLineCap.ROUND)
96+
result.getChildren.addAll(lines: _*)
97+
result
98+
}
99+
100+
private def createUpImage: Node = {
101+
val result = new Group()
102+
val lines = Array(
103+
new Line(-4, 2, 0, -2),
104+
new Line(0, -2, 4, 2),
105+
)
106+
for (line <- lines)
107+
line.setStrokeLineCap(StrokeLineCap.ROUND)
108+
result.getChildren.addAll(lines: _*)
109+
result
110+
}
111+
60112
def activate(): Unit = {
61113
Platform.runLater(() => {
62-
this.requestFocus()
63114
val selRange = editor.selectionProperty().getValue
64115
if (selRange.getLength > 0) {
65-
startIndex = selRange.getStart
66-
endIndex = selRange.getEnd
116+
startPos = selRange.getStart
117+
endPos = selRange.getEnd
67118
} else {
68-
startIndex = 0
69-
endIndex = -1
119+
startPos = 0
120+
endPos = -1
70121
}
122+
fullText = null
123+
updateText()
71124
findTextField.setText("")
72125
findTextField.requestFocus()
73126
})
74127
}
75128

76-
private def doFindText(start: Int): Boolean = {
77-
if (searchText != null && searchText != "") {
78-
val s: String = editor.textProperty().getValue
79-
val index = s.indexOf(searchText, start)
80-
if (index >= start) {
81-
editor.selectRange(index, index + searchText.length)
82-
currentIndex = index
129+
private def hasUppercase(s: String): Boolean = {
130+
for (ch <- s)
131+
if (ch.isUpper)
83132
return true
133+
false
134+
}
135+
136+
private def doFindAll(): Array[Int] =
137+
if (searchText != null && searchText != "") {
138+
val s: String = if (hasUppercase(searchText)) fullText else fullTextLC
139+
val result = collection.mutable.ArrayBuffer[Int]()
140+
val lastIndex =
141+
if (endPos == -1)
142+
s.length - 1
143+
else
144+
endPos - searchText.length
145+
var idx = s.indexOf(searchText, startPos)
146+
while (startPos <= idx && idx <= lastIndex) {
147+
result += idx
148+
idx = s.indexOf(searchText, idx + 1)
84149
}
150+
result.toArray
151+
} else
152+
Array()
153+
154+
private def selectEntry(idx: Int): Unit =
155+
if (idx < 0 && occurrences.nonEmpty)
156+
selectEntry(idx + occurrences.length)
157+
else if (occurrences.nonEmpty && searchText != "") {
158+
currentIndex =
159+
if (occurrences.length > 1)
160+
idx % occurrences.length
161+
else
162+
0
163+
val pos = occurrences(currentIndex)
164+
editor.selectRange(pos, pos + searchText.length)
165+
countLabel.setText("%d/%d".format(currentIndex + 1, occurrences.length))
166+
} else {
167+
if (endPos > startPos)
168+
editor.selectRange(startPos, endPos)
169+
else
170+
editor.selectRange(startPos, startPos)
171+
countLabel.setText("0/0")
172+
currentIndex = -1
85173
}
86-
if (endIndex > startIndex)
87-
editor.selectRange(startIndex, endIndex)
88-
else
89-
editor.selectRange(startIndex, startIndex)
90-
currentIndex = -1
91-
false
174+
175+
private def nextIndexAfterPos: Int = {
176+
val curPos = editor.selectionProperty().getValue.getStart
177+
for ((pos, i) <- occurrences.zipWithIndex)
178+
if (curPos <= pos)
179+
return i
180+
0
92181
}
93182

94183
protected def findFirst(searchText: String): Unit = {
95184
if (this.searchText == null || !searchText.startsWith(this.searchText))
96-
currentIndex = startIndex
185+
currentIndex = startPos
97186
this.searchText = searchText
98-
doFindText(currentIndex)
187+
occurrences = doFindAll()
188+
if (occurrences.nonEmpty)
189+
selectEntry(nextIndexAfterPos)
190+
else
191+
selectEntry(0)
99192
}
100193

101-
protected def findNext(): Unit =
102-
if (currentIndex >= 0) {
103-
if (!doFindText(currentIndex + 1))
104-
doFindText(startIndex)
105-
}
194+
protected def findNext(): Unit = {
195+
selectEntry(currentIndex + 1)
196+
}
197+
198+
protected def findPrev(): Unit = {
199+
selectEntry(currentIndex - 1)
200+
}
201+
202+
protected def updateText(): Unit = {
203+
fullText = editor.textProperty().getValue
204+
fullTextLC = fullText.toLowerCase
205+
}
106206
}

0 commit comments

Comments
 (0)