Do basic highlighting of search results, finish stop #151 of the kilo tutorial
Some checks failed
timw4mail/scroll/pipeline/head There was a failure building this commit
Some checks failed
timw4mail/scroll/pipeline/head There was a failure building this commit
This commit is contained in:
parent
faf59b4235
commit
e6b53ef327
8
justfile
8
justfile
@ -30,9 +30,9 @@ clean:
|
|||||||
rm -f scroll.err
|
rm -f scroll.err
|
||||||
rm -f tsconfig.tsbuildinfo
|
rm -f tsconfig.tsbuildinfo
|
||||||
|
|
||||||
########################################################################################################################
|
##########################################################################################
|
||||||
# Bun-specific commands
|
# Bun-specific commands
|
||||||
########################################################################################################################
|
##########################################################################################
|
||||||
|
|
||||||
# Check code with actual Typescript compiler
|
# Check code with actual Typescript compiler
|
||||||
bun-check:
|
bun-check:
|
||||||
@ -46,9 +46,9 @@ bun-test:
|
|||||||
bun-run file="":
|
bun-run file="":
|
||||||
bun run ./src/scroll.ts {{file}}
|
bun run ./src/scroll.ts {{file}}
|
||||||
|
|
||||||
########################################################################################################################
|
##########################################################################################
|
||||||
# Deno-specific commands
|
# Deno-specific commands
|
||||||
########################################################################################################################
|
##########################################################################################
|
||||||
|
|
||||||
# Lint code and check types
|
# Lint code and check types
|
||||||
deno-check:
|
deno-check:
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import Row from './row.ts';
|
import Row from './row.ts';
|
||||||
import { arrayInsert } from './fns.ts';
|
import { arrayInsert, strlen } from './fns.ts';
|
||||||
|
import { HighlightType } from './highlight.ts';
|
||||||
import { getRuntime } from './runtime.ts';
|
import { getRuntime } from './runtime.ts';
|
||||||
import { Position } from './types.ts';
|
import { Position } from './types.ts';
|
||||||
import { Search } from './search.ts';
|
import { Search } from './search.ts';
|
||||||
@ -74,7 +75,26 @@ export class Document {
|
|||||||
q: string,
|
q: string,
|
||||||
key: string,
|
key: string,
|
||||||
): Position | null {
|
): Position | null {
|
||||||
return this.#search.search(q, key);
|
const potential = this.#search.search(q, key);
|
||||||
|
if (potential !== null) {
|
||||||
|
// Update highlight of search match
|
||||||
|
const row = this.#rows[potential.y];
|
||||||
|
|
||||||
|
// Okay, we have to take the Javascript string index (potential.x), convert
|
||||||
|
// it to the Row character index, and then convert that to the Row render index
|
||||||
|
// so that the highlighted color starts in the right place.
|
||||||
|
const start = row.cxToRx(row.byteIndexToCharIndex(potential.x));
|
||||||
|
|
||||||
|
// Just to be safe with unicode searches, take the number of 'characters'
|
||||||
|
// as the search query length, not the JS string length.
|
||||||
|
const end = start + strlen(q);
|
||||||
|
|
||||||
|
for (let i = start; i < end; i++) {
|
||||||
|
row.hl[i] = HighlightType.Match;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return potential;
|
||||||
}
|
}
|
||||||
|
|
||||||
public insert(at: Position, c: string): void {
|
public insert(at: Position, c: string): void {
|
||||||
|
@ -295,7 +295,8 @@ class Editor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find text within the document
|
* Find text within the document. This is roughly equivalent to the
|
||||||
|
* `editorFindCallback` function in the kilo tutorial.
|
||||||
*/
|
*/
|
||||||
public async find(): Promise<void> {
|
public async find(): Promise<void> {
|
||||||
const savedCursor = Position.from(this.#cursor);
|
const savedCursor = Position.from(this.#cursor);
|
||||||
@ -314,6 +315,7 @@ class Editor {
|
|||||||
if (query !== null && query.length > 0) {
|
if (query !== null && query.length > 0) {
|
||||||
const pos = this.#document.find(query, key);
|
const pos = this.#document.find(query, key);
|
||||||
if (pos !== null) {
|
if (pos !== null) {
|
||||||
|
// We have a match here
|
||||||
this.#cursor = pos;
|
this.#cursor = pos;
|
||||||
this.scroll();
|
this.scroll();
|
||||||
} else {
|
} else {
|
||||||
|
18
src/common/position.ts
Normal file
18
src/common/position.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* Convenience type for (x,y) coordinate values
|
||||||
|
*/
|
||||||
|
export class Position {
|
||||||
|
private constructor(public x: number = 0, public y: number = 0) {}
|
||||||
|
|
||||||
|
public static at(x: number, y: number): Position {
|
||||||
|
return new Position(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static from(p: Position): Position {
|
||||||
|
return new Position(p.x, p.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static default(): Position {
|
||||||
|
return new Position();
|
||||||
|
}
|
||||||
|
}
|
@ -12,18 +12,18 @@ export class Row {
|
|||||||
/**
|
/**
|
||||||
* The actual characters in the current row
|
* The actual characters in the current row
|
||||||
*/
|
*/
|
||||||
chars: string[] = [];
|
public chars: string[] = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The characters rendered for the current row
|
* The characters rendered for the current row
|
||||||
* (like replacing tabs with spaces)
|
* (like replacing tabs with spaces)
|
||||||
*/
|
*/
|
||||||
rchars: string[] = [];
|
public rchars: string[] = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The syntax highlighting map
|
* The syntax highlighting map
|
||||||
*/
|
*/
|
||||||
hl: HighlightType[] = [];
|
public hl: HighlightType[] = [];
|
||||||
|
|
||||||
private constructor(s: string | string[] = '') {
|
private constructor(s: string | string[] = '') {
|
||||||
this.chars = Array.isArray(s) ? s : strChars(s);
|
this.chars = Array.isArray(s) ? s : strChars(s);
|
||||||
@ -84,6 +84,10 @@ export class Row {
|
|||||||
this.chars.splice(at, 1);
|
this.chars.splice(at, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search the current row for the specified string, and return
|
||||||
|
* the index of the start of that match
|
||||||
|
*/
|
||||||
public find(s: string, offset: number = 0): number | null {
|
public find(s: string, offset: number = 0): number | null {
|
||||||
const thisStr = this.toString();
|
const thisStr = this.toString();
|
||||||
if (!this.toString().includes(s)) {
|
if (!this.toString().includes(s)) {
|
||||||
|
@ -49,13 +49,22 @@ export class Search {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public search(q: string, key: string): Position | null {
|
public search(q: string, key: string): Position | null {
|
||||||
|
if (this.parent === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
this.parseInput(key);
|
this.parseInput(key);
|
||||||
|
|
||||||
let i = 0;
|
let i = 0;
|
||||||
for (; i < this.parent!.numRows; i++) {
|
for (; i < this.parent.numRows; i++) {
|
||||||
const current = this.getNextRow(this.parent!.numRows);
|
const current = this.getNextRow(this.parent.numRows);
|
||||||
|
const row = this.parent.row(current);
|
||||||
|
|
||||||
const possible = this.parent!.row(current)!.find(q);
|
if (row === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const possible = row.find(q);
|
||||||
if (possible !== null) {
|
if (possible !== null) {
|
||||||
this.lastMatch = current;
|
this.lastMatch = current;
|
||||||
return Position.at(possible, current);
|
return Position.at(possible, current);
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import { RunTimeType } from './runtime.ts';
|
import { RunTimeType } from './runtime.ts';
|
||||||
|
|
||||||
|
export { Position } from './position.ts';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The size of terminal in rows and columns
|
* The size of terminal in rows and columns
|
||||||
*/
|
*/
|
||||||
@ -100,32 +102,6 @@ export type ITerminal = IRuntime['term'];
|
|||||||
*/
|
*/
|
||||||
export type IFileIO = IRuntime['file'];
|
export type IFileIO = IRuntime['file'];
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// General types
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
export class Position {
|
|
||||||
public x: number;
|
|
||||||
public y: number;
|
|
||||||
|
|
||||||
private constructor(x: number = 0, y: number = 0) {
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static at(x: number, y: number): Position {
|
|
||||||
return new Position(x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static from(p: Position): Position {
|
|
||||||
return new Position(p.x, p.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static default(): Position {
|
|
||||||
return new Position();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Testing
|
// Testing
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
Loading…
Reference in New Issue
Block a user