Skip to content

Commit b79dd27

Browse files
committed
Reorganize the code/selection/position into a map of files
This will allow us to have multiple files, such as a Cargo.toml.
1 parent 2a10390 commit b79dd27

File tree

11 files changed

+154
-92
lines changed

11 files changed

+154
-92
lines changed

ui/frontend/local_storage.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ describe('restoring saved state', () => {
2626
});
2727

2828
expect(parsed?.configuration?.orientation).toEqual('vertical');
29-
expect(parsed?.code).toEqual('not default code');
29+
expect(parsed?.files?.entries).toEqual('not default code');
3030
expect(parsed?.notifications?.seenRustSurvey2018).toBe(true);
3131
});
3232

ui/frontend/local_storage.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// playground.
44

55
import State from './state';
6-
import {removeVersion, initializeStorage, PartialState} from './storage';
6+
import {moveCode, removeVersion, initializeStorage, PartialState, MovedCode} from './storage';
77
import { AssemblyFlavor, DemangleAssembly, Editor, Orientation, PairCharacters, ProcessAssembly } from './types';
88
import { codeSelector } from './selectors';
99

@@ -100,16 +100,22 @@ function migrate(state: V1Configuration | V2Configuration): CurrentConfiguration
100100
}
101101
}
102102

103+
function rename(result: V2Configuration): MovedCode<V2Configuration> {
104+
return moveCode(result);
105+
}
106+
103107
export function deserialize(savedState: string): PartialState {
104108
if (!savedState) { return undefined; }
105109

106110
const parsedState = JSON.parse(savedState);
107111
if (!parsedState) { return undefined; }
108112

109-
const result = migrate(parsedState);
110-
if (!result) { return undefined; }
113+
const migrated = migrate(parsedState);
114+
if (!migrated) { return undefined; }
115+
116+
const renamed = rename(migrated);
111117

112-
return removeVersion(result);
118+
return removeVersion(renamed);
113119
}
114120

115121
export default initializeStorage({

ui/frontend/reducers/code.ts

Lines changed: 0 additions & 34 deletions
This file was deleted.

ui/frontend/reducers/files.ts

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { Action, ActionType } from '../actions';
2+
import { makePosition, Entries, File } from '../types';
3+
4+
const DEFAULT_MAIN_RS = `fn main() {
5+
println!("Hello, world!");
6+
}`;
7+
8+
const makeFile = (code: string): File => ({
9+
code,
10+
position: makePosition(0, 0),
11+
selection: {},
12+
lastEdited: new Date(),
13+
});
14+
15+
const SINGLE_FILE_FILENAME = 'playground.rs';
16+
17+
export const makeState = (code: string): State => ({
18+
current: SINGLE_FILE_FILENAME,
19+
entries: { [SINGLE_FILE_FILENAME]: makeFile(code) },
20+
});
21+
22+
const DEFAULT: State = makeState(DEFAULT_MAIN_RS);
23+
24+
export type State = {
25+
current: string;
26+
entries: Entries;
27+
}
28+
29+
const reduceSingleFile = (state: State, callback: (entry: File) => File) => {
30+
let entries = state.entries;
31+
const entry = callback(entries[SINGLE_FILE_FILENAME]);
32+
entries = { ...entries, [SINGLE_FILE_FILENAME]: entry };
33+
return { ...state, entries };
34+
};
35+
36+
const reduceSingleFileCode = (state: State, callback: (code: string) => string) => {
37+
return reduceSingleFile(state, (entry) => {
38+
const code = callback(entry.code);
39+
return { ...entry, code, lastEdited: new Date() };
40+
});
41+
}
42+
43+
export default function files(state = DEFAULT, action: Action): State {
44+
switch (action.type) {
45+
// These are actions for the one-file mode
46+
47+
case ActionType.RequestGistLoad:
48+
return reduceSingleFileCode(state, () => '');
49+
50+
case ActionType.GistLoadSucceeded:
51+
return reduceSingleFileCode(state, () => action.code);
52+
53+
case ActionType.EditCode:
54+
return reduceSingleFileCode(state, () => action.code);
55+
56+
case ActionType.AddMainFunction:
57+
return reduceSingleFileCode(state, code => `${code}\n\n${DEFAULT_MAIN_RS}`);
58+
59+
case ActionType.AddImport:
60+
return reduceSingleFileCode(state, code => action.code + code);
61+
62+
case ActionType.EnableFeatureGate:
63+
return reduceSingleFileCode(state, code => `#![feature(${action.featureGate})]\n${code}`);
64+
65+
case ActionType.FormatSucceeded:
66+
return reduceSingleFileCode(state, () => action.code);
67+
68+
case ActionType.GotoPosition: {
69+
const { line, column } = action;
70+
return reduceSingleFile(state, entry => {
71+
return {...entry, position: { line, column }};
72+
});
73+
}
74+
75+
case ActionType.SelectText: {
76+
const { start, end } = action;
77+
return reduceSingleFile(state, entry => {
78+
return {...entry, selection: { start, end }};
79+
});
80+
}
81+
82+
default:
83+
return state;
84+
}
85+
}

ui/frontend/reducers/index.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,25 @@
11
import { combineReducers } from 'redux';
22

33
import browser from './browser';
4-
import code from './code';
54
import configuration from './configuration';
65
import crates from './crates';
6+
import files from './files';
77
import globalConfiguration from './globalConfiguration';
88
import notifications from './notifications';
99
import output from './output';
1010
import page from './page';
11-
import position from './position';
12-
import selection from './selection';
1311
import versions from './versions';
1412
import websocket from './websocket';
1513

1614
const playgroundApp = combineReducers({
1715
browser,
18-
code,
1916
configuration,
2017
crates,
18+
files,
2119
globalConfiguration,
2220
notifications,
2321
output,
2422
page,
25-
position,
26-
selection,
2723
versions,
2824
websocket,
2925
});

ui/frontend/reducers/position.ts

Lines changed: 0 additions & 20 deletions
This file was deleted.

ui/frontend/reducers/selection.ts

Lines changed: 0 additions & 16 deletions
This file was deleted.

ui/frontend/selectors/index.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,17 @@ import {
1414
Version,
1515
} from '../types';
1616

17-
export const codeSelector = (state: State) => state.code;
18-
export const positionSelector = (state: State) => state.position;
19-
export const selectionSelector = (state: State) => state.selection;
17+
const currentEntryNameSelector = (state: State) => state.files.current;
18+
const entriesSelector = (state: State) => state.files.entries;
19+
20+
const currentEntrySelector = createSelector(
21+
currentEntryNameSelector,
22+
entriesSelector,
23+
(name, entries) => ({ ...entries[name], name }));
24+
25+
export const codeSelector = createSelector(currentEntrySelector, entry => entry.code);
26+
export const positionSelector = createSelector(currentEntrySelector, entry => entry.position);
27+
export const selectionSelector = createSelector(currentEntrySelector, entry => entry.selection);
2028

2129
const HAS_TESTS_RE = /^\s*#\s*\[\s*test\s*([^"]*)]/m;
2230
export const hasTestsSelector = createSelector(codeSelector, code => !!code.match(HAS_TESTS_RE));

ui/frontend/session_storage.ts

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,36 @@
33
// when we reopen a closed tab.
44

55
import { State } from './reducers';
6-
import {removeVersion, initializeStorage, PartialState} from './storage';
6+
import {moveCode, removeVersion, initializeStorage, PartialState, MovedCode} from './storage';
77
import { codeSelector } from './selectors';
8+
import { PrimaryAction } from './types';
89

910
const CURRENT_VERSION = 1;
1011

12+
interface V1Configuration {
13+
version: 1;
14+
configuration: {
15+
primaryAction: PrimaryAction,
16+
};
17+
code: string;
18+
}
19+
20+
type CurrentConfiguration = V1Configuration;
21+
1122
export function serialize(state: State): string {
1223
const code = codeSelector(state);
13-
14-
return JSON.stringify({
24+
const conf: CurrentConfiguration = {
1525
version: CURRENT_VERSION,
1626
configuration: {
1727
primaryAction: state.configuration.primaryAction,
1828
},
1929
code,
20-
});
30+
};
31+
return JSON.stringify(conf);
32+
}
33+
34+
function rename(result: V1Configuration): MovedCode<V1Configuration> {
35+
return moveCode(result);
2136
}
2237

2338
export function deserialize(savedState: string): PartialState {
@@ -28,11 +43,9 @@ export function deserialize(savedState: string): PartialState {
2843

2944
if (parsedState.version !== CURRENT_VERSION) { return undefined; }
3045

31-
// This assumes that the keys we serialize with match the keys in the
32-
// live state. If that's no longer true, an additional renaming step
33-
// needs to be added.
46+
const renamed = rename(parsedState);
3447

35-
return removeVersion(parsedState);
48+
return removeVersion(renamed);
3649
}
3750

3851
export default initializeStorage({

ui/frontend/storage.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { DeepPartial } from 'ts-essentials';
22

33
import State from './state';
4+
import { makeState } from './reducers/files';
45

56
type SimpleStorage = Pick<Storage, 'getItem' | 'setItem'>;
67

@@ -19,6 +20,19 @@ interface InitializedStorage {
1920
saveChanges: (state: State) => void;
2021
}
2122

23+
type FileStateSlice = Pick<State, 'files'>;
24+
export type MovedCode<T> = Omit<T, 'code'> & FileStateSlice;
25+
26+
export function moveCode<T extends { code: string }>(data: T): MovedCode<T> {
27+
const code = data.code;
28+
29+
const munged: Record<string, unknown> = {...data};
30+
delete munged.code;
31+
const removed = munged as Omit<T, 'code'>;
32+
33+
return { ...removed, files: makeState(code) };
34+
}
35+
2236
export function removeVersion<T extends { version: unknown }>(data: T): Omit<T, 'version'> {
2337
const munged: Record<string, unknown> = {...data};
2438
delete munged.version;

ui/frontend/types.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,16 @@ export interface Selection {
1313
end?: Position;
1414
}
1515

16+
export interface File {
17+
code: string;
18+
position: Position;
19+
selection: Selection;
20+
lastEdited: Date;
21+
lastSaved?: Date;
22+
}
23+
24+
export type Entries = { [k: string]: File };
25+
1626
export interface Crate {
1727
id: string;
1828
name: string;

0 commit comments

Comments
 (0)