Skip to content

Commit

Permalink
fix: cleanup all and new project layout (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexanderBrevig authored Jun 27, 2023
1 parent 6fceb45 commit 350754b
Show file tree
Hide file tree
Showing 15 changed files with 514 additions and 333 deletions.
2 changes: 2 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ pub enum Error {
SendError(String),
#[error("OutOfBounds at ix {0}")]
OutOfBounds(usize),
#[error("NoSuchFile {0}")]
NoSuchFile(String),

#[error(transparent)]
IO(#[from] std::io::Error),
Expand Down
351 changes: 30 additions & 321 deletions src/main.rs

Large diffs are not rendered by default.

27 changes: 27 additions & 0 deletions src/utils/handlers/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#[allow(unused_imports)]
use crate::prelude::*;

pub mod notification_did_change;
pub mod notification_did_open;
pub mod request_completion;
pub mod request_goto_definition;
pub mod request_hover;

use lsp_server::{Notification, Request, RequestId};

pub fn cast<R>(req: Request) -> Result<(RequestId, R::Params)>
where
R: lsp_types::request::Request,
R::Params: serde::de::DeserializeOwned,
{
req.extract(R::METHOD).map_err(Error::ExtractRequestError)
}

pub fn cast_notification<N>(req: Notification) -> Result<N::Params>
where
N: lsp_types::notification::Notification,
N::Params: serde::de::DeserializeOwned,
{
req.extract(N::METHOD)
.map_err(Error::ExtractNotificationError)
}
33 changes: 33 additions & 0 deletions src/utils/handlers/notification_did_change.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#[allow(unused_imports)]
use crate::prelude::*;

use std::collections::HashMap;

use lsp_server::Notification;
use ropey::Rope;

use super::cast_notification;

pub fn handle_did_change_text_document(
notification: &Notification,
files: &mut HashMap<String, Rope>,
) -> Result<()> {
match cast_notification::<lsp_types::notification::DidChangeTextDocument>(notification.clone())
{
Ok(params) => {
let rope = files
.get_mut(&params.text_document.uri.to_string())
.expect("Must be able to get rope for lang");
for change in params.content_changes {
let range = change.range.unwrap_or_default();
let start =
rope.line_to_char(range.start.line as usize) + range.start.character as usize;
let end = rope.line_to_char(range.end.line as usize) + range.end.character as usize;
rope.remove(start..end);
rope.insert(start, change.text.as_str());
}
Ok(())
}
Err(_) => todo!(),
}
}
28 changes: 28 additions & 0 deletions src/utils/handlers/notification_did_open.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#[allow(unused_imports)]
use crate::prelude::*;

use std::collections::HashMap;

use lsp_server::Notification;
use ropey::Rope;

use super::cast_notification;

pub fn handle_did_open_text_document(
notification: &Notification,
files: &mut HashMap<String, Rope>,
) -> Result<()> {
match cast_notification::<lsp_types::notification::DidOpenTextDocument>(notification.clone()) {
Ok(params) => {
if let std::collections::hash_map::Entry::Vacant(e) =
files.entry(params.text_document.uri.to_string())
{
let rope = Rope::from_str(params.text_document.text.as_str());
e.insert(rope);
}
Ok(())
}
Err(Error::ExtractNotificationError(req)) => Err(Error::ExtractNotificationError(req)),
Err(err) => panic!("{err:?}"),
}
}
94 changes: 94 additions & 0 deletions src/utils/handlers/request_completion.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#[allow(unused_imports)]
use crate::prelude::*;
use crate::{
utils::{
ropey::{get_ix::GetIx, word_at::WordAt, RopeSliceIsLower},
HashMapGetForLSPParams,
},
words::Words,
};

use std::collections::HashMap;

use lsp_server::{Connection, Message, Request, Response};
use lsp_types::{request::Completion, CompletionItem, CompletionResponse};
use ropey::Rope;

use super::cast;

pub fn handle_completion(
req: &Request,
connection: &Connection,
data: &Words,
files: &mut HashMap<String, Rope>,
) -> Result<()> {
match cast::<Completion>(req.clone()) {
Ok((id, params)) => {
eprintln!("#{id}: {params:?}");
let rope = if let Some(rope) = files.for_position_param(&params.text_document_position)
{
rope
} else {
return Err(Error::NoSuchFile(
params.text_document_position.text_document.uri.to_string(),
));
};
let mut ix = rope.get_ix(&params);
if ix >= rope.len_chars() {
return Err(Error::OutOfBounds(ix));
}
if let Some(char_at_ix) = rope.get_char(ix) {
if char_at_ix.is_whitespace() && ix > 0 {
ix -= 1;
}
}
let word = rope.word_at(ix);
let result = if word.len_chars() > 0 {
eprintln!("Found word {}", word);
let use_lower = word.is_lowercase();
let mut ret = vec![];
let candidates = data.words.iter().filter(|x| {
x.token
.to_lowercase()
.starts_with(word.to_string().to_lowercase().as_str())
});
for candidate in candidates {
let label = candidate.token.to_owned();
let label = if use_lower {
label.to_lowercase()
} else {
label
};
ret.push(CompletionItem {
label,
detail: Some(candidate.stack.to_owned()),
documentation: Some(lsp_types::Documentation::MarkupContent(
lsp_types::MarkupContent {
kind: lsp_types::MarkupKind::Markdown,
value: candidate.documentation(),
},
)),
..Default::default()
});
}
Some(CompletionResponse::Array(ret))
} else {
None
};
let result = serde_json::to_value(result)
.expect("Must be able to serialize the CompletionResponse");
let resp = Response {
id,
result: Some(result),
error: None,
};
connection
.sender
.send(Message::Response(resp))
.map_err(|err| Error::SendError(err.to_string()))?;
Ok(())
}
Err(Error::ExtractRequestError(req)) => Err(Error::ExtractRequestError(req)),
Err(err) => panic!("{err:?}"),
}
}
99 changes: 99 additions & 0 deletions src/utils/handlers/request_goto_definition.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#[allow(unused_imports)]
use crate::prelude::*;
use crate::{
utils::{
data_to_position::ToPosition,
find_variant_sublists_from_to::FindVariantSublistsFromTo,
ropey::{get_ix::GetIx, word_on_or_before::WordOnOrBefore},
HashMapGetForLSPParams,
},
words::Words,
};

use std::{collections::HashMap, mem::discriminant};

use forth_lexer::{
parser::Lexer,
token::{Data, Token},
};
use lsp_server::{Connection, Message, Request, Response};
use lsp_types::{request::GotoDefinition, GotoDefinitionResponse, Location, Range, Url};
use ropey::Rope;

use super::cast;

pub fn handle_goto_definition(
req: &Request,
connection: &Connection,
_data: &Words,
files: &mut HashMap<String, Rope>,
) -> Result<()> {
match cast::<GotoDefinition>(req.clone()) {
Ok((id, params)) => {
eprintln!("#{id}: {params:?}");
let mut ret: Vec<Location> = vec![];
let rope = if let Some(rope) =
files.for_position_param(&params.text_document_position_params)
{
rope
} else {
return Err(Error::NoSuchFile(
params
.text_document_position_params
.text_document
.uri
.to_string(),
));
};
let ix = rope.get_ix(&params);
if ix >= rope.len_chars() {
return Err(Error::OutOfBounds(ix));
}
let word = rope.word_on_or_before(ix).to_string();
for (file, rope) in files.iter() {
eprintln!("Word: {}", word);
let progn = rope.to_string();
let mut lexer = Lexer::new(progn.as_str());
let tokens = lexer.parse();

for result in tokens.find_variant_sublists_from_to(
discriminant(&Token::Colon(Data::default())),
discriminant(&Token::Semicolon(Data::default())),
) {
eprintln!("{:?}", result);
let tok = Token::Illegal(Data::new(0, 0, ""));
let begin = result.first().unwrap_or(&tok).get_data();
let end = result.last().unwrap_or(&tok).get_data();
if let Ok(uri) = Url::from_file_path(file) {
ret.push(Location {
uri,
range: Range {
start: begin.to_position_start(rope),
end: end.to_position_end(rope),
},
});
} else {
eprintln!("Failed to parse URI for {}", file);
}
}
}
let result = Some(GotoDefinitionResponse::Array(ret));
let result = serde_json::to_value(result)
.expect("Must be able to serialize the GotoDefinitionResponse");
let resp = Response {
id,
result: Some(result),
error: None,
};
connection
.sender
.send(Message::Response(resp))
.map_err(|err| Error::SendError(err.to_string()))?;
Ok(())
}
Err(Error::ExtractRequestError(req)) => Err(Error::ExtractRequestError(req)),
Err(err) => panic!("{err:?}"),
// Err(err @ ExtractError::JsonError { .. }) => panic!("{err:?}"),
// Err(ExtractError::MethodMismatch(req)) => req,
}
}
80 changes: 80 additions & 0 deletions src/utils/handlers/request_hover.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#[allow(unused_imports)]
use crate::prelude::*;
use crate::{
utils::{
ropey::{get_ix::GetIx, word_on_or_before::WordOnOrBefore},
HashMapGetForLSPParams,
},
words::{Word, Words},
};

use std::collections::HashMap;

use lsp_server::{Connection, Message, Request, Response};
use lsp_types::{request::HoverRequest, Hover};
use ropey::Rope;

use super::cast;

pub fn handle_hover(
req: &Request,
connection: &Connection,
data: &Words,
files: &mut HashMap<String, Rope>,
) -> Result<()> {
match cast::<HoverRequest>(req.clone()) {
Ok((id, params)) => {
eprintln!("#{id}: {params:?}");
let rope = if let Some(rope) =
files.for_position_param(&params.text_document_position_params)
{
rope
} else {
return Err(Error::NoSuchFile(
params
.text_document_position_params
.text_document
.uri
.to_string(),
));
};
let ix = rope.get_ix(&params);
if ix >= rope.len_chars() {
return Err(Error::OutOfBounds(ix));
}
let word = rope.word_on_or_before(ix);
let result = if !word.len_chars() > 0 {
let default_info = &Word::default();
let info = data
.words
.iter()
.find(|x| x.token.to_lowercase() == (word.to_string().to_lowercase().as_str()))
.unwrap_or(&default_info);
Some(Hover {
contents: lsp_types::HoverContents::Markup(lsp_types::MarkupContent {
kind: lsp_types::MarkupKind::Markdown,
value: info.documentation(),
}),
range: None,
})
} else {
None
};
let result = serde_json::to_value(result).expect("Must be able to serialize the Hover");
let resp = Response {
id,
result: Some(result),
error: None,
};
connection
.sender
.send(Message::Response(resp))
.map_err(|err| Error::SendError(err.to_string()))?;
Ok(())
}
Err(Error::ExtractRequestError(req)) => Err(Error::ExtractRequestError(req)),
Err(err) => panic!("{err:?}"),
// Err(err @ ExtractError::JsonError { .. }) => panic!("{err:?}"),
// Err(ExtractError::MethodMismatch(req)) => req,
}
}
18 changes: 16 additions & 2 deletions src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,19 @@ use crate::prelude::*;
pub mod data_to_position;
pub mod find_variant_sublists;
pub mod find_variant_sublists_from_to;
pub mod ropey_get_ix;
pub mod ropey_word_at_char;
pub mod handlers;
pub mod ropey;
pub mod server_capabilities;

use lsp_types::TextDocumentPositionParams;
use std::collections::HashMap;

pub trait HashMapGetForLSPParams<T> {
fn for_position_param(&mut self, params: &TextDocumentPositionParams) -> Option<&mut T>;
}

impl<T> HashMapGetForLSPParams<T> for HashMap<String, T> {
fn for_position_param(&mut self, params: &TextDocumentPositionParams) -> Option<&mut T> {
self.get_mut(&params.text_document.uri.to_string())
}
}
File renamed without changes.
Loading

0 comments on commit 350754b

Please sign in to comment.