Skip to content

Commit

Permalink
feat: using class Position instance instead of plain object
Browse files Browse the repository at this point in the history
  • Loading branch information
weirongxu committed Mar 28, 2022
1 parent b83595f commit 5897774
Show file tree
Hide file tree
Showing 9 changed files with 214 additions and 18 deletions.
2 changes: 1 addition & 1 deletion src/edit/location.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export class Location implements ILocation {
return false
}
return Range.isRange((thing as Location).range)
&& URI.isUri((thing as Location).uri)
&& URI.isUri((thing as Location).uri)
}

/**
Expand Down
17 changes: 17 additions & 0 deletions src/edit/position.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,23 @@ export class Position implements IPosition {
throw new Error('Invalid argument, is NOT a position-like object')
}

/**
* Creates a new Position literal from the given line and character.
*
* @param line The position's line.
* @param character The position's character.
*/
public static create(line: number, character: number): Position {
return new Position(line, character)
}

/**
* Checks whether the given liternal conforms to the [Position](#Position) interface.
*/
public static is(value: any): value is IPosition {
return IPosition.is(value)
}

private _line: number
private _character: number

Expand Down
4 changes: 2 additions & 2 deletions src/edit/range.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export class Range implements IRange {
return false
}
return Position.isPosition((thing as Range).start)
&& Position.isPosition(thing.end)
&& Position.isPosition(thing.end)
}

public static of(obj: IRange): Range {
Expand Down Expand Up @@ -101,7 +101,7 @@ export class Range implements IRange {
public contains(positionOrRange: Position | Range): boolean {
if (Range.isRange(positionOrRange)) {
return this.contains(positionOrRange.start)
&& this.contains(positionOrRange.end)
&& this.contains(positionOrRange.end)

} else if (Position.isPosition(positionOrRange)) {
if (Position.of(positionOrRange).isBefore(this._start)) {
Expand Down
6 changes: 3 additions & 3 deletions src/edit/selection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ export class Selection extends Range {
return false
}
return Range.isRange(thing)
&& Position.isPosition((thing as Selection).anchor)
&& Position.isPosition((thing as Selection).active)
&& typeof (thing as Selection).isReversed === 'boolean'
&& Position.isPosition((thing as Selection).anchor)
&& Position.isPosition((thing as Selection).active)
&& typeof (thing as Selection).isReversed === 'boolean'
}

private _anchor: Position
Expand Down
2 changes: 1 addition & 1 deletion src/edit/textEdit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export class TextEdit implements ITextEdit {
return false
}
return Range.isRange((thing as TextEdit))
&& typeof (thing as TextEdit).newText === 'string'
&& typeof (thing as TextEdit).newText === 'string'
}

public static replace(range: Range, newText: string): TextEdit {
Expand Down
177 changes: 177 additions & 0 deletions src/edit/workspaceEdit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
/* ---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { URI } from "vscode-uri"
import { Position } from "./position"
import { Range } from "./range"
import { TextEdit } from "./textEdit"

/**
* Remove all falsy values from `array`. The original array IS modified.
*/
function coalesceInPlace<T>(array: Array<T | undefined | null>): void {
let to = 0
for (let i = 0; i < array.length; i++) {
if (array[i]) {
array[to] = array[i]
to += 1
}
}
array.length = to
}

/**
* Additional data for entries of a workspace edit. Supports to label entries and marks entries
* as needing confirmation by the user. The editor groups edits with equal labels into tree nodes,
* for instance all edits labelled with "Changes in Strings" would be a tree node.
*/
export interface WorkspaceEditEntryMetadata {

/**
* A flag which indicates that user confirmation is needed.
*/
needsConfirmation: boolean;

/**
* A human-readable string which is rendered prominent.
*/
label: string;

/**
* A human-readable string which is rendered less prominent on the same line.
*/
description?: string;

/**
* The icon path or {@link ThemeIcon} for the edit.
*/
iconPath?: URI | { light: URI; dark: URI };
}

export interface IFileOperationOptions {
overwrite?: boolean;
ignoreIfExists?: boolean;
ignoreIfNotExists?: boolean;
recursive?: boolean;
}

export const enum FileEditType {
File = 1,
Text = 2,
Cell = 3,
CellReplace = 5,
}

export interface IFileOperation {
_type: FileEditType.File;
from?: URI;
to?: URI;
options?: IFileOperationOptions;
metadata?: WorkspaceEditEntryMetadata;
}

export interface IFileTextEdit {
_type: FileEditType.Text;
uri: URI;
edit: TextEdit;
metadata?: WorkspaceEditEntryMetadata;
}

type WorkspaceEditEntry = IFileOperation | IFileTextEdit

export class WorkspaceEdit {

private readonly _edits: WorkspaceEditEntry[] = []

public _allEntries(): ReadonlyArray<WorkspaceEditEntry> {
return this._edits
}

// --- file

public renameFile(from: URI, to: URI, options?: { overwrite?: boolean; ignoreIfExists?: boolean }, metadata?: WorkspaceEditEntryMetadata): void {
this._edits.push({ _type: FileEditType.File, from, to, options, metadata })
}

public createFile(uri: URI, options?: { overwrite?: boolean; ignoreIfExists?: boolean }, metadata?: WorkspaceEditEntryMetadata): void {
this._edits.push({ _type: FileEditType.File, from: undefined, to: uri, options, metadata })
}

public deleteFile(uri: URI, options?: { recursive?: boolean; ignoreIfNotExists?: boolean }, metadata?: WorkspaceEditEntryMetadata): void {
this._edits.push({ _type: FileEditType.File, from: uri, to: undefined, options, metadata })
}

// --- text

public replace(uri: URI, range: Range, newText: string, metadata?: WorkspaceEditEntryMetadata): void {
this._edits.push({ _type: FileEditType.Text, uri, edit: new TextEdit(range, newText), metadata })
}

public insert(resource: URI, position: Position, newText: string, metadata?: WorkspaceEditEntryMetadata): void {
this.replace(resource, new Range(position, position), newText, metadata)
}

public delete(resource: URI, range: Range, metadata?: WorkspaceEditEntryMetadata): void {
this.replace(resource, range, '', metadata)
}

// --- text (Maplike)

public has(uri: URI): boolean {
return this._edits.some(edit => edit._type === FileEditType.Text && edit.uri.toString() === uri.toString())
}

public set(uri: URI, edits: TextEdit[]): void {
if (!edits) {
// remove all text edits for `uri`
for (let i = 0; i < this._edits.length; i++) {
const element = this._edits[i]
if (element._type === FileEditType.Text && element.uri.toString() === uri.toString()) {
this._edits[i] = undefined! // will be coalesced down below
}
}
coalesceInPlace(this._edits)
} else {
// append edit to the end
for (const edit of edits) {
if (edit) {
this._edits.push({ _type: FileEditType.Text, uri, edit })
}
}
}
}

public get(uri: URI): TextEdit[] {
const res: TextEdit[] = []
for (let candidate of this._edits) {
if (candidate._type === FileEditType.Text && candidate.uri.toString() === uri.toString()) {
res.push(candidate.edit)
}
}
return res
}

public entries(): [URI, TextEdit[]][] {
const textEdits = new ResourceMap<[URI, TextEdit[]]>()
for (let candidate of this._edits) {
if (candidate._type === FileEditType.Text) {
let textEdit = textEdits.get(candidate.uri)
if (!textEdit) {
textEdit = [candidate.uri, []]
textEdits.set(candidate.uri, textEdit)
}
textEdit[1].push(candidate.edit)
}
}
return [...textEdits.values()]
}

public get size(): number {
return this.entries().length
}

public toJSON(): any {
return this.entries()
}
}
1 change: 0 additions & 1 deletion src/markdown/baseMarkdownString.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ export const enum MarkdownStringTextNewlineStyle {
}

export class BaseMarkdownString implements IMarkdownString {

public value: string
public isTrusted?: boolean
public supportThemeIcons?: boolean
Expand Down
13 changes: 7 additions & 6 deletions src/model/textdocument.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Position, Range } from 'vscode-languageserver-protocol'
import { Position as IPosition, Range } from 'vscode-languageserver-protocol'
import { TextDocument } from 'vscode-languageserver-textdocument'
import { Position } from '../edit/position'

function computeLineOffsets(text: string, isAtLineStart: boolean, textOffset = 0): number[] {
const result: number[] = isAtLineStart ? [textOffset] : []
Expand Down Expand Up @@ -117,8 +118,8 @@ export class LinesTextDocument implements TextDocument {
return this._content
}

public lineAt(lineOrPos: number | Position): TextLine {
const line = Position.is(lineOrPos) ? lineOrPos.line : lineOrPos
public lineAt(lineOrPos: number | IPosition): TextLine {
const line = IPosition.is(lineOrPos) ? lineOrPos.line : lineOrPos
if (typeof line !== 'number' ||
line < 0 ||
line >= this.lineCount ||
Expand All @@ -135,7 +136,7 @@ export class LinesTextDocument implements TextDocument {
let low = 0
let high = lineOffsets.length
if (high === 0) {
return { line: 0, character: offset }
return new Position(0, offset)
}
while (low < high) {
let mid = Math.floor((low + high) / 2)
Expand All @@ -148,10 +149,10 @@ export class LinesTextDocument implements TextDocument {
// low is the least x for which the line offset is larger than the current offset
// or array.length if no line offset is larger than the current offset
let line = low - 1
return { line, character: offset - lineOffsets[line] }
return new Position(line, offset - lineOffsets[line])
}

public offsetAt(position: Position) {
public offsetAt(position: IPosition) {
let lineOffsets = this.getLineOffsets()
if (position.line >= lineOffsets.length) {
return this._content.length
Expand Down
10 changes: 6 additions & 4 deletions src/window.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { Neovim } from '@chemzqm/neovim'
import fs from 'fs'
import path from 'path'
import { CancellationToken, Disposable, Emitter, Event, Position, Range } from 'vscode-languageserver-protocol'
import { CancellationToken, Disposable, Emitter, Event, Position as IPosition, Range } from 'vscode-languageserver-protocol'
import { URI } from 'vscode-uri'
import channels from './core/channels'
import { TextEditor } from './core/editors'
import Terminals from './core/terminals'
import * as ui from './core/ui'
import { Position } from './edit/position'
import events from './events'
import Dialog, { DialogConfig, DialogPreferences } from './model/dialog'
import Menu, { isMenuItem, MenuItem } from './model/menu'
Expand Down Expand Up @@ -408,16 +409,17 @@ class Window {
*
* @returns Cursor position.
*/
public getCursorPosition(): Promise<Position> {
return ui.getCursorPosition(this.nvim)
public async getCursorPosition(): Promise<Position> {
const position = await ui.getCursorPosition(this.nvim)
return new Position(position.line, position.character)
}

/**
* Move cursor to position.
*
* @param position LSP position.
*/
public async moveTo(position: Position): Promise<void> {
public async moveTo(position: IPosition): Promise<void> {
await ui.moveTo(this.nvim, position, workspace.env.isVim)
}

Expand Down

0 comments on commit 5897774

Please sign in to comment.