Skip to content

Commit df1c2b3

Browse files
committed
feat: using class Position instance instead of plain object
1 parent b6fca6a commit df1c2b3

File tree

9 files changed

+214
-18
lines changed

9 files changed

+214
-18
lines changed

src/edit/location.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export class Location implements ILocation {
1616
return false
1717
}
1818
return Range.isRange((thing as Location).range)
19-
&& URI.isUri((thing as Location).uri)
19+
&& URI.isUri((thing as Location).uri)
2020
}
2121

2222
/**

src/edit/position.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,23 @@ export class Position implements IPosition {
5959
throw new Error('Invalid argument, is NOT a position-like object')
6060
}
6161

62+
/**
63+
* Creates a new Position literal from the given line and character.
64+
*
65+
* @param line The position's line.
66+
* @param character The position's character.
67+
*/
68+
public static create(line: number, character: number): Position {
69+
return new Position(line, character)
70+
}
71+
72+
/**
73+
* Checks whether the given liternal conforms to the [Position](#Position) interface.
74+
*/
75+
public static is(value: any): value is IPosition {
76+
return IPosition.is(value)
77+
}
78+
6279
private _line: number
6380
private _character: number
6481

src/edit/range.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export class Range implements IRange {
1515
return false
1616
}
1717
return Position.isPosition((thing as Range).start)
18-
&& Position.isPosition(thing.end)
18+
&& Position.isPosition(thing.end)
1919
}
2020

2121
public static of(obj: IRange): Range {
@@ -101,7 +101,7 @@ export class Range implements IRange {
101101
public contains(positionOrRange: Position | Range): boolean {
102102
if (Range.isRange(positionOrRange)) {
103103
return this.contains(positionOrRange.start)
104-
&& this.contains(positionOrRange.end)
104+
&& this.contains(positionOrRange.end)
105105

106106
} else if (Position.isPosition(positionOrRange)) {
107107
if (Position.of(positionOrRange).isBefore(this._start)) {

src/edit/selection.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ export class Selection extends Range {
1414
return false
1515
}
1616
return Range.isRange(thing)
17-
&& Position.isPosition((thing as Selection).anchor)
18-
&& Position.isPosition((thing as Selection).active)
19-
&& typeof (thing as Selection).isReversed === 'boolean'
17+
&& Position.isPosition((thing as Selection).anchor)
18+
&& Position.isPosition((thing as Selection).active)
19+
&& typeof (thing as Selection).isReversed === 'boolean'
2020
}
2121

2222
private _anchor: Position

src/edit/textEdit.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export class TextEdit implements ITextEdit {
2727
return false
2828
}
2929
return Range.isRange((thing as TextEdit))
30-
&& typeof (thing as TextEdit).newText === 'string'
30+
&& typeof (thing as TextEdit).newText === 'string'
3131
}
3232

3333
public static replace(range: Range, newText: string): TextEdit {

src/edit/workspaceEdit.ts

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
/* ---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
import { URI } from "vscode-uri"
6+
import { Position } from "./position"
7+
import { Range } from "./range"
8+
import { TextEdit } from "./textEdit"
9+
10+
/**
11+
* Remove all falsy values from `array`. The original array IS modified.
12+
*/
13+
function coalesceInPlace<T>(array: Array<T | undefined | null>): void {
14+
let to = 0
15+
for (let i = 0; i < array.length; i++) {
16+
if (array[i]) {
17+
array[to] = array[i]
18+
to += 1
19+
}
20+
}
21+
array.length = to
22+
}
23+
24+
/**
25+
* Additional data for entries of a workspace edit. Supports to label entries and marks entries
26+
* as needing confirmation by the user. The editor groups edits with equal labels into tree nodes,
27+
* for instance all edits labelled with "Changes in Strings" would be a tree node.
28+
*/
29+
export interface WorkspaceEditEntryMetadata {
30+
31+
/**
32+
* A flag which indicates that user confirmation is needed.
33+
*/
34+
needsConfirmation: boolean;
35+
36+
/**
37+
* A human-readable string which is rendered prominent.
38+
*/
39+
label: string;
40+
41+
/**
42+
* A human-readable string which is rendered less prominent on the same line.
43+
*/
44+
description?: string;
45+
46+
/**
47+
* The icon path or {@link ThemeIcon} for the edit.
48+
*/
49+
iconPath?: URI | { light: URI; dark: URI };
50+
}
51+
52+
export interface IFileOperationOptions {
53+
overwrite?: boolean;
54+
ignoreIfExists?: boolean;
55+
ignoreIfNotExists?: boolean;
56+
recursive?: boolean;
57+
}
58+
59+
export const enum FileEditType {
60+
File = 1,
61+
Text = 2,
62+
Cell = 3,
63+
CellReplace = 5,
64+
}
65+
66+
export interface IFileOperation {
67+
_type: FileEditType.File;
68+
from?: URI;
69+
to?: URI;
70+
options?: IFileOperationOptions;
71+
metadata?: WorkspaceEditEntryMetadata;
72+
}
73+
74+
export interface IFileTextEdit {
75+
_type: FileEditType.Text;
76+
uri: URI;
77+
edit: TextEdit;
78+
metadata?: WorkspaceEditEntryMetadata;
79+
}
80+
81+
type WorkspaceEditEntry = IFileOperation | IFileTextEdit
82+
83+
export class WorkspaceEdit {
84+
85+
private readonly _edits: WorkspaceEditEntry[] = []
86+
87+
public _allEntries(): ReadonlyArray<WorkspaceEditEntry> {
88+
return this._edits
89+
}
90+
91+
// --- file
92+
93+
public renameFile(from: URI, to: URI, options?: { overwrite?: boolean; ignoreIfExists?: boolean }, metadata?: WorkspaceEditEntryMetadata): void {
94+
this._edits.push({ _type: FileEditType.File, from, to, options, metadata })
95+
}
96+
97+
public createFile(uri: URI, options?: { overwrite?: boolean; ignoreIfExists?: boolean }, metadata?: WorkspaceEditEntryMetadata): void {
98+
this._edits.push({ _type: FileEditType.File, from: undefined, to: uri, options, metadata })
99+
}
100+
101+
public deleteFile(uri: URI, options?: { recursive?: boolean; ignoreIfNotExists?: boolean }, metadata?: WorkspaceEditEntryMetadata): void {
102+
this._edits.push({ _type: FileEditType.File, from: uri, to: undefined, options, metadata })
103+
}
104+
105+
// --- text
106+
107+
public replace(uri: URI, range: Range, newText: string, metadata?: WorkspaceEditEntryMetadata): void {
108+
this._edits.push({ _type: FileEditType.Text, uri, edit: new TextEdit(range, newText), metadata })
109+
}
110+
111+
public insert(resource: URI, position: Position, newText: string, metadata?: WorkspaceEditEntryMetadata): void {
112+
this.replace(resource, new Range(position, position), newText, metadata)
113+
}
114+
115+
public delete(resource: URI, range: Range, metadata?: WorkspaceEditEntryMetadata): void {
116+
this.replace(resource, range, '', metadata)
117+
}
118+
119+
// --- text (Maplike)
120+
121+
public has(uri: URI): boolean {
122+
return this._edits.some(edit => edit._type === FileEditType.Text && edit.uri.toString() === uri.toString())
123+
}
124+
125+
public set(uri: URI, edits: TextEdit[]): void {
126+
if (!edits) {
127+
// remove all text edits for `uri`
128+
for (let i = 0; i < this._edits.length; i++) {
129+
const element = this._edits[i]
130+
if (element._type === FileEditType.Text && element.uri.toString() === uri.toString()) {
131+
this._edits[i] = undefined! // will be coalesced down below
132+
}
133+
}
134+
coalesceInPlace(this._edits)
135+
} else {
136+
// append edit to the end
137+
for (const edit of edits) {
138+
if (edit) {
139+
this._edits.push({ _type: FileEditType.Text, uri, edit })
140+
}
141+
}
142+
}
143+
}
144+
145+
public get(uri: URI): TextEdit[] {
146+
const res: TextEdit[] = []
147+
for (let candidate of this._edits) {
148+
if (candidate._type === FileEditType.Text && candidate.uri.toString() === uri.toString()) {
149+
res.push(candidate.edit)
150+
}
151+
}
152+
return res
153+
}
154+
155+
public entries(): [URI, TextEdit[]][] {
156+
const textEdits = new ResourceMap<[URI, TextEdit[]]>()
157+
for (let candidate of this._edits) {
158+
if (candidate._type === FileEditType.Text) {
159+
let textEdit = textEdits.get(candidate.uri)
160+
if (!textEdit) {
161+
textEdit = [candidate.uri, []]
162+
textEdits.set(candidate.uri, textEdit)
163+
}
164+
textEdit[1].push(candidate.edit)
165+
}
166+
}
167+
return [...textEdits.values()]
168+
}
169+
170+
public get size(): number {
171+
return this.entries().length
172+
}
173+
174+
public toJSON(): any {
175+
return this.entries()
176+
}
177+
}

src/markdown/baseMarkdownString.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ export const enum MarkdownStringTextNewlineStyle {
2121
}
2222

2323
export class BaseMarkdownString implements IMarkdownString {
24-
2524
public value: string
2625
public isTrusted?: boolean
2726
public supportThemeIcons?: boolean

src/model/textdocument.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { Position, Range } from 'vscode-languageserver-protocol'
1+
import { Position as IPosition, Range } from 'vscode-languageserver-protocol'
22
import { TextDocument } from 'vscode-languageserver-textdocument'
3+
import { Position } from '../edit/position'
34

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

120-
public lineAt(lineOrPos: number | Position): TextLine {
121-
const line = Position.is(lineOrPos) ? lineOrPos.line : lineOrPos
121+
public lineAt(lineOrPos: number | IPosition): TextLine {
122+
const line = IPosition.is(lineOrPos) ? lineOrPos.line : lineOrPos
122123
if (typeof line !== 'number' ||
123124
line < 0 ||
124125
line >= this.lineCount ||
@@ -135,7 +136,7 @@ export class LinesTextDocument implements TextDocument {
135136
let low = 0
136137
let high = lineOffsets.length
137138
if (high === 0) {
138-
return { line: 0, character: offset }
139+
return new Position(0, offset)
139140
}
140141
while (low < high) {
141142
let mid = Math.floor((low + high) / 2)
@@ -148,10 +149,10 @@ export class LinesTextDocument implements TextDocument {
148149
// low is the least x for which the line offset is larger than the current offset
149150
// or array.length if no line offset is larger than the current offset
150151
let line = low - 1
151-
return { line, character: offset - lineOffsets[line] }
152+
return new Position(line, offset - lineOffsets[line])
152153
}
153154

154-
public offsetAt(position: Position) {
155+
public offsetAt(position: IPosition) {
155156
let lineOffsets = this.getLineOffsets()
156157
if (position.line >= lineOffsets.length) {
157158
return this._content.length

src/window.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { Neovim } from '@chemzqm/neovim'
22
import fs from 'fs'
33
import path from 'path'
4-
import { CancellationToken, Disposable, Emitter, Event, Position, Range } from 'vscode-languageserver-protocol'
4+
import { CancellationToken, Disposable, Emitter, Event, Position as IPosition, Range } from 'vscode-languageserver-protocol'
55
import { URI } from 'vscode-uri'
66
import channels from './core/channels'
77
import { TextEditor } from './core/editors'
88
import Terminals from './core/terminals'
99
import * as ui from './core/ui'
10+
import { Position } from './edit/position'
1011
import events from './events'
1112
import Dialog, { DialogConfig, DialogPreferences } from './model/dialog'
1213
import Menu, { isMenuItem, MenuItem } from './model/menu'
@@ -410,16 +411,17 @@ class Window {
410411
*
411412
* @returns Cursor position.
412413
*/
413-
public getCursorPosition(): Promise<Position> {
414-
return ui.getCursorPosition(this.nvim)
414+
public async getCursorPosition(): Promise<Position> {
415+
const position = await ui.getCursorPosition(this.nvim)
416+
return new Position(position.line, position.character)
415417
}
416418

417419
/**
418420
* Move cursor to position.
419421
*
420422
* @param position LSP position.
421423
*/
422-
public async moveTo(position: Position): Promise<void> {
424+
public async moveTo(position: IPosition): Promise<void> {
423425
await ui.moveTo(this.nvim, position, workspace.env.isVim)
424426
}
425427

0 commit comments

Comments
 (0)