Move citeproc into a separate crate

This commit is contained in:
Patrick Förster 2019-07-30 14:05:04 +02:00
parent bacbcdb824
commit 5e612219d1
19 changed files with 70 additions and 15 deletions

View file

@ -0,0 +1,20 @@
[package]
name = "texlab-citeproc"
version = "0.1.0"
authors = [
"Eric Förster <efoerster@users.noreply.github.com>",
"Patrick Förster <pfoerster@users.noreply.github.com>"]
edition = "2018"
[dependencies]
futures-preview = { version = "0.3.0-alpha.15", features = ["compat"] }
html2md = "0.2.9"
lsp-types = { git = "https://github.com/latex-lsp/lsp-types", rev = "9fcc5d9b9d3013ce84e20ef566267754d594b268", features = ["proposed"] }
runtime = "0.3.0-alpha.4"
runtime-tokio = "0.3.0-alpha.4"
serde = { version = "1.0.97", features = ["derive", "rc"] }
tempfile = "3"
texlab-syntax = { path = "../texlab_syntax" }
tokio = "0.1"
tokio-process = "0.2.4"

View file

@ -0,0 +1,20 @@
use std::process::Command;
fn sh(command: &'static str) {
let (executable, args) = if cfg!(windows) {
("cmd", vec!["/C", command])
} else {
("sh", vec!["-c", command])
};
Command::new(executable)
.args(args)
.current_dir("script")
.output()
.expect(&format!("Failed to execute \"{}\"", command));
}
fn main() {
sh("npm ci");
sh("npm run dist");
}

View file

@ -0,0 +1,93 @@
# Created by https://www.gitignore.io/api/node
# Edit at https://www.gitignore.io/?templates=node
### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# End of https://www.gitignore.io/api/node
# TypeScript output
out/
# dist
dist/

View file

@ -0,0 +1,5 @@
{
"printWidth": 80,
"singleQuote": true,
"trailingComma": "all"
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,31 @@
{
"name": "texlab-citeproc",
"version": "0.1.0",
"description": "LaTeX Language Server",
"repository": "https://github.com/latex-lsp/texlab.git",
"author": "Eric Förster <efoerster@users.noreply.github.com>",
"license": "MIT",
"scripts": {
"dist": "webpack",
"build": "tsc -p .",
"watch": "tsc -p . --watch",
"lint": "tslint --project .",
"format": "prettier --write \"src/**/*.{ts,json}\" \"*.{ts,json,yml,md}\" \".vscode/**/*.{json}\""
},
"devDependencies": {
"@citation-js/core": "^0.4.8",
"@citation-js/plugin-bibtex": "^0.4.8",
"@citation-js/plugin-csl": "^0.4.8",
"@types/node": "^11.13.17",
"@types/webpack": "^4.4.35",
"null-loader": "^0.1.1",
"prettier": "^1.18.2",
"ts-loader": "^5.4.5",
"ts-node": "^8.3.0",
"tslint": "^5.18.0",
"tslint-config-prettier": "^1.15.0",
"typescript": "^3.5.3",
"webpack": "^4.35.3",
"webpack-cli": "^3.3.6"
}
}

View file

@ -0,0 +1,7 @@
declare module '@citation-js/core' {
export class Cite {
constructor(text: string);
public format(type: string, options: any): string;
}
}

View file

@ -0,0 +1,13 @@
import { Cite } from '@citation-js/core';
import '@citation-js/plugin-bibtex';
import '@citation-js/plugin-csl';
import fs from 'fs';
const code = fs.readFileSync('entry.bib').toString();
const cite = new Cite(code);
const html = cite.format('bibliography', {
format: 'html',
template: 'apa',
lang: 'en-US',
});
console.log(html);

View file

@ -0,0 +1,15 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es6",
"lib": ["es6"],
"rootDir": "src",
"outDir": "dist",
"sourceMap": true,
"strict": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src/**/*"]
}

View file

@ -0,0 +1,20 @@
{
"extends": ["tslint:latest", "tslint-config-prettier"],
"rules": {
"no-implicit-dependencies": false,
"interface-name": false,
"max-classes-per-file": false,
"object-literal-sort-keys": false,
"variable-name": [
true,
"ban-keywords",
"check-format",
"allow-leading-underscore",
"allow-pascal-case"
],
"no-empty": false,
"no-console": false,
"no-conditional-assignment": false,
"no-bitwise": false
}
}

View file

@ -0,0 +1,37 @@
import path from 'path';
import { Configuration } from 'webpack';
const config: Configuration = {
target: 'node',
entry: './src/main.ts',
mode: 'production',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'citeproc.js',
libraryTarget: 'commonjs2',
devtoolModuleFilenameTemplate: '../[resource-path]',
},
resolve: {
extensions: ['.ts', '.js', '.json'],
},
module: {
rules: [
{
test: /\.ts$/,
exclude: /node_modules/,
use: [
{
loader: 'ts-loader',
},
],
},
{
// Map browser dependencies to an empty module
test: /node_modules[/\\](sync-request|isomorphic-fetch|ws)[/\\]/,
use: 'null-loader',
},
],
},
};
export default config;

View file

@ -0,0 +1,90 @@
#![feature(async_await)]
use texlab_syntax::*;
use futures::compat::*;
use lsp_types::*;
use std::process::{Command, Stdio};
use tempfile::tempdir;
use tokio_process::CommandExt;
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum RenderCitationError {
InitializationFailed,
InvalidEntry,
NodeNotInstalled,
ScriptFaulty,
InvalidOutput,
}
pub async fn render_citation(entry_code: &str) -> Result<MarkupContent, RenderCitationError> {
let tree = BibtexSyntaxTree::from(entry_code);
if tree.entries().iter().any(|entry| entry.fields.len() == 0) {
return Err(RenderCitationError::InvalidEntry);
}
let directory = tempdir().map_err(|_| RenderCitationError::InitializationFailed)?;
let entry_path = directory.path().join("entry.bib");
tokio::fs::write(entry_path, &entry_code)
.compat()
.await
.map_err(|_| RenderCitationError::InitializationFailed)?;
let mut process = Command::new("node")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.current_dir(directory.path())
.spawn_async()
.map_err(|_| RenderCitationError::NodeNotInstalled)?;
tokio::io::write_all(process.stdin().as_mut().unwrap(), SCRIPT)
.compat()
.await
.map_err(|_| RenderCitationError::ScriptFaulty)?;
let output = process
.wait_with_output()
.compat()
.await
.map_err(|_| RenderCitationError::ScriptFaulty)?;
if output.status.success() {
let html =
String::from_utf8(output.stdout).map_err(|_| RenderCitationError::InvalidOutput)?;
let markdown = html2md::parse_html(&html);
Ok(MarkupContent {
kind: MarkupKind::Markdown,
value: markdown.trim().to_owned().into(),
})
} else {
Err(RenderCitationError::InvalidEntry)
}
}
const SCRIPT: &str = include_str!("../script/dist/citeproc.js");
#[cfg(test)]
mod tests {
use super::*;
#[runtime::test(runtime_tokio::Tokio)]
async fn test_valid() {
let markdown =
render_citation("@article{foo, author = {Foo Bar}, title = {Baz Qux}, year = 1337}")
.await;
assert_eq!(markdown.unwrap().value, "Bar, F. (1337). Baz Qux.");
}
#[runtime::test(runtime_tokio::Tokio)]
async fn test_invalid() {
let markdown = render_citation("@article{}").await;
assert_eq!(markdown, Err(RenderCitationError::InvalidEntry));
}
#[runtime::test(runtime_tokio::Tokio)]
async fn test_empty() {
let markdown = render_citation("@article{foo,}").await;
assert_eq!(markdown, Err(RenderCitationError::InvalidEntry));
}
}