Skip to content

Commit 8c534be

Browse files
committed
feat: using class Position instance instead of plain object
1 parent 6837b27 commit 8c534be

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,7 +1,8 @@
11
'use strict'
2-
import { Position, Range } from 'vscode-languageserver-protocol'
2+
import { Position as IPosition, Range } from 'vscode-languageserver-protocol'
33
import { TextDocument } from 'vscode-languageserver-textdocument'
44
import { TextLine } from './textline'
5+
import { Position } from '../edit/position'
56

67
export function computeLinesOffsets(lines: ReadonlyArray<string>, eol: boolean): number[] {
78
const result: number[] = []
@@ -72,8 +73,8 @@ export class LinesTextDocument implements TextDocument {
7273
return this.content
7374
}
7475

75-
public lineAt(lineOrPos: number | Position): TextLine {
76-
const line = Position.is(lineOrPos) ? lineOrPos.line : lineOrPos
76+
public lineAt(lineOrPos: number | IPosition): TextLine {
77+
const line = IPosition.is(lineOrPos) ? lineOrPos.line : lineOrPos
7778
if (typeof line !== 'number' ||
7879
line < 0 ||
7980
line >= this.lineCount ||
@@ -90,7 +91,7 @@ export class LinesTextDocument implements TextDocument {
9091
let low = 0
9192
let high = lineOffsets.length
9293
if (high === 0) {
93-
return { line: 0, character: offset }
94+
return new Position(0, offset)
9495
}
9596
while (low < high) {
9697
let mid = Math.floor((low + high) / 2)
@@ -103,10 +104,10 @@ export class LinesTextDocument implements TextDocument {
103104
// low is the least x for which the line offset is larger than the current offset
104105
// or array.length if no line offset is larger than the current offset
105106
let line = low - 1
106-
return { line, character: offset - lineOffsets[line] }
107+
return new Position(line, offset - lineOffsets[line])
107108
}
108109

109-
public offsetAt(position: Position) {
110+
public offsetAt(position: IPosition) {
110111
let lineOffsets = this.getLineOffsets()
111112
if (position.line >= lineOffsets.length) {
112113
return this.content.length

src/window.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22
import { Neovim } from '@chemzqm/neovim'
33
import fs from 'fs'
44
import path from 'path'
5-
import { CancellationToken, Disposable, Emitter, Event, Position, Range } from 'vscode-languageserver-protocol'
5+
import { CancellationToken, Disposable, Emitter, Event, Position as IPosition, Range } from 'vscode-languageserver-protocol'
66
import { URI } from 'vscode-uri'
77
import channels from './core/channels'
88
import { TextEditor } from './core/editors'
99
import Terminals from './core/terminals'
1010
import * as ui from './core/ui'
11+
import { Position } from './edit/position'
1112
import events from './events'
1213
import Dialog, { DialogConfig, DialogPreferences } from './model/dialog'
1314
import Menu, { isMenuItem, MenuItem } from './model/menu'
@@ -452,16 +453,17 @@ class Window {
452453
*
453454
* @returns Cursor position.
454455
*/
455-
public getCursorPosition(): Promise<Position> {
456-
return ui.getCursorPosition(this.nvim)
456+
public async getCursorPosition(): Promise<Position> {
457+
const position = await ui.getCursorPosition(this.nvim)
458+
return new Position(position.line, position.character)
457459
}
458460

459461
/**
460462
* Move cursor to position.
461463
*
462464
* @param position LSP position.
463465
*/
464-
public async moveTo(position: Position): Promise<void> {
466+
public async moveTo(position: IPosition): Promise<void> {
465467
await ui.moveTo(this.nvim, position, workspace.env.isVim)
466468
}
467469

0 commit comments

Comments
 (0)