Skip to content

Commit

Permalink
feat(model): add document.lineAt function (#3520)
Browse files Browse the repository at this point in the history
* feat(model): add document.lineAt function

* feat(model): add document.lineAt function

* feat(typings): add TextLine and lineAt

* test(modules): lineAt tests

* fix(index): export TextLine class

Co-authored-by: Qiming Zhao <chemzqm@gmail.com>
  • Loading branch information
fannheyward and chemzqm authored Jan 12, 2022
1 parent c0b8a83 commit 34fae82
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 1 deletion.
12 changes: 12 additions & 0 deletions src/__tests__/modules/textdocument.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,18 @@ describe('LinesTextDocument', () => {
expect(content).toBe('a\nb\n')
})

it('should get text by line', async () => {
const doc = createTextDocument(['foo', 'bar'])
const textLine = doc.lineAt(0)
expect(textLine.text).toBe('foo')
})

it('should get text by position', async () => {
const doc = createTextDocument(['foo', 'bar'])
const textLine = doc.lineAt(Position.create(0, 3))
expect(textLine.text).toBe('foo')
})

it('should get position', async () => {
let doc = createTextDocument(['foo', 'bar'])
let pos = doc.positionAt(4)
Expand Down
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import FloatFactory from './model/floatFactory'
import fetch from './model/fetch'
import download from './model/download'
import Highligher from './model/highligher'
import { TextLine } from './model/textdocument'
import services from './services'
import sources from './sources/index'
import workspace from './workspace'
Expand Down Expand Up @@ -67,7 +68,7 @@ export {
} from './language-client'

export { Neovim, MarkupKind, DiagnosticTag, DocumentHighlightKind, SymbolKind, SignatureHelpTriggerKind, FileChangeType, CodeActionKind, Diagnostic, DiagnosticSeverity, CompletionItemKind, InsertTextFormat, Location, LocationLink, CancellationTokenSource, CancellationToken, ProgressType, Position, Range, TextEdit, RequestType, RequestType0, NotificationType, NotificationType0, Buffer, Window, Highligher, Mru, Watchman, URI as Uri, Disposable, Event, Emitter, FloatFactory, fetch, download, ansiparse }
export { workspace, window, CompletionTriggerKind, snippetManager, SnippetString, events, services, commands, sources, languages, diagnosticManager, extensions, listManager, BasicList, Mutex }
export { workspace, window, CompletionTriggerKind, snippetManager, SnippetString, events, services, commands, sources, languages, diagnosticManager, extensions, listManager, BasicList, Mutex, TextLine }
export { disposeAll, concurrent, watchFile, wait, runCommand, isRunning, executable } from './util'
export { TreeItem, TreeItemCollapsibleState } from './tree/index'
export { SemanticTokensBuilder } from './semanticTokensBuilder'
72 changes: 72 additions & 0 deletions src/model/textdocument.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,69 @@ function computeLineOffsets(text: string, isAtLineStart: boolean, textOffset = 0
}
return result
}
/**
* Represents a line of text, such as a line of source code.
*
* TextLine objects are __immutable__. When a {@link TextDocument document} changes,
* previously retrieved lines will not represent the latest state.
*/
export class TextLine {

private readonly _line: number
private readonly _text: string
private readonly _isLastLine: boolean

constructor(line: number, text: string, isLastLine: boolean) {
this._line = line
this._text = text
this._isLastLine = isLastLine
}

/**
* The zero-based line number.
*/
public get lineNumber(): number {
return this._line
}

/**
* The text of this line without the line separator characters.
*/
public get text(): string {
return this._text
}

/**
* The range this line covers without the line separator characters.
*/
public get range(): Range {
return Range.create(this._line, 0, this._line, this._text.length)
}

/**
* The range this line covers with the line separator characters.
*/
public get rangeIncludingLineBreak(): Range {
return this._isLastLine ? this.range : Range.create(this._line, 0, this._line + 1, 0)
}

/**
* The offset of the first character which is not a whitespace character as defined
* by `/\s/`. **Note** that if a line is all whitespace the length of the line is returned.
*/
public get firstNonWhitespaceCharacterIndex(): number {
// TODO@api, rename to 'leadingWhitespaceLength'
return /^(\s*)/.exec(this._text)![1].length
}

/**
* Whether this line is whitespace only, shorthand
* for {@link TextLine.firstNonWhitespaceCharacterIndex} === {@link TextLine.text TextLine.text.length}.
*/
public get isEmptyOrWhitespace(): boolean {
return this.firstNonWhitespaceCharacterIndex === this._text.length
}
}

/**
* Text document that created with readonly lines.
Expand Down Expand Up @@ -49,6 +112,15 @@ export class LinesTextDocument implements TextDocument {
return this._content
}

public lineAt(lineOrPos: number | Position): TextLine {
const line = Position.is(lineOrPos) ? lineOrPos.line : lineOrPos
if (typeof line !== 'number' || line < 0 || line >= this.lines.length || Math.floor(line) !== line) {
throw new Error('Illegal value for `line`')
}

return new TextLine(line, this.lines[line], line === this.lines.length - 1)
}

public positionAt(offset: number): Position {
offset = Math.max(Math.min(offset, this._content.length), 0)
let lineOffsets = this.getLineOffsets()
Expand Down
51 changes: 51 additions & 0 deletions typings/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1756,6 +1756,48 @@ declare module 'coc.nvim' {
dispose(): void
}

/**
* Represents a line of text, such as a line of source code.
*
* TextLine objects are __immutable__. When a {@link TextDocument document} changes,
* previously retrieved lines will not represent the latest state.
*/
export class TextLine {
constructor(line: number, text: string, isLastLine: boolean)

/**
* The zero-based line number.
*/
get lineNumber(): number

/**
* The text of this line without the line separator characters.
*/
get text(): string

/**
* The range this line covers without the line separator characters.
*/
get range(): Range

/**
* The range this line covers with the line separator characters.
*/
get rangeIncludingLineBreak(): Range

/**
* The offset of the first character which is not a whitespace character as defined
* by `/\s/`. **Note** that if a line is all whitespace the length of the line is returned.
*/
get firstNonWhitespaceCharacterIndex(): number

/**
* Whether this line is whitespace only, shorthand
* for {@link TextLine.firstNonWhitespaceCharacterIndex} === {@link TextLine.text TextLine.text.length}.
*/
get isEmptyOrWhitespace(): boolean
}

/**
* A simple text document. Not to be implemented. The document keeps the content
* as string.
Expand Down Expand Up @@ -1797,6 +1839,15 @@ declare module 'coc.nvim' {
* range is provided.
*/
getText(range?: Range): string
/**
* Returns a text line denoted by the line number. Note
* that the returned object is *not* live and changes to the
* document are not reflected.
*
* @param line or position
* @return A {@link TextLine line}.
*/
lineAt(lineOrPos: number | Position): TextLine
/**
* Converts a zero-based offset to a position.
*
Expand Down

0 comments on commit 34fae82

Please sign in to comment.