Adding basic search to the text editor
This commit is contained in:
parent
8e05690ef3
commit
4a62774a57
3 changed files with 131 additions and 15 deletions
|
|
@ -160,6 +160,15 @@ find : (a \ Bool) \ List a \ Maybe a = f list \ list
|
||||||
| True \ Some x
|
| True \ Some x
|
||||||
| False \ find f xs);
|
| False \ find f xs);
|
||||||
|
|
||||||
|
findAll = query lines \
|
||||||
|
mapWithIndex (line row \
|
||||||
|
findInLine = col \
|
||||||
|
strIndexOf query line col
|
||||||
|
| None \ []
|
||||||
|
| Some i \ [{ row = row, col = i }, ...findInLine (i + 1)];
|
||||||
|
findInLine 0
|
||||||
|
) lines ~ flatten;
|
||||||
|
|
||||||
flatten : List (List a) \ List a = lists \ fold cat [] lists;
|
flatten : List (List a) \ List a = lists \ fold cat [] lists;
|
||||||
|
|
||||||
and : Bool \ Bool \ Bool = x y \ x
|
and : Bool \ Bool \ Bool = x y \ x
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
@textEditor
|
@textEditor
|
||||||
|
|
||||||
textEditorBuffers = [];
|
|
||||||
|
|
||||||
textEditor = name \
|
textEditor = name \
|
||||||
# defaults = {};
|
# defaults = {};
|
||||||
# c = { ...defaults, ...config };
|
# c = { ...defaults, ...config };
|
||||||
|
|
@ -12,13 +10,6 @@ textEditor = name \
|
||||||
charGap = 2;
|
charGap = 2;
|
||||||
lineH = charH * scale + lineGap;
|
lineH = charH * scale + lineGap;
|
||||||
|
|
||||||
buffersKey = "textEditorBuffers";
|
|
||||||
|
|
||||||
# load from staging buffers if it exists there. if not, load from source
|
|
||||||
# source = getAt [buffersKey, name]
|
|
||||||
# | None \ getSource name
|
|
||||||
# | Some v \ v;
|
|
||||||
|
|
||||||
source = getModuleSource name
|
source = getModuleSource name
|
||||||
| Some s \ s
|
| Some s \ s
|
||||||
| None \ getSource name;
|
| None \ getSource name;
|
||||||
|
|
@ -251,6 +242,25 @@ textEditor = name \
|
||||||
newState = clampCursor state.{ cursorCol = state.cursorCol + 1 };
|
newState = clampCursor state.{ cursorCol = state.cursorCol + 1 };
|
||||||
{ state = newState, emit = [] };
|
{ state = newState, emit = [] };
|
||||||
|
|
||||||
|
findNext = query lines row col \
|
||||||
|
rest = strIndexOf query (nth row lines ~ unwrapOr "") (col + 1);
|
||||||
|
rest
|
||||||
|
| Some c \ { row = row, col = c }
|
||||||
|
| None \ (
|
||||||
|
findInLines = r \
|
||||||
|
r >= len lines
|
||||||
|
| True \ findInLines 0 # wrap to top
|
||||||
|
| False \ (r == row
|
||||||
|
| True \ (strIndexOf query (nth row lines ~ unwrapOr "") 0
|
||||||
|
| Some c \ { row = row, col = c }
|
||||||
|
| None \ { row = row, col = col }) # no match
|
||||||
|
| False \ (strIndexOf query (nth r lines ~ unwrapOr "") 0
|
||||||
|
| Some c \ { row = r, col = c }
|
||||||
|
| None \ findInLines (r + 1)));
|
||||||
|
findInLines (row + 1));
|
||||||
|
|
||||||
|
# TODO: findPrev
|
||||||
|
|
||||||
{
|
{
|
||||||
view = ctx \
|
view = ctx \
|
||||||
|
|
||||||
|
|
@ -273,6 +283,62 @@ textEditor = name \
|
||||||
withScroll = result \
|
withScroll = result \
|
||||||
{ state = autoScroll result.state, emit = result.emit };
|
{ state = autoScroll result.state, emit = result.emit };
|
||||||
|
|
||||||
|
# search stuff
|
||||||
|
initSearch = state \
|
||||||
|
newState = state.{
|
||||||
|
mode = Search,
|
||||||
|
searchQuery = "",
|
||||||
|
savedCursorCol = state.cursorCol,
|
||||||
|
savedCursorRow = state.cursorRow,
|
||||||
|
};
|
||||||
|
|
||||||
|
{ state = newState, emit = [] };
|
||||||
|
|
||||||
|
cancelSearch = state \
|
||||||
|
newState = state.{
|
||||||
|
mode = Normal,
|
||||||
|
searchQuery = "",
|
||||||
|
cursorCol = state.savedCursorCol,
|
||||||
|
cursorRow = state.savedCursorRow,
|
||||||
|
};
|
||||||
|
|
||||||
|
{ state = newState, emit = [] };
|
||||||
|
|
||||||
|
updateSearchQuery = query state \
|
||||||
|
next = findNext query state.lines state.cursorRow state.cursorCol;
|
||||||
|
newState = autoScroll state.{
|
||||||
|
searchQuery = query,
|
||||||
|
cursorCol = next.col,
|
||||||
|
cursorRow = next.row,
|
||||||
|
};
|
||||||
|
{ state = newState, emit = [] };
|
||||||
|
|
||||||
|
backspaceSearch = state \
|
||||||
|
updateSearchQuery (slice state.searchQuery 0 (len state.searchQuery - 1)) state;
|
||||||
|
|
||||||
|
addCharToSearchQuery = char state \
|
||||||
|
updateSearchQuery (state.searchQuery & char) state;
|
||||||
|
|
||||||
|
endSearch = state \
|
||||||
|
# next = findNext state.searchQuery state.lines state.cursorRow state.cursorCol;
|
||||||
|
newState = autoScroll state.{
|
||||||
|
mode = Normal,
|
||||||
|
# cursorCol = next.col,
|
||||||
|
# cursorRow = next.row,
|
||||||
|
};
|
||||||
|
|
||||||
|
{ state = newState, emit = [] };
|
||||||
|
|
||||||
|
nextSearch = state \
|
||||||
|
next = findNext state.searchQuery state.lines state.cursorRow state.cursorCol;
|
||||||
|
newState = autoScroll state.{
|
||||||
|
mode = Normal,
|
||||||
|
cursorCol = next.col,
|
||||||
|
cursorRow = next.row,
|
||||||
|
};
|
||||||
|
|
||||||
|
{ state = newState, emit = [] };
|
||||||
|
|
||||||
scrollHalfUp = state ctx \ (
|
scrollHalfUp = state ctx \ (
|
||||||
diff = floor ((ctx.h / 2) / lineH);
|
diff = floor ((ctx.h / 2) / lineH);
|
||||||
newRow = state.cursorRow - diff;
|
newRow = state.cursorRow - diff;
|
||||||
|
|
@ -295,19 +361,30 @@ textEditor = name \
|
||||||
key = "textEditor-" & name,
|
key = "textEditor-" & name,
|
||||||
|
|
||||||
init = {
|
init = {
|
||||||
|
mode = Normal, # Normal | Insert | Visual | Search
|
||||||
|
|
||||||
lines = lines,
|
lines = lines,
|
||||||
cursorRow = 0,
|
cursorRow = 0,
|
||||||
cursorCol = 0,
|
cursorCol = 0,
|
||||||
scrollX = 0,
|
scrollX = 0,
|
||||||
scrollY = 0,
|
scrollY = 0,
|
||||||
|
|
||||||
|
pending = None, # Some "d" | Some "g" | etc.
|
||||||
|
lastAction = None, # Some { operator, motion },
|
||||||
undoStack = [],
|
undoStack = [],
|
||||||
redoStack = [],
|
redoStack = [],
|
||||||
mode = Normal, # Normal | Insert | Visual
|
|
||||||
pending = None, # Some "d" | Some "g" | etc.
|
searchQuery = "",
|
||||||
lastAction = None, # Some { operator, motion }
|
searchIndex = 0,
|
||||||
},
|
},
|
||||||
|
|
||||||
update = state event \ state.mode
|
update = state event \ state.mode
|
||||||
|
| Search \ (event
|
||||||
|
| Key { key = "Escape" } \ cancelSearch state
|
||||||
|
| Key { key = "Enter" } \ endSearch state
|
||||||
|
| Key { key = "Backspace" } \ backspaceSearch state
|
||||||
|
| Key { key = k, printable = True } \ addCharToSearchQuery k state
|
||||||
|
| _ \ { state = state , emit = [] })
|
||||||
| Insert \ (event
|
| Insert \ (event
|
||||||
| Key { key = "Escape" } \ escape state
|
| Key { key = "Escape" } \ escape state
|
||||||
| Key { key = "Control" } \ escape state
|
| Key { key = "Control" } \ escape state
|
||||||
|
|
@ -352,8 +429,13 @@ textEditor = name \
|
||||||
| _ \ { state = state.{ pending = Some "g" }, emit = [] })
|
| _ \ { state = state.{ pending = Some "g" }, emit = [] })
|
||||||
| Key { key = "G" } \ handleMotion LastLine state
|
| Key { key = "G" } \ handleMotion LastLine state
|
||||||
| Key { key = "." } \ (state.lastAction
|
| Key { key = "." } \ (state.lastAction
|
||||||
| None \ { state = state, emit = [] }
|
| None \ { state = state, emit = [] }
|
||||||
| Some action \ applyOperator action.operator action.motion state)
|
| Some action \ applyOperator action.operator action.motion state)
|
||||||
|
# Search
|
||||||
|
| Key { key = "/" } \ initSearch state
|
||||||
|
| Key { key = "n" } \ nextSearch state
|
||||||
|
| Key { key = "N" } \ prevSearch state
|
||||||
|
|
||||||
| Key { key = "W", ctrl = True, shift = True } \ write state
|
| Key { key = "W", ctrl = True, shift = True } \ write state
|
||||||
| Key { key = "A", ctrl = True, shift = True } \ apply state
|
| Key { key = "A", ctrl = True, shift = True } \ apply state
|
||||||
# any other key or event
|
# any other key or event
|
||||||
|
|
@ -374,6 +456,22 @@ textEditor = name \
|
||||||
totalWidth = maxLineLen * charW * scale + maxLineLen * charGap;
|
totalWidth = maxLineLen * charW * scale + maxLineLen * charGap;
|
||||||
totalHeight = len state.lines * lineH;
|
totalHeight = len state.lines * lineH;
|
||||||
|
|
||||||
|
highlights = state.searchQuery == ""
|
||||||
|
| True \ []
|
||||||
|
| False \ findAll state.searchQuery state.lines;
|
||||||
|
|
||||||
|
highlightsUi = map (c \
|
||||||
|
ui.positioned {
|
||||||
|
x = c.col * charW * scale + charGap * c.col,
|
||||||
|
y = c.row * lineH,
|
||||||
|
child = ui.rect {
|
||||||
|
w = (len state.searchQuery) * charW * scale + charGap * (len state.searchQuery - 1),
|
||||||
|
h = charH * scale,
|
||||||
|
color = "rgba(255,200,0,0.5)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) highlights;
|
||||||
|
|
||||||
# buffer = map (l \ text l) state.lines;
|
# buffer = map (l \ text l) state.lines;
|
||||||
# only render visible lines
|
# only render visible lines
|
||||||
startLine = max 0 (floor (state.scrollY / lineH));
|
startLine = max 0 (floor (state.scrollY / lineH));
|
||||||
|
|
@ -399,7 +497,8 @@ textEditor = name \
|
||||||
child = ui.stack {
|
child = ui.stack {
|
||||||
children = [
|
children = [
|
||||||
...buffer,
|
...buffer,
|
||||||
cursor
|
cursor,
|
||||||
|
...highlightsUi
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,14 @@ export const _rt = {
|
||||||
return String(value);
|
return String(value);
|
||||||
},
|
},
|
||||||
chars: (s: string) => s.split(''),
|
chars: (s: string) => s.split(''),
|
||||||
|
strIndexOf: (needle: string) => (haystack: string) => (start: number) => {
|
||||||
|
const i = haystack.indexOf(needle, start);
|
||||||
|
return i === -1 ? { _tag: 'None' } : { _tag: 'Some', _0: i };
|
||||||
|
},
|
||||||
|
strLastIndexOf: (needle: string) => (haystack: string) => {
|
||||||
|
const i = haystack.lastIndexOf(needle);
|
||||||
|
return i === -1 ? { _tag: 'None' } : { _tag: 'Some', _0: i };
|
||||||
|
},
|
||||||
isWordChar: (s: string) => ({ _tag: /^\w$/.test(s) ? 'True' : 'False' }),
|
isWordChar: (s: string) => ({ _tag: /^\w$/.test(s) ? 'True' : 'False' }),
|
||||||
// join: (delim: string) => (xs: string[]) => xs.join(delim),
|
// join: (delim: string) => (xs: string[]) => xs.join(delim),
|
||||||
split: (delim: string) => (xs: string) => xs.split(delim),
|
split: (delim: string) => (xs: string) => xs.split(delim),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue