Skip to content

Commit 85ea990

Browse files
committed
Editor/Buffer Changes
1 parent 48023ee commit 85ea990

12 files changed

+712
-383
lines changed

package-lock.json

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
"dependencies": {
5050
"@atom/teletype-client": "github:Rijul5/teletype-client",
5151
"google-protobuf": "^3.8.0",
52+
"mkdirp-promise": "^5.0.1",
5253
"node-fetch": "^2.6.0",
5354
"wrtc": "^0.4.1"
5455
}

src/BufferBinding.ts

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
import * as fs from 'fs';
2+
import * as vscode from 'vscode';
3+
4+
import { BufferProxy } from '@atom/teletype-client';
5+
6+
import { Position, TextUdpate } from './teletype_types';
7+
8+
export default class BufferBinding {
9+
public readonly buffer: vscode.TextDocument;
10+
private editor!: vscode.TextEditor;
11+
private readonly isHost: boolean;
12+
private bufferProxy!: BufferProxy;
13+
private onGetText: any;
14+
public didDispose: Function;
15+
private disposed!: boolean;
16+
private onUpdateText: any;
17+
private onInsert: any;
18+
private onDelete: any;
19+
20+
21+
constructor({ buffer, isHost, didDispose }: { buffer: any; isHost: any; didDispose: any; }) {
22+
23+
this.buffer = buffer;
24+
this.isHost = isHost;
25+
this.didDispose = didDispose;
26+
}
27+
28+
dispose() {
29+
this.disposed = true;
30+
}
31+
isDisposed() {
32+
return this.disposed;
33+
}
34+
getText() {
35+
if (typeof this.onGetText === "function") {
36+
return this.onGetText();
37+
}
38+
return null;
39+
}
40+
41+
setBufferProxy(bufferProxy: BufferProxy) {
42+
this.bufferProxy = bufferProxy;
43+
}
44+
45+
setText(text: string) {
46+
fs.writeFileSync(this.buffer.uri.fsPath, text);
47+
}
48+
49+
setEditor(editor: vscode.TextEditor) {
50+
this.editor = editor;
51+
}
52+
53+
updateText(textUpdates: any) {
54+
return this.editor.edit(builder => {
55+
for (let i = textUpdates.length - 1; i >= 0; i--) {
56+
const textUpdate = textUpdates[i];
57+
// console.log("update text oldEnd r:" + textUpdate.oldEnd.row + " c:" + textUpdate.oldEnd.column + " oldStart r: " + textUpdate.oldStart.row + " c:" + textUpdate.oldStart.column + " newText " + textUpdate.newText);
58+
builder.replace(this.createRange(textUpdate.oldStart, textUpdate.oldEnd), textUpdate.newText);
59+
}
60+
}, { undoStopBefore: false, undoStopAfter: true });
61+
}
62+
63+
// updateText (textUpdates: TextUdpate[]) {
64+
// return this.editor.edit(builder => {
65+
// for (let i = textUpdates.length - 1; i >= 0; i--) {
66+
// const {oldStart, oldEnd, newText} = textUpdates[i];
67+
// builder.replace(this.createRange(oldStart, oldEnd), newText);
68+
// }
69+
// }, { undoStopBefore: false, undoStopAfter: true });
70+
// }
71+
72+
traverse(start: any, distance: any) {
73+
if (distance.row === 0) {
74+
return { row: start.row, column: start.column + distance.column };
75+
}
76+
77+
else {
78+
return { row: start.row + distance.row, column: distance.column };
79+
}
80+
}
81+
82+
insert(position: any, text: any) {
83+
console.log("buffer insert pos:" + position + " text: " + text);
84+
if (typeof this.onInsert === "function") {
85+
this.onInsert(position, text);
86+
}
87+
return [position, position, text];
88+
}
89+
delete(startPosition: any, extent: any) {
90+
console.log("buffer delete start pos:" + startPosition + " extent: " + extent);
91+
if (typeof this.onDelete === "function") {
92+
this.onDelete(startPosition, extent);
93+
}
94+
const endPosition = this.traverse(startPosition, extent);
95+
return [startPosition, endPosition, ''];
96+
}
97+
98+
99+
100+
101+
private createRange(start: Position, end: Position): vscode.Range {
102+
return new vscode.Range(
103+
new vscode.Position(start.row, start.column),
104+
new vscode.Position(end.row, end.column)
105+
);
106+
}
107+
108+
onDidChangeBuffer(changes: vscode.TextDocumentContentChangeEvent[]) {
109+
this.bufferProxy.onDidChangeBuffer(changes.map(change => {
110+
const { start, end } = change.range;
111+
112+
return {
113+
oldStart: { row: start.line, column: start.character },
114+
oldEnd: { row: end.line, column: end.character },
115+
newText: change.text
116+
};
117+
}));
118+
}
119+
120+
requestSavePromise() {
121+
return new Promise(() => {
122+
this.bufferProxy.requestSave();
123+
});
124+
}
125+
126+
save() {
127+
this.buffer.save();
128+
}
129+
}

src/EditorBinding.ts

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
import * as vscode from 'vscode';
2+
3+
import { EditorProxy, Portal } from '@atom/teletype-client';
4+
5+
import { SelectionMap, Selection, Position, Range } from './teletype_types';
6+
7+
interface SiteDecoration {
8+
cursorDecoration: vscode.TextEditorDecorationType;
9+
selectionDecoration: vscode.TextEditorDecorationType;
10+
}
11+
12+
export default class EditorBinding {
13+
public readonly editor: vscode.TextEditor;
14+
private portal: Portal;
15+
private readonly isHost: boolean;
16+
private editorProxy!: EditorProxy;
17+
private localSelectionMap: SelectionMap;
18+
private disposed!: boolean;
19+
private selectionsBySiteId: any;
20+
private decorationBySiteId: Map<number, SiteDecoration>;
21+
private localMarkerSelectionMap: Map<number, SelectionMap>;
22+
23+
constructor({ editor, portal, isHost }: { editor: any; portal: any; isHost: any; }) {
24+
this.editor = editor;
25+
this.portal = portal;
26+
this.isHost = isHost;
27+
this.localSelectionMap = {};
28+
this.selectionsBySiteId = new Map();
29+
this.decorationBySiteId = new Map();
30+
this.localMarkerSelectionMap = new Map();
31+
}
32+
33+
dispose() {
34+
this.disposed = true;
35+
}
36+
37+
isDisposed() {
38+
return this.disposed;
39+
}
40+
41+
onDidDispose(onDidDipose: (onDidDipose: any) => void) {
42+
this.onDidDispose = onDidDipose;
43+
}
44+
45+
setEditorProxy(editorProxy: EditorProxy) {
46+
this.editorProxy = editorProxy;
47+
}
48+
49+
updateSelectionsForSiteId(siteId: number, selectionUpdates: SelectionMap) {
50+
console.log("updateSelectionsForSiteID: " + siteId);
51+
let selectionsForSite = this.localMarkerSelectionMap.get(siteId);
52+
const selectionMap = { ...selectionsForSite, ...selectionUpdates };
53+
this.localMarkerSelectionMap.set(siteId, selectionMap);
54+
let selectionRanges: vscode.Range[] = [];
55+
let cursorRanges: vscode.Range[] = [];
56+
if (!selectionsForSite) {
57+
selectionsForSite = {};
58+
this.selectionsBySiteId[siteId] = selectionsForSite;
59+
}
60+
for (const selectionId in selectionUpdates) {
61+
const selectionUpdate = selectionUpdates[selectionId];
62+
if (selectionUpdate) {
63+
selectionsForSite[selectionId] = selectionUpdate;
64+
if (this.isCursor(selectionUpdate)) {
65+
cursorRanges = cursorRanges.concat(this.convertTeletypeRange(selectionUpdate.range));
66+
} else {
67+
if (selectionUpdate.tailed) {
68+
const cursorRange = this.getCursorRangeFromSelection(selectionUpdate);
69+
cursorRanges = cursorRanges.concat(this.convertTeletypeRange(cursorRange));
70+
}
71+
selectionRanges = selectionRanges.concat(this.convertTeletypeRange(selectionUpdate.range));
72+
}
73+
}
74+
else {
75+
delete selectionsForSite[selectionId];
76+
}
77+
}
78+
let siteDecoration = this.findSiteDecoration(siteId);
79+
this.updateDecorations(siteDecoration, cursorRanges, selectionRanges);
80+
}
81+
82+
83+
private updateDecorations(siteDecoration: SiteDecoration, cursorRanges: vscode.Range[], selectionRanges: vscode.Range[]) {
84+
const { cursorDecoration, selectionDecoration } = siteDecoration;
85+
this.editor.setDecorations(cursorDecoration, cursorRanges);
86+
this.editor.setDecorations(selectionDecoration, selectionRanges);
87+
}
88+
89+
private findSiteDecoration(siteId: number) {
90+
let siteDecoration = this.decorationBySiteId.get(siteId);
91+
if (!siteDecoration) {
92+
siteDecoration = this.createDecorationFromSiteId(siteId);
93+
this.decorationBySiteId.set(siteId, siteDecoration);
94+
}
95+
return siteDecoration;
96+
}
97+
98+
isScrollNeededToViewPosition(position: any) {
99+
100+
}
101+
102+
updateTether(state: any, position: any) {
103+
}
104+
105+
clearSelectionsForSiteId(siteId: number) {
106+
const siteDecoration = this.findSiteDecoration(siteId);
107+
this.updateDecorations(siteDecoration, [], []);
108+
}
109+
110+
updateSelections(selections: vscode.Selection[]) {
111+
this.processSelections(selections);
112+
this.editorProxy.updateSelections(this.localSelectionMap);
113+
}
114+
115+
private processSelections(selections: vscode.Selection[]) {
116+
const currentSelectionKeys = Object.keys(this.localSelectionMap);
117+
const newSelectionsLength = selections.length;
118+
119+
selections.forEach((selection, index) => {
120+
this.localSelectionMap[index] = {
121+
range: {
122+
start: this.convertVSCodePosition(selection.start),
123+
end: this.convertVSCodePosition(selection.end)
124+
},
125+
reversed: selection.isReversed,
126+
};
127+
});
128+
129+
selections.forEach((selection, index) => {
130+
if (currentSelectionKeys.length > newSelectionsLength) {
131+
for (let index = newSelectionsLength; index < currentSelectionKeys.length; index += 1) {
132+
this.localSelectionMap[index] = {
133+
range: {
134+
start: this.convertVSCodePosition(selection.start),
135+
end: this.convertVSCodePosition(selection.end)
136+
},
137+
reversed: false,
138+
};
139+
}
140+
}
141+
}
142+
);
143+
}
144+
145+
private convertVSCodePosition(position: vscode.Position): Position {
146+
return {
147+
column: position.character,
148+
row: position.line
149+
};
150+
}
151+
152+
private convertTeletypePosition(position: Position): vscode.Position {
153+
return new vscode.Position(
154+
position.row,
155+
position.column
156+
);
157+
}
158+
159+
private convertTeletypeRange(range: Range): vscode.Range {
160+
return new vscode.Range(
161+
this.convertTeletypePosition(range.start),
162+
this.convertTeletypePosition(range.end)
163+
);
164+
}
165+
166+
private createDecorationFromSiteId(siteId: number): SiteDecoration {
167+
const selectionDecorationRenderOption: vscode.DecorationRenderOptions = {
168+
backgroundColor: `rgba(0,0,255,0.6)`
169+
};
170+
171+
const { login: siteLogin } = this.portal.getSiteIdentity(siteId);
172+
173+
const nameTagStyleRules = {
174+
position: 'absolute',
175+
top: '12px',
176+
padding: '0px 5px 0px 0px',
177+
display: 'inline-block',
178+
'z-index': 1,
179+
'border-radius': '20px',
180+
'font-size': '15px',
181+
'font-weight': 'bold'
182+
};
183+
184+
const curosrDecorationRenderOption: vscode.DecorationRenderOptions = {
185+
border: 'solid rgba(0,0,255,0.6)',
186+
borderWidth: '5px 5px 5px 5px',
187+
after: {
188+
contentText: siteLogin,
189+
backgroundColor: 'rgba(0,0,255,0.6)',
190+
color: 'rgba(192,192,192,30)',
191+
textDecoration: `none; ${this.stringifyCssProperties(nameTagStyleRules)}`
192+
}
193+
};
194+
195+
const create = vscode.window.createTextEditorDecorationType;
196+
197+
return {
198+
selectionDecoration: create(selectionDecorationRenderOption),
199+
cursorDecoration: create(curosrDecorationRenderOption)
200+
};
201+
}
202+
203+
204+
private stringifyCssProperties(rules: any) {
205+
return Object.keys(rules)
206+
.map((rule) => {
207+
return `${rule}: ${rules[rule]};`;
208+
}).join(' ');
209+
}
210+
211+
private getCursorRangeFromSelection(selection: Selection): Range {
212+
const { range: { end, start } } = selection;
213+
if (selection.reversed) {
214+
return {
215+
start: start,
216+
end: start
217+
};
218+
} else {
219+
return {
220+
start: end,
221+
end: end
222+
};
223+
}
224+
}
225+
226+
private isCursor(selection: Selection): boolean {
227+
const { start, end } = selection.range;
228+
return (
229+
start.column === end.column &&
230+
start.row === end.row
231+
);
232+
}
233+
}

0 commit comments

Comments
 (0)