Skip to content

Commit 54b5dfc

Browse files
committed
BASIC workspaces
1 parent f21ef40 commit 54b5dfc

60 files changed

Lines changed: 2833 additions & 880 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,26 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [4.3.0] - 2026-03-15
9+
10+
### Fixes
11+
12+
* smart copy fixes
13+
- reconcile CP/M and Windows (`:` and `_` swapping)
14+
- path parsing is a little smarter
15+
- suppress a misleading warning
16+
* REM following ONERR GOTO no longer hangs the Applesoft server
17+
* move lines no longer adds new-lines to the end unnecessarily
18+
* where possible, line labels are gathered even if there is an error on the line
19+
20+
### New Features
21+
22+
* Applesoft and Integer BASIC expression evaluation
23+
* Workspace scan for Applesoft and Integer BASIC
24+
- detects DOS or ProDOS CHAIN commands
25+
- identifies CHAIN links between files
26+
- analysis accounts for chained variables
27+
828
## [4.2.0] - 2026-02-15
929

1030
### Fixes

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "a2kit"
3-
version = "4.2.0"
3+
version = "4.3.0"
44
edition = "2024"
55
readme = "README.md"
66
license = "MIT"

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ Command line interface and library for retro disk images, file systems, and lang
55
* Designed to be scriptable
66
* Language Servers - Applesoft, Integer BASIC, Merlin Assembly
77
- deep analysis, tokenization, disassembly, adheres to [LSP](https://microsoft.github.io/language-server-protocol/)
8-
* File Systems - Apple DOS 3.2, Apple DOS 3.3, ProDOS, CP/M, Pascal, FAT (such as MS-DOS)
8+
* File Systems - Apple DOS 3.2, Apple DOS 3.3, ProDOS, CP/M, UCSD Pascal, FAT (such as MS-DOS)
99
- full read and write access
10-
- high or low level manipulations
11-
- interface for handling sparse and random access files
10+
- preservation of sparse structure
11+
- conversion of source files to and from legacy formats
1212
* Disk Images - 2MG, D13, DO, DSK, IMD, IMG, NIB, PO, TD0, WOZ
1313
- create, read, and write with all types
14+
- handles proprietary soft sector formats
1415

1516
## Documentation
1617

src/bin/server-applesoft/main.rs

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
//! Cargo will compile this to a standalone executable.
44
//!
55
//! The a2kit library crate provides most of the analysis.
6-
//! The server activity is all in this file.
6+
//! The server activity is all in this module.
77
88
use lsp_types as lsp;
99
use lsp::{notification::Notification, request::Request};
@@ -14,9 +14,10 @@ use std::collections::VecDeque;
1414
use std::error::Error;
1515
use std::sync::{Arc,Mutex};
1616
use a2kit::lang::server::TOKEN_TYPES; // used if we register tokens on server side
17-
use a2kit::lang::server::Analysis;
17+
use a2kit::lang::server::{Analysis,Checkpoint};
1818
use a2kit::lang::applesoft;
1919
use a2kit::lang::applesoft::diagnostics::Analyzer;
20+
use a2kit::lang::applesoft::checkpoint::CheckpointManager;
2021
use a2kit::lang::disk_server::DiskServer;
2122

2223
mod notification;
@@ -33,6 +34,12 @@ mod rpc_error {
3334
// pub const INTERNAL_ERROR: i32 = -32603;
3435
}
3536

37+
enum WorkspaceScanMethod {
38+
None,
39+
UseCheckpoints,
40+
FullUpdate,
41+
}
42+
3643
#[derive(thiserror::Error,Debug)]
3744
enum ServerError {
3845
#[error("Parsing")]
@@ -43,7 +50,8 @@ struct AnalysisResult {
4350
uri: lsp::Uri,
4451
version: Option<i32>,
4552
diagnostics: Vec<lsp::Diagnostic>,
46-
symbols: applesoft::Symbols
53+
symbols: applesoft::Symbols,
54+
workspace: applesoft::Workspace
4755
}
4856

4957
/// Send log messages to the client.
@@ -97,16 +105,40 @@ fn parse_configuration(resp: lsp_server::Response) -> Result<applesoft::settings
97105
Err(Box::new(ServerError::Parsing))
98106
}
99107

100-
fn launch_analysis_thread(analyzer: Arc<Mutex<Analyzer>>, doc: a2kit::lang::Document) -> std::thread::JoinHandle<Option<AnalysisResult>> {
108+
fn launch_analysis_thread(analyzer: Arc<Mutex<Analyzer>>, doc: a2kit::lang::Document, ws_scan: WorkspaceScanMethod, chks: &HashMap<String,CheckpointManager>) -> std::thread::JoinHandle<Option<AnalysisResult>> {
109+
let checkpoints = match ws_scan {
110+
WorkspaceScanMethod::FullUpdate => {
111+
let mut ans = Vec::new();
112+
for chk in chks.values() {
113+
ans.push(chk.get_doc());
114+
}
115+
ans
116+
},
117+
_ => Vec::new()
118+
};
101119
std::thread::spawn( move || {
102120
match analyzer.lock() {
103121
Ok(mut analyzer) => {
122+
match ws_scan {
123+
WorkspaceScanMethod::None => {},
124+
WorkspaceScanMethod::UseCheckpoints => {
125+
match analyzer.rescan_workspace(false) {
126+
_ => {}
127+
}
128+
},
129+
WorkspaceScanMethod::FullUpdate => {
130+
match analyzer.rescan_workspace_and_update(checkpoints) {
131+
_ => {}
132+
}
133+
}
134+
};
104135
match analyzer.analyze(&doc) {
105136
Ok(()) => Some(AnalysisResult {
106137
uri: doc.uri.clone(),
107138
version: doc.version,
108139
diagnostics: analyzer.get_diags(&doc),
109-
symbols: analyzer.get_symbols()
140+
symbols: analyzer.get_symbols(),
141+
workspace: analyzer.get_workspace().clone()
110142
}),
111143
Err(_) => None
112144
}
@@ -135,6 +167,7 @@ pub fn push_diagnostics(connection: &lsp_server::Connection,uri: lsp::Uri, versi
135167

136168
struct Tools {
137169
config: applesoft::settings::Settings,
170+
workspace: applesoft::Workspace,
138171
thread_handles: VecDeque<std::thread::JoinHandle<Option<AnalysisResult>>>,
139172
doc_chkpts: HashMap<String,applesoft::checkpoint::CheckpointManager>,
140173
analyzer: Arc<Mutex<applesoft::diagnostics::Analyzer>>,
@@ -150,6 +183,7 @@ impl Tools {
150183
pub fn new() -> Self {
151184
Self {
152185
config: applesoft::settings::Settings::new(),
186+
workspace: applesoft::Workspace::new(),
153187
thread_handles: VecDeque::new(),
154188
doc_chkpts: HashMap::new(),
155189
analyzer: Arc::new(Mutex::new(Analyzer::new())),
@@ -194,6 +228,7 @@ fn main() -> Result<(), Box<dyn Error + Sync + Send>> {
194228
..lsp::CompletionOptions::default()
195229
}),
196230
document_symbol_provider: Some(lsp::OneOf::Left(true)),
231+
workspace_symbol_provider: Some(lsp::OneOf::Left(true)),
197232
rename_provider: Some(lsp::OneOf::Left(true)),
198233
semantic_tokens_provider: match suppress_tokens {
199234
true => None,
@@ -263,6 +298,18 @@ fn main() -> Result<(), Box<dyn Error + Sync + Send>> {
263298
Err(_) => logger(&connection,"could not request starting configuration")
264299
}
265300

301+
// Initial workspace scan
302+
if let Some(folders) = params.workspace_folders {
303+
let source_dirs = folders.iter().map(|f| f.uri.clone()).collect::<Vec<lsp::Uri>>();
304+
//tools.hover_provider.set_workspace_folder(source_dirs.clone());
305+
if let Ok(mut mutex) = tools.analyzer.lock() {
306+
match mutex.init_workspace(source_dirs, Vec::new()) {
307+
Ok(()) => {},
308+
Err(e) => logger(&connection,&format!("initial workspace scan failed: {}",e))
309+
}
310+
}
311+
}
312+
266313
// Main loop
267314
loop {
268315

@@ -271,8 +318,10 @@ fn main() -> Result<(), Box<dyn Error + Sync + Send>> {
271318
if oldest.is_finished() {
272319
let done = tools.thread_handles.pop_front().unwrap();
273320
if let Ok(Some(result)) = done.join() {
321+
tools.workspace = result.workspace;
274322
if let Some(chkpt) = tools.doc_chkpts.get_mut(&result.uri.to_string()) {
275323
chkpt.update_symbols(result.symbols);
324+
chkpt.update_ws_symbols(tools.workspace.borrow_ws_symbols().clone());
276325
tools.hover_provider.use_shared_symbols(chkpt.shared_symbols());
277326
tools.completion_provider.use_shared_symbols(chkpt.shared_symbols());
278327
}

src/bin/server-applesoft/notification.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,31 @@ pub fn handle_notification(
3535
uri: normalized_uri.clone(),
3636
version: Some(params.text_document.version),
3737
text: params.text_document.text
38-
}
38+
},
39+
crate::WorkspaceScanMethod::FullUpdate,
40+
&tools.doc_chkpts
3941
);
4042
tools.thread_handles.push_back(handle);
4143
}
4244
},
45+
lsp::notification::DidSaveTextDocument::METHOD => {
46+
if let Ok(params) = serde_json::from_value::<lsp::DidSaveTextDocumentParams>(note.params) {
47+
let normalized_uri = normalize_client_uri(params.text_document.uri);
48+
if let Some(text) = params.text {
49+
let handle = launch_analysis_thread(
50+
Arc::clone(&tools.analyzer),
51+
a2kit::lang::Document {
52+
uri: normalized_uri.clone(),
53+
version: None,
54+
text
55+
},
56+
crate::WorkspaceScanMethod::FullUpdate,
57+
&tools.doc_chkpts
58+
);
59+
tools.thread_handles.push_back(handle);
60+
}
61+
}
62+
},
4363
lsp::notification::DidCloseTextDocument::METHOD => {
4464
if let Ok(params) = serde_json::from_value::<lsp::DidCloseTextDocumentParams>(note.params) {
4565
let normalized_uri = normalize_client_uri(params.text_document.uri);
@@ -63,7 +83,9 @@ pub fn handle_notification(
6383
uri: normalized_uri.clone(),
6484
version: Some(params.text_document.version),
6585
text: change.text
66-
}
86+
},
87+
crate::WorkspaceScanMethod::UseCheckpoints,
88+
&tools.doc_chkpts
6789
);
6890
tools.thread_handles.push_back(handle);
6991
}

src/bin/server-applesoft/request.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@ pub fn handle_request(
7171
lsp::request::HoverRequest::METHOD => Checkpoint::hover_response(chkpts, &mut tools.hover_provider, req.clone(), &mut resp),
7272
lsp::request::Completion::METHOD => Checkpoint::completion_response(chkpts, &mut tools.completion_provider, req.clone(), &mut resp),
7373
lsp::request::SemanticTokensFullRequest::METHOD => Checkpoint::sem_tok_response(chkpts, &mut tools.highlighter, req.clone(), &mut resp),
74+
lsp::request::WorkspaceSymbolRequest::METHOD => {
75+
if let Ok(_params) = serde_json::from_value::<lsp::WorkspaceSymbolParams>(req.params) {
76+
let ws_syms = tools.workspace.get_ws_symbols_for_client();
77+
resp = lsp_server::Response::new_ok(req.id,ws_syms);
78+
}
79+
},
7480

7581
lsp::request::Shutdown::METHOD => {
7682
logger(&connection,"shutdown request");
@@ -91,7 +97,9 @@ pub fn handle_request(
9197
if let Some(chk) = tools.doc_chkpts.get(&normalized_uri.to_string()) {
9298
let handle = crate::launch_analysis_thread(
9399
Arc::clone(&tools.analyzer),
94-
chk.get_doc()
100+
chk.get_doc(),
101+
crate::WorkspaceScanMethod::UseCheckpoints,
102+
&tools.doc_chkpts
95103
);
96104
tools.thread_handles.push_back(handle);
97105
}

src/bin/server-applesoft/response.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ use std::sync::{Mutex,Arc};
55
use a2kit::lang::applesoft;
66
use a2kit::lang::server::Checkpoint;
77
use a2kit::lang::applesoft::diagnostics::Analyzer;
8+
use a2kit::lang::applesoft::Workspace;
89
use super::logger;
910

1011
pub fn handle_response(connection: &lsp_server::Connection, resp: lsp_server::Response, tools: &mut super::Tools) {
1112
match resp.id.to_string().as_str() {
1213
"\"applesoft-pull-config\"" => {
1314
match super::parse_configuration(resp) {
1415
Ok(config) => {
16+
let mut workspace_data = Workspace::new();
1517
tools.config = config.clone();
1618
tools.hover_provider.set_config(config.clone());
1719
tools.completion_provider.set_config(config.clone());
@@ -21,17 +23,24 @@ pub fn handle_response(connection: &lsp_server::Connection, resp: lsp_server::Re
2123
// configure main analyzer
2224
if let Ok(mut mutex) = tools.analyzer.lock() {
2325
mutex.set_config(config.clone());
26+
if let Err(_) = mutex.rescan_workspace(false) {
27+
logger(&connection,"failed to rescan workspace after user changed settings");
28+
}
29+
workspace_data = mutex.get_workspace().clone();
2430
}
2531

2632
// run through open documents and update
2733
for (key,chkpt) in &tools.doc_chkpts {
2834
let doc = chkpt.get_doc();
2935
let mut loc_analyzer = Analyzer::new();
3036
loc_analyzer.set_config(config.clone());
37+
loc_analyzer.set_workspace(workspace_data.clone());
3138
logger(&connection,&format!("updated configuration for {}",key));
3239
let handle = super::launch_analysis_thread(
3340
Arc::new(Mutex::new(loc_analyzer)),
34-
doc
41+
doc,
42+
crate::WorkspaceScanMethod::None,
43+
&tools.doc_chkpts
3544
);
3645
tools.thread_handles.push_back(handle);
3746
}

0 commit comments

Comments
 (0)