Created
October 16, 2016 01:34
-
-
Save romgrk/7b155dcc273d46f125c22fe5de2b2c4f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
inspect, | |
createToken, Token} from '../common'; | |
let stringConcat = (prev: string, cur: string) => prev.concat(cur) | |
function tokensToString (tokens: Token[]) { | |
return tokens.map(t => t.text) | |
.reduce(stringConcat, ''); | |
} | |
function clone(object) { | |
return JSON.parse(JSON.stringify(object)); | |
} | |
export default class LineModel { | |
tokens: Token[]; | |
changed: () => void; | |
private _size: number; | |
constructor(size: number) { | |
this._size = size; | |
this.tokens = []; | |
this.changed = () => {}; | |
} | |
get content() { | |
let res = tokensToString(this.tokens); | |
if (!this._size) | |
return res; | |
if (res.length < this._size) | |
res += " ".repeat(this._size - res.length); | |
if (res.length > this._size) | |
res = res.substring(0, this._size); | |
return res; | |
} | |
set content(value: string) { | |
this.tokens = [{text: value}]; | |
} | |
public insert(pos: number, data: any) { | |
const newToken = createToken(data); | |
const n_tokens = this.tokens.length; | |
let start_index: number, | |
start_offset: number, | |
end_index: number, | |
end_offset: number; | |
let charOffset = 0; | |
for (let n = 0; n < n_tokens; n++) { | |
let token = this.tokens[n]; | |
let len = token.text.length; | |
charOffset += len; | |
if (start_index == undefined && charOffset >= pos) { | |
start_index = n; | |
start_offset = pos - (charOffset - len); | |
} | |
if (charOffset >= pos + newToken.text.length) { | |
end_index = n; | |
end_offset = (pos + newToken.text.length) - (charOffset - len); | |
break; | |
} | |
} | |
// inspect({pos, charOffset, start_index, start_offset, end_index, end_offset}) | |
if (start_index == undefined) { | |
if (charOffset < pos) { | |
this.tokens.push(createToken(pos - charOffset)); | |
} | |
start_index = this.tokens.length; | |
end_index = this.tokens.length; | |
} else if (end_index == undefined) { | |
start_index = this.splitAt(start_index, start_offset); | |
end_index = this.tokens.length; | |
} else if (start_index == end_index) { | |
end_offset -= start_offset; | |
start_index = this.splitAt(start_index, start_offset); | |
end_index = this.splitAt(start_index, end_offset); | |
} else { | |
start_index = this.splitAt(start_index, start_offset); | |
if (n_tokens != this.tokens.length) | |
end_index += 1; | |
end_index = this.splitAt(end_index, end_offset); | |
} | |
let delete_count = end_index - start_index; | |
// inspect({start_index, delete_count}, this.tokens) | |
this.tokens.splice(start_index, delete_count, newToken); | |
} | |
public prepend(...items: Token[]) { | |
for (let item of items) { | |
this.tokens.unshift(createToken(item)); | |
} | |
} | |
public append(...items: Token[]) { | |
for (let item of items) { | |
this.tokens.push(createToken(item)); | |
} | |
} | |
public slice(start: number, end?: number) { | |
const startIndex = this.splitNodeAt(start); | |
const endIndex = (end != undefined) ? this.splitNodeAt(end) : this.tokens.length; | |
return this.tokens | |
.slice(startIndex, endIndex) | |
.map(t => clone(t)); | |
} | |
/** | |
* Remove a range tokens. | |
* see Array.prototype.slice | |
* @param pos:number the zero-based character index | |
* @param count:number [defaults to line size] | |
* @param ...items:[] [replacement items] | |
* @returns the extracted tokens | |
*/ | |
public splice(pos: number, count = this._size - pos, ...items: Token[]) { | |
const startIndex = this.splitNodeAt(pos); | |
const endIndex = this.splitNodeAt(pos + count); | |
return this.tokens.splice( | |
startIndex, | |
endIndex - startIndex, | |
...items | |
); | |
} | |
/** | |
* Replace tokens in range with an empty token. | |
* @param pos:number the zero-based character index | |
* @param count:number [defaults to line size] | |
* @returns the extracted tokens | |
*/ | |
public extract(pos: number, count = this._size - pos, items?: Token[]) { | |
const startIndex = this.splitNodeAt(pos); | |
const endIndex = this.splitNodeAt(pos + count); | |
if (items == undefined) | |
items = [createToken(count)]; | |
return this.tokens.splice( | |
startIndex, | |
endIndex - startIndex, | |
...items | |
); | |
} | |
/** | |
* Asserts that the node at {@param offset} ends/begins there. | |
* @returns the index of the first token after split | |
*/ | |
private splitNodeAt(offset: number): number { | |
if (offset == 0) | |
return 0; | |
const n_tokens = this.tokens.length; | |
let charOffset = 0, | |
index: number, | |
innerOffset: number; | |
for (let n = 0; n < n_tokens; n++) { | |
let token = this.tokens[n]; | |
let len = token.text.length; | |
charOffset += len; | |
if (charOffset >= offset) { | |
index = n; | |
innerOffset = offset - (charOffset - len); | |
break; | |
} | |
} | |
if (index == undefined) | |
return n_tokens; | |
return this.splitAt(index, innerOffset); | |
} | |
/** | |
* Asserts that the node @param index is split at @param offset. | |
* @returns the index of the first token after split | |
*/ | |
private splitAt(index: number, offset: number) { | |
if (index > this.tokens.length) | |
return this.tokens.length; | |
if (offset == 0) | |
return index; | |
let token = this.tokens[index]; | |
let text = token.text; | |
if (offset >= text.length) | |
return index + 1; | |
this.tokens.splice(index, 0, clone(token)); | |
this.tokens[index].text = text.substring(0, offset); | |
this.tokens[index + 1].text = text.substring(offset); | |
return index + 1; | |
} | |
/* | |
* Section: Boring getter/setters | |
*/ | |
public setSize(value: number) { | |
if (value > 0) | |
this._size = value; | |
} | |
public getSize() { | |
return this._size; | |
} | |
public toString(): string { | |
return tokensToString(this.tokens); | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment